Предыстория Я читаю Эта статья о шаблоне проектирования Bridge. К сожалению, очень часто его используют неправильно.
Более того, я тогда открыл книгу Методы объектно-ориентированного проектирования.
Шаблоны проектирования .
Оказалось - и там авторы очень расплывчато заявляют причины его наличия и когда его использовать.
Поэтому ниже я расскажу вам, как и зачем это использовать.
Обновлять Это поверхностная статья, которую нельзя трактовать совсем точно.
Но его преимущество в том, что оно короткое и создает проблемы.
У специалистов это может вызвать вопросы более глубокого содержания, а у молодых разработчиков — некоторые недопонимания, потому что… Я по сути спорю с «Бандой четырёх», но полностью согласен с Фаулером и его подходом к рефакторингу (а у них тоже есть противоречия среди сами) - но типа, кто я такой, чтобы спорить.
Готовлю расширенную статью для специалистов, но она может быть полезна и молодым разработчикам, механически усвоившим паттерны проектирования.
Они не очень хорошо понимают, когда их использовать, но думаю специалистам будет важно поспорить в смысле, каким паттернам отдать предпочтение.
Надеемся, что эта расширенная статья объяснит, почему необходимо избавиться от паттерна «Мост», а также использовать паттерн «Посредник» в ограниченном смысле.
Уже есть ветка этой статьи Правильное использование шаблона моста (двусторонний мост) или MVC-> Business Entity — Visualization — Controller. .
Где показано, что Bridge/Mediator можно использовать в той или иной комбинации при разделении визуализации и бизнес-логики, но это практически единственная область для этих паттернов.
В чистой бизнес-логике и низкоуровневых/системных задачах следует избегать этих шаблонов.
Но опубликовать не могу - кармы у меня нет, и других вариантов, как я понимаю, пока не напишу.
Так что если вы хотите это прочитать, вы знаете, что делать :) Что на самом деле представляет собой шаблон проектирования Bridge? Если вы знаете объектно-ориентированное программирование, то со всей ответственностью заявляю, что знать о шаблонах совершенно не обязательно.
Паттерны — это лишь частное и не всегда самое удачное решение, основанное на принципах ООП.
Давайте разберемся, что такое паттерн «Мост» и что кроется за этим заумным термином.
Это не что иное, как комбинация использования наследования и агрегации.
К сожалению, они часто не знают, что такое агрегация.
Проще говоря, это когда один объект включается в другой.
Настоящие причины использования шаблона моста Авторы книги декларируют следующую цель: «отделить абстракцию от ее реализации».
Но это совершенно неправильно.
В принципе это даже вредно.
Это противоречит ООП.
Реализация должна полностью соответствовать тому, что заявлено в абстракции.
В противном случае отделение декларации от реализации бесполезно.
Если мы посмотрим на объявление класса, а потом окажется, что реализация класса ему не соответствует, то мы потеряли самое главное — мы потеряли полиморфизм .
Затем нам приходится знакомиться с реализацией, и нам недостаточно знать только абстракцию/спецификацию.
Но дело оказывается несколько в другом.
И это становится ясно из примеров.
Показаны примеры использования наследования.
Но наследование происходит не просто, а по двум разным критериям.
Например, «Windows» можно классифицировать как для разных операционных систем: XWindow, PMWindow,.
, так и разные по назначению: окна для значков (IconWindow), окна для сообщений (MessageWindow).
Тогда итоговое окно должно содержать функции из одной иерархии и функции из другой.
Те.
быть специализированным по назначению и операционной системе.
Тогда, конечно, аппарат наследования становится проблематичным, получается, что необходимо создавать классы как произведение этих иерархий, т.е.
XIconWindow, PMIconWindow, XMessageWindow, PMMessageWindow. Конечно, это уже нарушает другой принцип ООП — создание ненужных сущностей, представляющих собой лишь комбинацию более простых.
Обратите внимание, что это классический пример использования множественного наследования.
Те.
XIconWindow является потомком IconWindow и XWindow. Но последние тенденции определили множественное наследование как неприемлемое для ООП.
Мы не будем здесь это обсуждать, но действительно, теоретически множественное наследование ненужно, и тут скорее непонимание предметной области.
Итак, авторы схемы предлагают другой путь.
Увидели, в частности, что за физическую реализацию отвечает одна из иерархий, т.е.
для какой операционной системы это делается? И, конечно же, это должно быть скрыто.
Но это лишь особенность, и в действительности могут быть и другие иерархии.
Например, окна также могут различаться по принципу «одного окна» (SDI) или «многоокна» (MDI).
И что, очередное умножение иерархий? И, конечно же, нет. Здесь помогает понимание того, что эти иерархии объектов являются их свойствами.
Как следует реализовать свойства? Правильно, включая их в определенный объект. Те.
применить агрегацию.
Это то, что составляет узор «Мост».
Но, по сути, этот паттерн представляет собой корявое объяснение более общих принципов ООП.
Как же тогда мы можем рассуждать более правильно? Очень просто.
Есть отношения «общее-частное» — используем наследование, есть отношения «часть-целое» — используем агрегацию.
Но иногда это не так очевидно, и нам кажется, что связь «обще-видовая» существует, но по двум-трем разным критериям (как описано выше).
Но мы помним, что подобные рассуждения позволят нам только запутаться, и соответственно ухудшить архитектуру программы.
Затем мы должны решить, какие критерии более важны, а какие менее важны.
Расположите их в иерархии, вложите одно в другое.
Авторы паттерна «Мост» считают, что реализация функции операционной системой менее важна, чем назначение окна.
Но это лишь частный случай.
Могут быть и другие критерии, и каждый раз вам придется решать, что такое часть, а что целое.
Причем часто это придется решать, когда деталь уже реализована, и появилось дополнительное производство.
Тогда вам придется провести рефакторинг и превратить наследование в агрегацию.
Поэтому нужно помнить, что наследование создает более жесткую связь между классами.
И всякий раз, когда есть возможность, лучше отдать предпочтение агрегации.
Это позволит вам более четко сформулировать спецификацию класса и отношения между различными классами.
Наследование нужно использовать очень осторожно.
В противном случае вам потом придется конвертировать его в агрегацию — а это зачастую не так просто.
Наследование следует использовать только тогда, когда вы не можете четко определить критерий наследственной классификации, т. е.
когда вы знаете, что этот класс является подтипом более абстрактного класса, причем не по какому-то критерию, а вообще.
Но никогда не отделяйте реализацию «будильника» от абстракции «будильника».
Это лишь показывает непонимание ООП.
Так вот в статье, послужившей поводом для написания этой заметки, в качестве «моста» была вынесена сущность, заявленная как реализация будильника.
Интересно, а что остается в т.н.
Абстракции будильника не являются реализацией? По сути, там есть две функции – совершить звонок и отправить сообщение.
Причем сделать это можно разными способами.
Очевидно, что сочетание этих функций является искусственным, т.к.
возможны различные их комбинации.
Поэтому наша абстракция будильника состоит/использует не псевдореализацию, а две сущности — абстракцию менеджера сообщений и менеджера проигрывателя мелодий.
А конкретные классы с помощью конструкторов указывают, какой тип сообщений и как воспроизводить мелодии.
Вывод один: шаблоны проектирования, особенно плохо продуманные, такие как обсуждаемый «Мост», и особенно те, кто не понимает более важных принципов, только мешают и сбивают с толку программистов и их программы.
обновление.
Дополнительные пояснения.
Статья рассчитана на то, чтобы читатель был знаком с тем, что такое «Мост».
Пожалуйста, не путайте паттерн «Мост» с: 1. Простая агрегация - когда двигатели объединяются в Машину (независимо от того, сколько типов машин и двигателей) 2. Использование интерфейсов аналогично первому, с той лишь разницей, что вместо абстрактного класса используется интерфейс, например IEngine, IMachine все это не соответствует необходимости паттерна «Мост» (слишком мелкий, т.е.
паттерн «Мост» используется по замыслу авторов в более сложных случаях).
Еще раз заявлено, что при использовании «Моста» выделяется ВСЯ реализация, т.е.
ВСЕ низкоуровневые функции в другой класс, т.е.
не функционал Движка от Машины, а низкоуровневый функционал (приватный) сама машина в MachineImplementation. Если под «Мостом» вы имеете в виду что-то другое, то эта статья не для вас.
Все ли вы делаете правильно, или вы просто еще не сталкивались с ситуацией, когда вам нужен «Мост» — и это очень хорошо, потому что.
он особо не нужен.
Еще один «Мост» можно реализовать с помощью множественного наследования, поэтому проверьте себя и свои примеры.
Например, первые два типа — «простая агрегация» и «использование интерфейсов» — явно показывают, что множественное наследование вставлять некуда — а значит, «Мост» здесь не будет иметь никакого применения.
Или еще проще — вам бы вообще пришло в голову унаследовать Двигатель от Машины? Или скажем Отправитель писем от писем? Или абстракция базы данных или класс обработки файлов из Windows? Но в примерах использования паттерна «Мост» это исходное положение, которое нужно исправить с помощью «Мост».
Но задумайтесь: возможны ли такие виды наследования? Как вы думаете, почему вы использовали «Мост», когда даже исходная ситуация у вас была другая и о чем-то другом? История с «мостом» начинается тогда, когда есть наследство, причем не в одном разделе.
Те.
Недостаточно того, что Car и Truck унаследованы от Car. Также системе необходимо желание наследовать от Автомобиля - CarForTourism, CarForTransportation. А еще вы хотите иметь комбинированные классы PassengerCarForTourism, PassengerCarForTransportation. и только когда появится такой ужас, вы будете вынуждены использовать "Мост" (иначе ваша ситуация не об этом и вы даже при желании не пользуетесь мостом).
Но я очень надеюсь, что не так, как описывают авторы паттерна, а так, как объясняет эта статья.
обновление2. Попробуем разобраться еще в одном недоразумении.
Конечно, для реализации «Моста» используется агрегация абстрактных классов, или используются интерфейсы, или что-то еще.
Речь идет не об этом.
Мы говорим о причинах – когда использовать паттерн «Мост», а когда нет. Речь идет о мотивации использования.
Когда есть определенная мотивация, описанная авторами, а частично здесь, в статье, тогда она необходима.
Но это не значит наоборот, что где бы вы ни использовали интерфейсы или абстрактные классы, это будет мост. Таким образом, приведенные примеры с Машиной и Движком не будут шаблоном «Мост» чисто семантически, хотя синтаксически вы можете видеть те же интерфейсы — но это не будет «Мост».
Это только один аспект. Еще одно и главное в статье, когда вы действительно столкнулись с ситуацией, в которой авторы рекомендуют использовать «Мост», я не рекомендую — это вредно, и есть другие способы.
Но главное, я не рекомендую думать как авторы паттерна — давайте отделим реализацию от абстракции.
обновление3. У меня проблемы с комментариями.
Здесь я отвечаю на два вопроса.
1. По поводу множественного наследования для TheShock. Ну видимо потому, что компилятор не может проверить, сделали вы это или нет, и поэтому возможны ошибки.
2. Интересная головоломка от Колвина.
Один из вариантов.
Первым шагом является создание иерархии ОС с преемниками Win и Os2. Создайте интерфейс ImageProcessing с помощью методов ImageMap LoadFromFile() и Paint(BigImage argImage).
Win и Os2 реализуют обработку изображений, каждая по-своему.
Избавление от BigImageImp путем рефакторинга в подготовленную иерархию.
В BigImage я агрегирую интерфейс ImageProcessing im. В потомках BigImage метод Load сначала вызывает im.LoadFromFile(), а метод Show, наконец, вызывает im.Paint(this).
Можно ли сказать, что это тоже мост? Но тогда, по крайней мере, семантика стала на порядок понятнее.
Вариант второй (сделан из первого, причем можно даже делать параллельно, т.е.
оставив первый вариант роботизированным).
Убедиться, что клиент использует его, можно через операционную систему, т. е.
вызвав OS.Paint(argBigImage).
Для этого необходимо, наоборот, чтобы операционная система вызывала методы BigImage. Те.
Paint вызывает argImageMap=LoadFromFile(), затем argBigImage.Load(argImageMap) — наследник изображения, например, BmpBigImage делает свою специфику.
Затем операционная система вызывает argBigImage.PrepareForShow() (это проясняет смысл метода Show изображения, так как по факту оказывается, что оно не может отобразить само себя), а затем операционная система фактически отображает его.
И моста здесь точно нет. А если убрать первое решение, то можно избавиться и от интерфейса ImageProcessing, но это необязательно.
И отметим, кстати, что второй вариант можно додумать только разложив первый на правильную семантику, но думая в рамках моста - вариантов действительно нет, и решение плохое.
Теги: #шаблоны проектирования #наследование #агрегация #агрегация #Идеальный код
-
Найдите Аниме В Интернете
19 Oct, 24 -
Покупка Macbook Pro В Apple Store, Дубликат?
19 Oct, 24 -
Услуги Wcf Ria. Обновление Данных. Часть 3
19 Oct, 24