Также известен как Bridge

Мост

Суть паттерна

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

Проблема

В этом паттерне многих путает терминология.

«Абстракцию» в Мосте часто ещё называют «интерфейсом», а «реализацию» — «платформой». Что же это значит?

«Абстракция» или «интерфейс» означает образный слой управления чем-либо, в котором нет конкретных деталей реализации. Не путайте её с интерфейсами или абстрактными классами из вашего языка программирования.

Вы разрабатываете приложения для бронирования авиа-билетов. Приложение работает с несколькими авиа-компаниями, которые имеют отличающиеся API. Из-за этого каждая фича приложения содержит условный код, работающий по-разному для каждой авиа-компании.

Добавляя новые элементы GUI, вы пересекаетесь с вызовами API авиа-компаний. Добавляя новую авиа-компанию в приложение, вам нужно менять код всех классов GUI. Если какая-то фича не работает с определённой авиа-компанией, вам приходится вставлять костыли прямо в код GUI.

Всё это приводит к раздутию кода, замедлению работы и частым случайным ошибкам.

Решение

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

- Абстракцию: слой GUI приложения. - Реализацию: слой работы с авиа-компаниями с единым интерфейсом.

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

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

Структура

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

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

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

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

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

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

Псевдокод

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

  • Приборы (реализация)
  • Пульты управления ними (абстракция)

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

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

// Общий интерфейс для всех пультов управления.
interface Remote is
    method power()
    method volumeDown()
    method volumeUp()
    method channelDown()
    method channel()

// Каждая реализация пульта имеет ссылку на устройство, которым управляет.
// Методы пульта вызывают методы устройства.
class BasicRemote implements Remote is
    field device: Device
    constructor BasicRemote(device: Device) is
        this.device = device
    method power() 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 BasicRemote 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.pover()

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

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

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

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

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

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

Например, класс Геометрических Фигур требуется расширить до Квадрата и Трехугольника, а также до Красных и Синих. При этом для получения СинихКвадратов придётся создавать комбинации подклассов.

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

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

Мост позволяет заменять реализацию даже во время выполнения программы.

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

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

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

  2. Создайте интерфейс Реализации. Он должен содержать все платформо-зависимые операции, которые нужны управляющему слою.

  3. Для всех платформ создайте классы конкретных реализаций, с общим интерфейсом Реализации.

  4. Создайте базовый класс Абстракции. Он должен содержать ссылку на объект из иерархии Реализации. Поместите в класс абстракции все операции, которые имеют смысл для Клиента. Операции должны делегировать конкретную работу объекту конкретной реализации.

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

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

  • Платформо-независимость.
  • Реализует принцип открытости/закрытости.
  • Скрывает лишние или опасные детали реализации от клиента.
  • Усложняет код программы за счёт дополнительных классов.

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

  • Мост проектируют загодя, чтобы развивать большие части приложения отдельно друг от друга. Адаптер применяется постфактум, чтобы заставить несовместимые классы работать вместе. Адаптер заставляет вещи работать уже после того как они были спроектированы. Мост делает их рабочими до этого.

  • Мост, Стратегия и Состояние (а также слегка и Адаптер) имеют схожие структуры классов — все они построены на принципе «композиции», то есть делегирования работы другим объектам. Тем не менее они отличаются тем, что решают разные проблемы. Помните, что паттерны — это не только рецепт построения кода определённым образом, но и описание проблем, которые привели к данному решению.

  • Абстрактная фабрика может работать совместно с Мостом. Это особенно полезно, если у вас есть абстракции, которые могут работать только с некоторыми из реализаций. В этом случае фабрика будет определять типы создаваемых абстракций и реализаций.

  • Паттерн Строитель может быть построен в виде Моста: Директор будет играть роль абстракции, а Строители — реализации.

Реализация в различных языках программирования

Java