Проектирование и рефакторинг / Шаблон Bridge дополнительные штрихи

в 20:33, , рубрики: bridge, паттерны проектирования, проектирование, проектирование по, шаблоны проектирования, метки: , , , ,

Обсуждение многострадального шаблона Bridge на хабре, выявило много интересных мнений и заблуждений. Попробуем разобраться, реанимировать данный шаблон в глазах тех кто борется с формулировками оригинального каталога GoF, а интересующимся темой шаблонов показать несколько дополнительных штрихов.

Кратко о шаблонах GoF (Gang of Four – «банда четырех»)

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

«Когда мы писали нашу книгу, мы действительно пытались кое-что скрыть, Нам хотелось избежать разговоров о том, что один шаблон является специализацией другого или один шаблон содержится в другом в качестве его компонента. Мы не хотели отвлекаться, и решили говорить только о шаблонах. Отчасти поэтому мы не описали в качестве самостоятельного шаблона «абстрактный класс», поскольку он содержится в большинстве шаблонов. Я думаю, это было оправданное решение при создании первого каталога шаблонов, но сейчас ситуация изменилась. Люди хотят знать о связях между шаблонами, и мы должны рассказать им об этом.»
Ральф Джонсон

«Разработанные GoF шаблоны проектирования учитывают только аспекты микроархитектуры. Необходимо правильно выбрать макроархитектуру: разбиение на уровни, распределение, изоляцию функций..., а также наноархитектуру: инкапсуляцию, принцип подстановки Лисков (Barbara Liskov). Возможно, кое-где удастся использовать некий шаблон проектирования, во всяком случае, весьма маловероятно, что такой шаблон уже разработан и описан в какой-нибудь книге.»
Ричард Хелм

Шаблоны GoF построены на двух базовых принципах

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

Критерии для включения шаблона в каталог

  • Воспроизводимость приема как минимум в 3-х независимых реализациях. По возможности независимо от языка программирования.
  • Согласие группы с именем (метафорой) и базовой структурой шаблона.

Шаблон Bridge («Мост»)

Тип: структурный
Уровень: компонент
Другое название: Handle/Body («Описание/тело»)
Время применения: момент проектирования.

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

Демонстрация

Рассмотрим идеи лежащие в основе данного шаблона на примере моделирования компонента под названием «Автомобиль». Практическая ценность не будет самоцелью.
Шаг 1. Иерархия функциональных возможностей (первый уровень), в данном случае это будут укрупненные виды автомобилей.
Проектирование и рефакторинг / Шаблон Bridge дополнительные штрихи

Шаг 2. Пространство реализаций будет отражать варианты представления автомобиля в недрах компании производителя. Обратите внимание на взрывное увеличение статических связей в модели и соответственно кол-во классов подлежащих разработке при использовании наследования. Но в момент выполнения сложность модели линейная.
Проектирование и рефакторинг / Шаблон Bridge дополнительные штрихи

Проектирование и рефакторинг / Шаблон Bridge дополнительные штрихи

Шаг 3. Разделение иерархий, откладывание связывания до момента выполнения. Сравните со схемотехническим элементом «многопозиционный переключатель» или «мультиплексор».
Проектирование и рефакторинг / Шаблон Bridge дополнительные штрихи

Шаг 4. «Классическое» представление шаблона с учетом указанных особенностей.
Проектирование и рефакторинг / Шаблон Bridge дополнительные штрихи

Сравнение с наследованием

Варианты функциональности:__2…..3....3…4…...4….5
Варианты реализации:________2…..2….3….3…...4….4
Классы (наследование):______ 4…..6….9…12...16...20
Классы (шаблон Bridge):______ 4....5….6…7…...8…..9

Архитектура шаблона Bridge позволяет выполнять мультиплексирование разных вариантов внешнего представления и внутренней реализации компонента. Термин «мультиплексирование» означает возможность ассоциации в любой комбинации внешних и внутренних элементов, что обеспечивает увеличение диапазона возможных вариаций компонента.
Разделение компонента на две отдельные концепции, кроме того, способствует облегчению понимания назначения компонента и его сопровождения. Это объясняется тем, что каждая ветвь наследования выстраивается на основании одной концепции — либо абстракции, либо реализации.

Важные моменты

Границы: Функции и реализация это тесто связанные иерархии, находящиеся на одном концептуальном уровне, отражающие особенность структуры одного компонента. Обратите внимание, что реализация находится в «private/protected» части компонента и отражает точки вариации функциональной концепции.
Пример: GUI в Java. Public находится в java.awt.*. Private во внутренних пакетах sun.*. Это обеспечивает защиту важного инварианта т.к нет никакого смысла использовать например на Win реализацию под Mac, и тем более позволять внешнему (недоверенному) коду подменять реализацию.

Главная иерархия: Функциональная иерархия является главной, она знает о вариации и использует ее (однонаправленная ассоциация). Иерархия реализации подчинена потребностям требуемых функций. Именно поэтому в GoF получается, что метод DrawRectangle() находится на функциональном уровне, а DrawLine() на уровне платформенных реализаций.
При двунаправленной ассоциации у нас получается уже не «мост», а «посредник» по GoF.

Место среди структурных шаблонов: Классическая формулировка говорит о равнозначимости изменения обоих иерархий. Однако один из «бандитов» Джон Влиссидес приводится следующий список точек вариации.
ADAPTER — интерфейс объекта;
BRIDGE — реализацию объекта;
COMPOSIТЕ — структуру и состав объекта;
DECORATOR — обязанности объекта без порождения подкласса;
FACADE — интерфейс подсистемы;
FLYWEIGHT — накладные расходы на хранение объектов;
PROXY — способ доступа к объекту и/или его местоположение.

Связь с другими шаблонами: Если вы пытаетесь перейти к «мосту» в существующей модели, то в 99% случаев вы либо агрегируете/адаптируете независимые сущности либо используете посредника/стратегию для инкапсуляции изменений, просто не забывайте что исходный шаблон это прием времени проектирования, такой начальный шаг, но как только ваша «реализация» выйдет из «private» зоны и заживет самостоятельно это уже не «мост». Связь между уровнями, инверсия зависимостей что угодно только не «мост» в смысле GoF.
«Мост» нужно «конфигурировать» в момент выполнения, не забывайте возможно любое сочетание Абстракция — Реализация. Логика «конфигурирования» и соблюдения инвариантов либо спрятана в компоненте либо отдается компоненту более высокого уровня (в сложных случаях может быть Абстрактная фабрика, контейнер зависимостей и т.п).
Такие варианты, как разделение реализации между объектами в момент выполнения, использование нескольких реализаций одновременно. Описаны у GoF и их можно считать экзотическими случаями в которых «мост» контекст для применения другого шаблона.

Определите для вашего компонента: что означает концепция равенства сущностей применительно к шаблону Bridge. Нужно ли сравнивать только абстракции либо только реализации объектов, или же нужно рассматривать их в совокупности?

Проблема качества модели: состоит в том, что нередко разработка реализации шаблона выполняется на основе одной или двух возможных вариаций. Опасность заключается в том, что при последующем развитии шаблона выясняется, что некоторые из элементов, считавшихся базовыми, на самом деле представляют собой конкретные вариации, базирующиеся на абстракции и (или) реализации. Данный момент несколько сумбурно был изложен в статье пользователя tac ссылка. Но здесь важно помнить, что шаблоны не определяют качество модели предметной области, глубина и адекватность модели существенно зависит от первичных задач приложения, квалификации проектировщика и мнения экспертов в соответствующей области.
Эмпирический (механический) критерий — если здесь и сейчас (в момент проектирования) вам действительно нужно (Варианты функциональности + Варианты реализации) > 6 т.е соотношение уровня 3:3, 3:4 и выше, то
— разбивайте иерархии
— какое соотношение объема кода получилось 80%:20%, 50%:50%, 20%-80%?
— подумайте не распалась ли ваша исходная концепция на независимые сущности?

Проблема исходной метафоры: строительная метафора + невнятная формулировка в каталоге приводят к тому, что любую горизонтальную стрелочку между двумя независимыми иерархиями объявляют «мостом». Метафора «мультиплексор» лучше отражает базовую технику реализации, а ограничение уровнем одного компонента проводит понятную логическую границу с другими шаблонами.
Надеюсь изложенные «дополнительные штрихи» позволили четче показать суть шаблона.

Список литературы

Э. Гамма, Р. Хелм, Р. Джонсон, Д. Влиссидес — Приемы объектно-ориентированного проектирования. Паттерны проектирования. Каталог шаблонов GoF.
Дж. Влиссидес — Применение шаблонов проектирования. Дополнительные штрихи. Здесь описаны шаблоны, не вошедшие в основной каталог Pull/Push, Multicast…и просто интересные зарисовки творческих мук «банды».
А. Шаллоуей, Дж. Тротт — Шаблоны проектирования. Новый подход к объектно-ориентированному анализу и проектированию. Рассуждения о невнятности формулировки «моста» и поиск контекста применения. Также хорошие рассуждения о сочетании/применении шаблонов.
С.Стелтинг, О.Маассен — Применение шаблонов JAVA. Четкое позиционирование «моста» как шаблона уровня компонента.

Автор: mishin_pavel


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js