Мост (Bridge)

Метафора

 

Представим ситуацию, когда вам требуется работать на разных автомобилях, однако садясь в новый автомобиль вам уже желательно знать, как им управлять. Таким образом, Вы сталкиваетесь с паттерном «мост». С одной стороны, вы имеете множество различных автомобилей (разные модели и марки), но среди них есть общая абстракция (интерфейс) в виде руля, педалей, коробки передач и так далее. Таким образом, мы задаем как-бы правила изготовления автомобилей, по которым мы можем создавать любые их виды, но за счет сохранения общих правил взаимодействия с ними, мы можем одинаково управлять каждым из них. «Мостом» в данном случае является пара двух «объектов»: конкретного автомобиля и правил взаимодействия с этим (и любым другим) автомобилем.

 

Назначение

 

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

Представим некоторую классическую схему проектирования классов – есть базовый абстрактный класс, определяющий интерфейс и его наследники, реализующие данный интерфейс. У данной схемы есть определенные недостатки:

  • Реализация жестко привязана к интерфейсу, изменить реализацию объекта во время выполнения уже нельзя.
  • Если число наследников велико, систему становится сложно поддерживать.

Используя паттерн мост, мы избавимся от этих недостатков.

 

Диаграмма

 

Мост

Мост

 

Пример

 

Предположим у нас с Вами есть некоторые классы для формирования PDF отчётов: OneReport, TwoReport:

sample1

 

Каждый из них наследуется от базового класса Report и переопределяет методы get_data и write_data, при этом write_data формирует PDF формуляр, а get_data содержит бизнес-логику получения данных для вывода.

В один прекрасный момент к нам приходит клиент и говорит: «Хочу вывод в DOC и в XML». Самое первое что приходит в голову это наследование:

sample2

 

Вывод PDF мы уберём из отчётов. Создадим по наследнику от каждого отчёта и переопределим метод вывода отчёта для конкретного формата. Как понятно из схемы, у нас появилась другая проблема — сложность поддержки большого числа классов.

Решить её мы можем, несколько изменив наше решение, применив паттерн мост:

bridge_sample

Реализацию, отвечающую за вывод данных, мы вынесли в отдельную иерархию классов — Printer. В клиенте при определении объекта OneReport или TwoReport мы будет внедрять ему реализацию конкретного принтера, который умеет печатать в нужном клиенту формате.

Пример на ABAP:

 

  • При реализации классов отчетов нет смысла переопределять метод write_data(), т.к. функционал у него один и тот же. Не проще ли его оставить на уровне родительского отчета? Или это для наглядности сделано?