Pull to refresh

Comments 53

>> в картинках
Я ожидал подробно расписаного алгоритма с картинкамИ.

Но все равно — спасибо.
Куда уж подробнее? Это ж structural pattern, и алгоритма тут нема :)
Я удивляюсь, почему эта картинка не попала в предыдущие посты…
Если эта статья не только для тех, кто уже знаком с этим паттерном, то я ничего не понял… можно описание проблемы и код который её решает. Из примера понял что можно наследовать то интерфейса можно от абстрактного класса — в чем паттерн?
Еще одна капитанская статья об очевидном паттерне :)
Ну хотя бы не простыня.
Правда пример, имхо, малость неудачен, лично у меня по названиям классов создалось сначала впечатление о том, что TVControl и RemoteControl должны быть расширениями одной абстракции — RemoteControl как пульт д/у, а TVControl как непосредственно управление вынесенное на панель телевизора.
Про названия классов: мне вот еще понравилось упоминание про бетонный пульт (ConcreteRemote).
Concrete mathematics тоже как «бетонную математику» переведе? ;)
Это шутка была. Ну так, на всякий случай.
В «Конкретной математике», между прочим, такой вариант перевода тоже приводится
Не хватает картинки с решением «в лоб», без использования моста. То есть демонстрации какую проблему паттерн решает.

А вообще, фиг знает — раньше я думал, что я этот паттерн понимаю, теперь совсем запутали.
в том посте «в лоб» — это было множественное наследование
Возможно вам поможет табличка из книги 2002 года «Применение шаблонов Java» авторы Стелтинг и Маассен

Внешнее представление: 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
2) При кол-ве классов (Варианты абстракции + Варианты реализации) < 6 паттерн «Мост» не дает никаких преимуществ по сравнению с обычным наследованием. Причем подразумевается, что кол-во вариантов абстракций > 1.
Например в приведенной в посте схеме достаточно всего трех классов (1 абстракция + 2 наследника (или реализации интерфейса). И даже если у вас будет еще 10 вариантов ТV все они будут реализацией одной абстракции.
Для демонстрации «моста» нужно приводить реальный пример с соотношением (3:3, 3:4 и выше).
Собственно задача управления кол-вом классов (сложностью модели) и является мотивом к разделению иерархий.
паттерн «Мост» решает проблему «Запутанных иерархий», но тут этого нет… и я удивляюсь как такое количество специалистов приняли этот пример за чистую монету.
Да нет. Я только за. Я даже за то чтобы создать отбельный блог специально для паттернов. Ибо много информации, но она слабо структурирована
— Профессор — я никак не могу представить себе предмет вашей сегодняшней лекции — сферу в девятимерном пространстве…
— Батенька! Да это же просто как дважды-два — просто представьте себе сферу в n-мерном пространстве и положите n = 9!

P.S. Книга действительно очень удачная и является прекрасным дополнением к Design Patterns by GoF.
еще тоже анекдот вспомнился
— папа, а как пишется восьмерка?
— как бесконечность повернутая на пи пополам…
Пример с одной стороны неплохой и очень правильный с точки зрения организации структуры, но здесь присутствует та же ошибка, на которую я пытался указать в своей недавней статье. Дело в том, что пульт — это привычный пользователю интерфейс взаимодействия, и логично что они разрабатываются ориентируясь на этот интерфейс изначально, поэтому приведенная схема не является правильным примером применения паттерна «мост».

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

Да, я читал вашу статью и вы снова пытаетесь уйти в дебри абстракционизма. К сожалению, не все программируют оконные интерфейсы, а это именно там, где этот конкретный паттерн находит свое наилучшее применение. Поэтому приходится объяснять его метафорами и нельзя это делать, как в анекдоте в комментарии выше. Ну или если очень хочется, рисуйте картинки.
Не путайте объяснения с применением метафоры, и объяснения с применением ошибочного примера. Это все равно что пытаться объяснить что такое собака указывая на кошку. Да, кошка это не собака, но проще ведь привести кошку в пример, просто потому что они схожи, чем описать собаку. Но так вы все равно не дадите человеку понять, что на самом деле представляет из себя собака, и он и дальше будет думать что кошка и собака это одно и то же (Вот это, кстати, был пример объяснения с применением метафоры.)

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

Ок, тогда объясните, чем это неправильно? Я не совсем понял при чем тут «привычный пользователю интерфейс взаимодействия».

Есть generic пульт, у которого определенный набор операций. Я беру, добавляю туда свои операции, которые расширяют этот набор, основываясь на них же. При этом, сам generic пульт остается интерфейсом к телевизору по средством делегирования.

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

Ок, тогда объясните, чем это неправильно? Я не совсем понял при чем тут «привычный пользователю интерфейс взаимодействия».

Есть generic пульт, у которого определенный набор операций. Я беру, добавляю туда свои операции, которые расширяют этот набор, основываясь на них же. При этом, сам generic пульт остается интерфейсом к телевизору по средством делегирования.

Когда человек не знаком с проектированием интерфейсом — очень сложно осознать паттерн и понять почему он не подходит. Поэтому метафорический пример, который приводит к общему знаменателю знания обсуждащих — правильный подход в этом деле.
Упс, не туда ответил
Ошибка в данном случае в том, что вы не обобщаете несколько реализаций, создавая для этого общий интерфейс (т.е. у вас изначально были реализации, а общего интерфейса не было), а, наоборот, — уже существующий интерфейс используете, чтобы создать две реализации в подклассах, т.е. обе эти реализации будут изначально этому интерфейсу соответствовать, в отличии от первого случая, в котором обе реализации созданы независимо друг от друга и общего интерфейса не имеют.
У меня дежавю. Как ни зайду на хабр, так статья про Bridge.
Неделя паттерна Bridge на Хабре =)
Хорошо, если неделя :-)
не совсем понял, класс RemoteControl абстрактный? почему в нем код?
В абстрактном классе может быть код.
почему то мне показалось, что мост это двухуровневый интерфейс.
с чего я сделал вывод, что эта функция setChannel должна так же быть абстрактной.

возможно я не совсем правильно понял исходный материал, но мост должен показать способ первым (верхнего уровня) интерфейсом управлять двумя (и более) несовместимыми между собой интерфейсами второго уровня.
как например телевизором и спутниковым ресивером. и имплементации первого интерфейса это собственно и будут мостами TVRemote — мост к TVControl интерфейсу, а SatRemote- мост к SatControl интерфейсу соответственно.
ну или, как предыдущий автор приводил пример — к интерфейсу OpenGL и интерфейсу DirectX.

на данной же картинке более похоже на фасад/адаптер.

ЗЫ ежели я понял неправильно, то утешает хотя бы то, что я не один такой :)
Советую вам забыть все прочитанное на хабре про паттерн «мост» за последнюю неделю и прочитать:
1) GOF — особенно разделы 'мотивация' и 'известные применения' для данного шаблона
2) затем обязательно Алан Шаллоуей, Джеймс Р. Тротт Шаблоны проектирования. Новый подход к объектно-ориентированному анализу и проектированию. Главу посвященную именно шаблону «мост».

Приведу для затравки лишь одну цитату из второй книги посвященную именно данному патерну
" Хм, ерунда какая-то!.. Я понимаю каждое слово в этом предложении (о формулировке GOF), но не имею ни малейшего представления, что оно может означать." (с)


Чуть выше в этом посте я уже приводил основную суть данного шаблона и критерии применимости здесь лишь добавлю, что удачной метафорой для «моста» является не строительная, а схемотехническая т.н элемент Мультиплексор (или например Общая шина).
Вот! Почитал я «Алан Шаллоуей, Джеймс Р. Тротт Шаблоны проектирования. Новый подход к объектно-ориентированному анализу и проектированию»

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

Т.е. там Shape и Drawing, а не Shape и ShapeImpl. Кроме того, они рассматривали два варианта кто кого использует. Но есть и третий и более правильный. Решать это надо в третьем клиентском классе: напр. (на базе их примера) клиент должен делать вызов следующего вида Drawing.Draw(argCircle), а эти две иерархии ВООБЩЕ не должны друг о друге знать.
Вот, а формулировка в GoF это просто ужас. Алан Шаллоуей, Джеймс Р. хотя бы нормально объясняют, но не дают лучшего решения.

Но при появлении семантики Shape и Drawing становится понятно, что это ни какая не реализация и абстракция, это совершенно разные классы. Это все равно, что художника назвать реализацией фигур. Нет художник рисует фигуры, и его надо об этом попросить с помощью Художник.Нарисуй(Круг), а предварительно нужно найти художника: Художник = new ДядяВася()

Вот ясная и нормальная нотация, решает все те же проблемы и никакого моста.

В теме рядом аналогичный пример был дан раньше.
Вот и еще одно важно отличие в примере Алан Шаллоуей, Джеймс Р. Тротт от моего предложения. Там считается, что D1 и D2 неизменны. Хорошо, но дальше они вводят абстракцию V1Drawing и V2Drawing. Если с кругом там все в порядке, и требуется лишь косметический рефакторинг, то с прямоугольником у них проблемы.

Они завязаны на том, что V1Drawing использует D1, а V2Drawing использует D2. Но там есть только метод DrawLine(). А нужно рисовать прямоугольник. И метод DrawRectangle() оказывается у Rectangle(). Но раз появилась абстракция рисовальщика (художника), то почему же он умеет рисовать только линии, надо взять и его научить рисовать прямоугольники. Т.е. надо элементарно сделать паттерном адаптер метод DrawRectangle() сделать у Drawing. Сразу же исчезнет зависть по Фаулеру, и не будет бедный Прямоугольник использовать Художника для рисования линий, и наконец Художник научится рисовать сам Прямоугольники.

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

И теперь подумайте как сильно искажает эту естественность -паттерн мост. Мост лишь прячит проблемы невнятного моделирования/проектирования предметной области.
Вот и тут вроде специалисты Алан Шаллоуей, Джеймс Р. Тротт дают такие же рекомендации как и в статье про будильники, которую я имел несчастье прочитать и заварить это обсуждение. Как там, так и тут: ну объясните мне чем реализация рисования линии отличается от реализации рисования прямоугольника. Ничем. А почем же тогда одно находится в абстракции, а другое в реализации? Ответ выше — это не верная и порочная декомпозиция. Да, призвана улучшить сложную ситуацию, но сама порождает другую не менее запутанную ситуацию. И вот только не пойму почему этого не видят?
Модель — это дистиллированнное знание (только существенные аспекты действительности).

Техники GOF построены на двух базовых принципах:
найдите точки изменений и инкапсулируйте их.
— композиция объектов предпочтительнее наследования
.

Следствие первое: найдите границы системы, т.к рассуждения о «семантике» и абсолютной истинности модели это бесконечный филосовский спор.

Давайте еще раз взглянем на паттерн «Мост», читаем GOF:
— это шаблон структуры уровня компонента
— это шаблон времени проектирования
— это шаблон заменяющий наследование
— это контекст для применения других шаблонов

Следствие второе: речь идет об одном компоненте, т.е мы разбиваем одни компонент (единую концепцию) на две иерархии.
Следствие третье: и оно очень удачно подмечено у Алан Шаллоуей, Джеймс Р. Тротт. Структура иерархии «Реализации» подчинена потребностям базовой иерархии «Абстракция». Говоря по другому в «Реализации» инкапсулированы возможные точки изменения базовой концепции.
Следование третье: переход к разделению иерархий начинается если здесь и сейчас (в момент проектирования модели) вы столкнулись с лавиноообразным ростом кол-ва классов подлежащих разработке при использовании наследования.
Ибо т.к это один компонент, то возможно любое сочетание Абстракция — Реализация. Вспомним альтернативное название шаблона «Описание — тело».
Математический критерий прост, более 2-х абстракций и более 4-х вариантов реализации.
Мост позволяет заменить композиционный взрыв линейным ростом числа классов подлежащих разработке.
Следствие четвертое: Обеспечение связи любая Абстракция — любая Реализация возлагается на Абстрактную фабрику, Фабричный метод, контейнер зависимостей и т.д. В общем «конфигурирование» откладывается до момента выполнения.
Следствие пятое: GOF не определяет «семантику», а лишь технику реализации, возможность «выстрелить себе в ногу» остается за проектировщиком.

Учитывая все вышеперечисленное считаю главной проблемой данного шаблона не очень верную метафору. Вместо строительной, лучше подходит схемотехника, а именно элемент Мультиплексор. Аналоги Телефонный коммутатор, Общая шина и т.п.
Следствие неудачной метаформы то, что любую горизонтальную стрелочку между двумя прямоугольниками именуют «Мостом», и вы правы в 99% случаев это обычная композиция.
Пример:
Car — Engine — это не мост, а обычная композиция, правильный пример отражающий особенности реализации единого компонента (см следствие второе) Автомобиль [Легковой, Грузовой, Автобус] -> [Бумажный прототип, Компьютерный прототип, Физический прототип, Серийная модель].

Ответ на ваш вопрос "… ну объясните мне чем реализация рисования линии отличается от реализации рисования прямоугольника. Ничем. А почем же тогда одно находится в абстракции, а другое в реализации?".
Авторам не нужна реализация прямоугольника, это всего провести 4 линии, но рисование даже такого примитива как линия оказывается завязано на платформенные особенности, которые они успешно инкапсулировали.
И на этом стоп, вот это граница системы.
В дальнейшем при возникновении потребностей, например в Эллипсе, потребуется пересмотреть модель возможно действительно выделить универсальную графическую библиотеку, но это будет совсем другая история, другая модель в которой исходный компонент рассыпался на действительно независимые сущности, а это уже не Мост, см. следствие второе…
Спор о «семантике», абсолютной верности модели не имеет к шаблону никакого отношения.
«И на этом стоп, вот это граница системы.»

В том то и дело, они сделали шаг, и не сделали второй. А если бы сделали, то оказалось бы что первый шаг сделан не в том направлении. В том то и дело при «возникновении потребностей» они мучаются и развивают систему в сторону увеличения запутанности. Т.е. они как раз не могут с помощью моста найти точки изменений. Они устранили одну проблему, но породили другую. Заменили наследование на агрегацию — молодцы, а разорвать вообще слабо? А ведь это и проще и лучше для развития. Есть другие примеры, где сложно полностью разорвать, но тогда есть другие решения.
Да, и речь не идет о абсолютной верности модели, речь идет хотя бы о каком-то приближении к верности модели. А они совершенно это подчиняют не тем принципами. Это как применение 2-3 локальных шагов, но не видя в целом весь путь, итерационные шаги будут не в том направлении, надо сразу учитывать большие число факторов — и шагать глобальнее. Можно и локально, но тогда правильность построения модели должна быть главенствующим фактором над принципом парадигмы структурного программирования: найдите точки изменения. И нахождение модели более точно соответствующий предметной области — это принцип ООП, и он не должен быть подчинен структурному найдите точки изменения — это вторично. И только тогда эти точки могут быть найден, а на уровне кода — они просто не видны, «лес мешает»
И это не философия, это увы практика :)
«в которой исходный компонент рассыпался на действительно независимые сущности, а это уже не Мост»

А нет самоцели использовать Мост, именно поэтому вместо Моста можно и нужно использовать другое, что лучше решает ту же проблему и ряд других в одном флаконе.
Проблема в том, что вы говорите о тех принципах которые лежат в основе Domain-driven design. Интуитивно или осознанно, не знаю. Тем не менее мне бы хотелось подчеркнуть к исходному паттерну «Мост» из GOF это не имеет никакого отношения. Этот паттерн не архитектурный и тем более не наделен высшим смыслом с точки зрения моделирования предметной области.

Удачи в поисках.
Интуитивно :) Пошел учить еще одно умное слово :)
Дам ссылку за затравочное описание Domain-driven design. Всю жизнь проектировал сверху-вниз, а тут вот оказалось, что это называется умным словом. Похоже надо будет написать отдельную статью про это, может хотя бы там есть здравые идеи как правильно использовать паттерны.
перечитал. два интерфейса x-win и Presentation Manager.
первый уровень бриджа — подведение их к общему знаменателю.
собственно да, изначально это считал за бридж, а это лишь первый его уровень. полезно знания обновлять, а то без применения их на практике подзабыл.

это вобщем то все то же разбиение на уровни или слои, частный случай.
в классах выделяются повторяющиеся действия, и выносятся в отдельную сущность уровнем ниже. в бридже же две похожие сущности, с идентичными целями, но работающие с разным материалом одариваются единым интерфейсом. а на верхнем уровне оказывается, что(в примере GoF) XIconWindow и PMIconWindow не просто повторяются, а идентичны. Из них ушли разные действия, остались одинаковые, и они «схлопываются» в один класс IconWindow.
такое вот разбитие на уровни позволяет количество сущностей из количества например 3х5 сделать 3+5.
ЗЫ про мультиплексор не совсем понял.

пример кстати придумался такого разбиения. Из классики. вот есть допустим протоколы верхнего уровня SIP и HTTP и протоколы нижнего TCP и UDP.
и в своем классе мы такой вот темплейтный метод делаем, чтоб отправить сообщение:
SIPTCPClass: { SIPMessageConstruct(); TCPSend(); }
SIPUDPClass: { SIPMessageConstruct(); UDPSend(); }
HTTPTCPClass: { HTTPMessageConstruct(); TCPSend(); }
HTTPUDPClass: { HTTPMessageConstruct(); UDPSend(); }
собственно разбиение на уровни очевидно.
конечно в случае 4ех классов преимущества не видны, ибо 2х2 = 2+2, но когда надо будет добавлять протоколы, как верхнего уровня, так и нижнего(RUDP например, как задекорированный UDP), то выгода от разбиения этого на уровни становится понятна.
тем более, что уровней то поболее. в итоге сложение нескольких(по числу уровней) чисел завсегда даст меньшее количество сущностей, нежели перемножение.
а в примере в данной статье после разбиения на одном из уровней осталась одна сущность. 2+1 это больше, чем 2х1, следовательно разбиение не имело смысла. что, я так полагаю, пытается донести tac
М-да, только как и пытались тут говорить — это пример не использования «Моста». Пример семантически не разделяет ни какую абстракцию и реализацию. Тут происходит адаптирование управления ряда TVControl`ов, совершенно другим устройством RemoteControl. Поэтому это паттерн «Адаптер».

И тогда уж напомню вам слова из GoF

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


И вы с успехом это перепутали.
Вы бы лучше картинку нарисовали и текста сократили. Могу пошарить гугле док с рисунком — вы поправите.
Основная идея: «Не используем мост, а вначале разбираемся в ситуации когда его использовать. Только когда поняли, думаем дальше что же такое семантически есть т.н. реализация. Если подумаете, то окажется это некий интересная сущность из реального мира, которая имеет право на существование. Если после этого появились заблуждения другого рода читаем дальше
Sign up to leave a comment.

Articles