Книга: Погружение в Паттерны Проектирования
Назад: Адаптер
Дальше: Компоновщик
Gang of Four / «Банда четырёх». Авторы книги Design Patterns: Elements of Reusable Object-Oriented Software . при описании Моста. На мой взгляд, они выглядят слишком академичными, делая описание паттерна сложнее, чем он есть на самом деле. Помня о примере с фигурами и цветами, давайте все же разберёмся, что имели в виду авторы паттерна.

Итак, «Абстракция» (или «интерфейс») — это образный слой управления чем-либо. Он не делает работу самостоятельно, а делегирует её слою «реализации» (иногда называемому «платформой»).

Только не путайте эти термины с интерфейсами или абстрактными классами из вашего языка программирования, это не одно и то же.

Если говорить о реальных программах, то абстракцией может выступать графический интерфейс программы, а реализацией — API, к которому интерфейс обращается по реакции на действия пользователя.

Вы можете развивать программу в двух разных направлениях:

Такая программа может выглядеть как один большой клубок кода, в котором намешаны условные операторы слоёв интерфейса и API операционных систем.

Защита от изменений

Когда изменения осаждают проект, легче отбиваться, если разделить монолитный код на части.

Вы можете попытаться структурировать этот хаос, создав для каждой вариаций интерфейса-платформы свои подклассы. Но такой подход приведёт к росту классов комбинаций, и с каждой новой платформой их будет всё больше.

Мы можем решить эту проблему, применив Мост. Паттерн предлагает распутать этот код, разделив его на две части:

Вариант кросс-платформенной архитектуры

Один из вариантов кросс-платформенной архитектуры.

Абстракция будет делегировать работу одному из объектов-Реализаций. Реализации можно будет взаимозаменять, если все они будут иметь общий интерфейс.

Вы сможете изменять интерфейс приложения, не трогая низкоуровневый код работы с операционной системой. И наоборот, сможете добавить поддержку новой операционной системы, создав подкласс реализации, но не трогая классы абстракции.

Структура

Структура классов паттерна Мост
  1. Абстракция содержит управляющую логику. Код абстракции делегирует реальную работу связанному объекту реализации.

  2. Реализация задаёт общий интерфейс для всех реализаций. Все методы, которые здесь описаны, будут доступны из класса абстракции и его подклассов.

    Интерфейсы абстракции и реализации могут как совпадать, так или быть совершенно разными. Но обычно, в реализации живут базовые операции, на которых строятся сложные операции абстракции.

  3. Конкретные Реализации содержат платформо-зависимый код.

  4. Расширенные Абстракции содержат различные вариации управляющей логики. Как и родитель, работает с реализациями только через общий интерфейс реализации.

  5. Клиент работает только с объектами абстракции. Не считая первичного связывания абстракции с одной из реализаций, клиентский код не имеет прямого доступа к объектам реализации.

Псевдокод

В этом примере Мост разделяет монолитный код приборов и пультов на две части: приборы (выступают реализацией) и пульты управления ними (выступают абстракцией).

Структура классов примера паттерна Мост

Пример разделения двух иерархий классов — приборов и пультов управления.

Класс пульта имеет ссылку на объект прибора, которым он управляет. Пульты работают с приборами через общий интерфейс. Это даёт возможность связать пульты с различными приборами.

Сами пульты можно развивать независимо от приборов. Для этого достаточно создать новый подкласс абстракции. Вы можете создать простой как простой пульт с двумя кнопками, так и более сложный пульт с тач-интерфейсом.

Клиентскому коду остаётся выбрать версию абстракции и реализации, с которым он хочет работать.

// Класс пультов имеет ссылку на устройство, которым
// управляет. Методы этого класса делегируют работу методам
// связанного устройства.
class Remote is
  protected field device: Device
  constructor Remote(device: Device) is
    this.device = device
  method togglePower() is
    if (device.isEnabled()) then
      device.disable()
    else
      device.enable()
  method volumeDown() is
    device.setVolume(device.getVolume() - 10)
  method volumeUp() is
    device.setVolume(device.getVolume() + 10)
  method channelDown() is
    device.setChannel(device.getChannel() - 1)
  method channelUp() is
    device.setChannel(device.getChannel() + 1)


// Вы можете расширять класс пультов не трогая
// код устройств.
class AdvancedRemote extends Remote is
  method mute() is
    device.setVolume(0)


// Все устройства имеют общий интерфейс. Поэтому с ними
// может работать любой пульт.
interface Device is
  method isEnabled()
  method enable()
  method disable()
  method getVolume()
  method setVolume(percent)
  method getChannel()
  method setChannel(channel)


// Но каждое устройство имеет особую реализацию.
class Tv implements Device is
  // ...

class Radio implements Device is
  // ...


// Где-то в клиентском коде.
tv = new Tv()
remote = new Remote(tv)
remote.power()

radio = new Radio()
remote = new AdvancedRemote(radio)

Применимость

Когда вы хотите разделить монолитный класс, который содержит несколько различных реализаций какой-то функциональности (например, может работать с разными системами баз данных).

Чем больше класс, тем тяжелее разобраться в его коде, тем больше это затягивает разработку. Кроме того, изменения, вносимые в одну из реализаций, приводят к редактированию всего класса, что может привести к ошибкам.

Мост позволяет разделить монолитный класс на несколько отдельных иерархий. После этого вы можете менять их код независимо друг от друга. Это упрощает работу над кодом и уменьшает вероятность внесения ошибок.

Когда класс нужно расширять в двух независимых плоскостях.

Мост предлагает выделить одну из таких плоскостей в отдельную иерархию классов, храня ссылку на один из её объектов в первоначальном классе.

Когда вы хотите, чтобы реализацию можно было бы изменять во время выполнения программы.

Мост позволяет заменять реализацию даже во время выполнения программы, так как конкретная реализация не «вшита» в класс абстракции.

Кстати, из-за этого пункта Мост часто путают со . Обратите внимания, что у Моста этот пункт стоит на последнем месте по значимости, так как его главная задача — структурная.

Шаги реализации

  1. Определите, существует ли в ваших классах два непересекающихся измерения. Это может быть функциональность/платформа, предметная-область/инфраструктура, фронт-энд/бэк-энд или интерфейс/реализация.

  2. Продумайте, какие операции будут нужны клиентам и опишите их в базовом классе абстракции.

  3. Определите поведения доступные на всех платформах и выделите из них ту часть, которая будет нужная абстракции. На основании этого опишите общий интерфейс реализации.

  4. Для каждой платформы создайте свой класс конкретной реализации. Все они должны следовать общему интерфейсу, который мы выделили перед этим.

  5. Добавьте в класс абстракции ссылку на объект реализации. Реализуйте методы абстракции, делегируя основную работу связанному объекту реализации.

  6. Если у вас есть несколько вариаций абстракции, создайте для каждой из них свой подкласс.

  7. Клиент должен подать объект реализации в конструктор абстракции, чтобы связать их воедино. После этого он может свободно использовать объект абстракции, забыв о реализации.

Преимущества и недостатки

Отношения с другими паттернами

Назад: Адаптер
Дальше: Компоновщик

asd
asdda