CodeLAB
на главную карта сайта обратная связь
каталог | задачи | паттерны | исходники | стат | форумы | ссылки
 гость
искать в
Главная >> Каталог задач >> Паттерны >> Структурные >> Мост (Bridge)

<< назад
распечатать обсудить >>


Мост (Bridge)
реализации: java, количество: 10

Aвтор: this
Дата: 07.07.2007
Просмотров: 42677
Рейтинг: 0/0,0(0)
+
реализации(исходники) +добавить

Имя

«Паттерн
Bridge» оригинал источник codelab.ru codelab.ru

Мост – паттерн, оптимальным образом структурирующий используемые иерархии общих абстракций и их конкретных реализаций. Известен также под именем Handle/Body (описатель/тело). codelab.ru codelab.ru источник оригинал

Условия, Задача, Назначение

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

Мотивация

Рассмотрим реализацию переносимой (повторно используемой) абстракции окна в библиотеке для разработки пользовательских интерфейсов. Написанные с ее помощью приложения должны работать в разных средах, например под X Window System и Presentation Manager (PM) от компании IBM. С помощью наследования мы могли бы определить абстрактный класс Window и его подклассы XWindow и PMWindow, реализующие интерфейс окна для разных платформ. Но у такого решения есть два недостатка: codelab.ru источник оригинал codelab.ru
  1. Неудобно распространять абстракцию Window на другие виды окон или новые платформы. Представьте себе абстрактный подкласс IconWindow, который специализирует (уточняет) абстракцию окна для пиктограмм. Чтобы поддержать пиктограммы на обеих платформах, нам придется реализовать два новых подкласса XlconWindow и PMIconWindow. И так далее по два подкласса необходимо будет также определять для каждого нового вида окон (DialogWindow) впоследствии. А для поддержки третьей платформы придется вообще для всех видов окон (Window, IconWindow, DialogWindow) определять по новому подклассу.
    codelab.ru codelab.ru оригинал источник
    оригинал codelab.ru источник codelab.ru
  2. Клиентский код становится платформенно-зависимым. При создании окна клиент инстанцирует конкретный класс, имеющий вполне определенную реализацию. Например, создавая объект XWindow, мы привязываем абстракцию окна к ее реализации для системы X Window и, следовательно, делаем код клиента ориентированным именно на эту оконную систему. Таким образом, усложняется перенос клиента на другие платформы.
    Клиенты должны иметь возможность создавать окно, не привязываясь к конкретной реализации. Только сама реализация окна должна зависеть от платформы, на которой работает приложение. Поэтому в клиентском коде не может быть никаких упоминаний о платформах.
    Паттерн абстрактная фабрика конечно решил бы эту проблему, но от п.1 все равно не избавил бы, а наоборот усложнил бы проблему введения новых видов окон. codelab.ru источник оригинал codelab.ru
    оригинал codelab.ru codelab.ru источник
С помощью паттерна мост эти проблемы решаются. Абстракция окна и ее реализация помещаются в раздельные иерархии классов. Таким образом, существует одна иерархия для абстрактных классов окон (Window, IconWindow, TransientWindow) и другая  (с корнем WindowImp) - для платформенно-зависимых конкретных реализаций. Так, подкласс XWindowImp предоставляет реализацию в системе X Window System. источник оригинал codelab.ru codelab.ru

источник codelab.ru codelab.ru оригинал

Все операции подклассов Window реализованы в терминах абстрактных операций из интерфейса WindowImp. Это отделяет абстракцию окна от различных ее платформенно-зависимых реализаций. Отношение между классами Window и WindowImp мы будем называть мостом, поскольку между абстракцией и реализацией строится своего рода мост, и они могут изменяться независимо. источник codelab.ru codelab.ru оригинал
оригинал codelab.ru codelab.ru источник

Признаки применения, использования паттерна Мост (Bridge)

Используйте паттерн мост, когда: оригинал источник codelab.ru codelab.ru
  1. Нужно избежать постоянной привязки абстракции к реализации. codelab.ru оригинал codelab.ru источник
  2. Когда конкретную реализацию необходимо выбирать во время выполнения программы. codelab.ru источник codelab.ru оригинал
  3. И абстракции, и реализации должны расширяться новыми подклассами. В таком случае паттерн мост позволяет комбинировать разные абстракции и реализации и изменять их независимо. оригинал codelab.ru источник codelab.ru
  4. Изменения в реализации абстракции не должны сказываться на клиентах, то есть клиентский код не должен перекомпилироваться. оригинал codelab.ru codelab.ru источник
  5. Количество классов начинает быстро расти, как мы видели на первой диаграмме из примера. Это признак того, что иерархию следует разделить на 2 части. источник оригинал codelab.ru codelab.ru
  6. Вы хотите разделить одну и ту же реализацию между несколькими объектами, и этот факт необходимо скрыть от клиента. источник оригинал codelab.ru codelab.ru

Решение

источник codelab.ru codelab.ru оригинал

codelab.ru оригинал codelab.ru источник

Участники паттерна Мост (Bridge)

источник codelab.ru оригинал codelab.ru

  1. Abstraction (Window) – абстракция.
    Определяет интерфейс абстракции и агрегирует (хранит ссылку) на объект типа Implement. источник codelab.ru оригинал codelab.ru
  2. RefinedAbstraction (IconWindow) - уточненная абстракция.
    Более специфичный подкласс основной абстракции, расширяет интерфейс, определенный абстракцией Abstraction. оригинал источник codelab.ru codelab.ru
  3. Implementor (WindowImp) – исполнитель.
    Определяет интерфейс для классов реализации. Он не обязан точно соответствовать интерфейсу класса Abstraction, более того оба интерфейса могут быть совершенно различны. Обычно интерфейс класса Implementor предоставляет только примитивные операции, а класс Abstraction определяет операции более высокого уровня, базирующиеся на этих примитивах. оригинал источник codelab.ru codelab.ru
  4. ConcreteImplementor (XWindowlmp, PMWindowlmp) – один из конкретных (платформо-зависимых) исполнителей.
    Содержит конкретную реализацию интерфейса класса Implementor, т.е. по-своему реализует примитивы, определенные в Implementor-е. codelab.ru источник codelab.ru оригинал

Схема использования паттерна Мост (Bridge)

Объект Abstraction решает свои задачи, реализует свою логику, пользуюсь методами объекта класса Implementor (примитивами), ссылку на которого он получает когда-то ранее, перед началом своей работы. codelab.ru codelab.ru источник оригинал
  codelab.ru источник codelab.ru оригинал

Вопросы, касающиеся реализации паттерна Мост (Bridge)

Если вы предполагаете применить паттерн мост, то подумайте еще о таких вопросах реализации: codelab.ru источник codelab.ru оригинал
  1. Только один класс Implementor.
    В ситуациях, когда есть только одна реализация, создавать абстрактный класс Implementor необязательно. Это вырожденный случай паттерн мост – между классами Abstraction и Implementor существует взаимно-однозначное соответствие. Тем не менее разделение все же полезно, если нужно, чтобы изменение реализации класса не отражалось на существующих клиентах (должно быть достаточно заново скомпоновать программу, не перекомпилируя клиентский код).
    В C++ интерфейс класса Implementor можно определить в закрытом заголовочном файле, который не передается клиентам. Это позволяет полностью скрыть реализацию класса от клиентов. codelab.ru codelab.ru оригинал источник
  2. Cоздание правильного объекта Implementor.
    Как, когда и где принимается решение о том, какой из нескольких классов Implementor инстанцировать?
    Если у класса Abstraction есть информация о конкретных классах Concretelmplementor, то он может инстанцировать один из них в своем конструкторе; какой именно - зависит от переданных конструктору параметров.
    Так, если класс коллекции поддерживает несколько реализаций, то решение можно принять в зависимости от размера коллекции. Для небольших коллекций применяется реализация в виде связанного списка, для больших – в виде хэшированных таблиц.
    Другой подход - заранее выбрать реализацию по умолчанию, а позже изменять ее в соответствии с тем, как она используется. Например, если число элементов в коллекции становится больше некоторой условной величины, то мы переключаемся с одной реализации на другую, более эффективную.
    Можно также делегировать решение другому объекту. В примере с иерархиями Window/WindowImp уместно было бы ввести фабричный объект (см. паттерн абстрактная фабрика), единственная задача которого - инкапсулировать платформенную специфику. Фабрика обладает информацией, объекты Windowlmp какого вида надо создавать для данной платформы, а объект Window просто обращается к ней с запросом о предоставлении какого-нибудь объекта Windowlmp, при этом понятно, что объект получит то, что нужно. Преимущество описанного подхода: класс Abstraction становится вообще уже напрямую не привязан ни к одному из классов Implementor – а, как мы знаем, чем меньше связанность сущностей, тем более система становится гибкой. источник codelab.ru оригинал codelab.ru
  3. Некорректность использования множественного наследования
    В C++, например, для объединения интерфейса с его реализацией можно воспользоваться множественным наследованием. Например, класс может открыто наследовать классу Abstraction и закрыто – классу ConcreteImplementor. Но такое решение зависит от статического наследования и жестко привязывает реализацию к ее интерфейсу. Поэтому реализовать настоящий мост с помощью множественного наследования невозможно, по крайней мере в C++. оригинал источник codelab.ru codelab.ru

Результаты

Результаты применения паттерна мост: оригинал codelab.ru источник codelab.ru
  1. Отделение реализации от интерфейса.
    Реализация больше не имеет постоянной привязки к интерфейсу. Реализацию абстракции можно конфигурировать во время выполнения. Объект может даже динамически изменять свою реализацию.
    Разделение классов Abstraction и Implementor устраняет также зависимости от реализации, устанавливаемые на этапе компиляции. Чтобы изменить класс реализации, теперь вовсе не обязательно перекомпилировать класс Abstraction и его клиентов. Это свойство особенно важно, если необходимо обеспечить двоичную совместимость между разными версиями библиотеки классов.
    Кроме того, такое разделение облегчает разбиение системы на слои и тем самым позволяет улучшить ее структуру. Высокоуровневые части системы должны знать только о классах Abstraction и Implementor. источник codelab.ru оригинал codelab.ru
  2. Повышение степени расширяемости.
    Можно расширять независимо иерархии классов Abstraction и Implementor. источник оригинал codelab.ru codelab.ru
  3. Сокрытие деталей реализации от клиентов.
    Клиенты, пользующиеся интерфейсом Abstraction, изолируются от деталей реализации этих операций, которые на самом деле выполняются с помощью вызовов методов Implementor-а. Что делает возможным прозрачную подмену Implementor-ов как статически на этапе компиляции, так и динамически. оригинал codelab.ru codelab.ru источник

Пример

Рассмотрим задачу построения общей системы команд. Существуют общие интерфейсы для выполнения тех или иных команд. При этом сами команды ничем не ограничиваются и могут выполнять любые задачи: редактирование файлов, работа с базой данных, отсылка e-mail и т.д. Соответственно, эту разнообразность команд необходимо абстрагировать, так чтобы клиенты, ничего не зная об их сущности, были способны запустить и обработать любую команду. источник codelab.ru codelab.ru оригинал
Паттерн мост здесь послужит одним из самых подходящих решений. Составляем иерархию общих интерфейсов команд, и попутную иерархию конкретных реализаций. codelab.ru оригинал codelab.ru источник

В качестве Abstraction выступает самый общий класс Command: Command. оригинал codelab.ru codelab.ru источник

Он агрегирует, хранит ссылку класс реализации команды CommandImpl (Implementor): CommandImpl. codelab.ru оригинал codelab.ru источник

Далее уточняем разновидности команд (RefinedAbstraction), как-то команды на удаление, добавление, редактирование. Например, можно выделить команды на редактирование и удаление: UpdateCommand, AddCommand. codelab.ru codelab.ru оригинал источник

Как видно, они для своих целей тоже пользуются интерфейсом ImplementorCommandImpl. оригинал codelab.ru codelab.ru источник
Таким образом, получаем общие интерфейсы, с которыми будут работать клиенты: выполнять команды и анализировать ошибки в случае неудачи. codelab.ru оригинал codelab.ru источник

Теперь пора перейти к реализации конкретных алгоритмов обработки данных видов команд. Для реализации этих 2-х команд в файловом контексте, т.е. по отношению к файлам, будет достаточно определить следующие ConcreteImplementator-ы: FileCommand, UpdateFileCommand, AddFileCommand. codelab.ru codelab.ru оригинал источник

Соответственно для редактирования файла, объект класса UpdateCommand (или Command) нужно будет сконфигурировать настроенным объектом UpdateFileCommand, а для добавления файла – объектом AddFileCommand. оригинал codelab.ru источник codelab.ru

Для работы с базой данной ConcreteImplementator-ы примут вид: SQLCommand, SQLUpdateCommand. codelab.ru codelab.ru оригинал источник

Заметьте, что не приводится специальная реализация для команды добавления – в данном случае можно использовать ту же SQLUpdateCommand, т.к. все необходимые процедуры она выполняет (по крайней мере, они не отличаются от случая добавления – разница будет лишь в тексте SQL-запроса). И в этом еще одна гибкость паттерна мост – в большинстве случаев нам не приходится выстраивать полные строго параллельные иерархии реализаций для всех абстракций, т.к. часто одни и те же ConcreteImplementor-ы удовлетворяют нескольким RefinedAbstraction-нам. codelab.ru codelab.ru источник оригинал
  codelab.ru codelab.ru оригинал источник
Все, - теперь клиенты нашей системы могут свободно запускать данные команды добавления и редактирования в контекстах файлов или базы данных: Client. codelab.ru codelab.ru оригинал источник
Обратите на код в методе main – это то, что и достигается применением данного паттерна: независимый, единый код запуска любых видов команд в любом контексте, который не нужно будет менять и перекомпилировать при каких-либо изменениях в деталях реализации тех или иных операций, алгоритмов выполнения команд и т.д. оригинал codelab.ru источник codelab.ru
Конкретные реализации clientUpdateImpl и balanceCorrectImpl для простоты здесь определяются напрямую, но как говорилось выше часто бывает полезно получать их динамически в конструкторе команды (Command) из абстрактной фабрики. codelab.ru источник codelab.ru оригинал

  источник codelab.ru оригинал codelab.ru

Известные применения паттерна Мост (Bridge)

Пример класса Window позаимствован из ЕТ++. В ЕТ++ класс WindowImp называется WindowPort и имеет такие подклассы, как XWindowPort и SunWindowPort. Объект Window создает соответствующего себе реализатора Implementor, запрашивая его у абстрактной фабрики, которая называется WindowSystem. Эта фабрика предоставляет интерфейс для создания платформенно-зависимых объектов: шрифтов, курсоров, растровых изображений и т.д. codelab.ru источник codelab.ru оригинал
Дизайн классов Window/WindowPort в ЕТ++ обобщает паттерн мост в том отношении, что WindowPort сохраняет также обратную ссылку на Window. Класс-реализатор WindowPort использует эту ссылку для извещения Window о событиях, специфичных для WindowPort: поступлений событий ввода, изменениях размера окна и т.д. codelab.ru оригинал codelab.ru источник
В работах Джеймса Коплиена и Бьерна Страуструпа упоминаются классы описателей и приводятся некоторые примеры. Основной акцент в этих примерах сделан на вопросах управления памятью, например разделении представления строк и поддержке объектов переменного размера. Нас же в первую очередь интересует поддержка независимых расширений абстракции и ее реализации. codelab.ru codelab.ru оригинал источник
В библиотеке libg++ определены классы, которые реализуют универсальные структуры данных: Set (множество), LinkedSet (множество как связанный список), HashSet (множество какхэш-таблица), LihkedList (связанный список) и HashTable (хэш-таблица). Set - это абстрактный класс, определяющий абстракцию множества, a LinkedList и HashTable - конкретные реализации связанного списка и хэш-таблицы. LinkedSet и HashSet - реализаторы абстракции Set, перекидывающие мост между Set и LinkedList и HashTable соответственно. Перед вами пример вырожденного моста, поскольку абстрактного класса Implement or здесь нет. оригинал codelab.ru codelab.ru источник
В библиотеке NeXT AppKit паттерн мост используется при реализации и отображении графических изображений. Рисунок может быть представлен по-разному. Оптимальный способ его отображения на экране зависит от свойств дисплея и прежде всего от числа цветов и разрешения. Если бы не AppKit, то для каждого приложения разработчикам пришлось бы самостоятельно выяснять, какой реализацией пользоваться в конкретных условиях. источник codelab.ru оригинал codelab.ru

AppKit предоставляет мост [google=NXImage NXImageRep]NXImage/NXImageRep[google]. Класс NXImage определяет интерфейс для обработки изображений. Реализация же определена в отдельной иерархии классов NXImageRep, в которой есть такие подклассы, как NXEPSImageRep, NXCachedlmageRep и NXBitMapImageRep. В классе NXImage хранятся ссылки на один или более объектов NXImageRep. Если имеется более одной реализации изображения, то NXImage выбирает самую подходящую для данного дисплея. При необходимости NXImage также может преобразовать изображение из одного формата в другой. Интересная особенность этого варианта моста в том, что NXImage может одновременно хранить несколько реализаций NXImageRep. codelab.ru оригинал codelab.ru источник

Родственные паттерны

Паттерн абстрактная фабрика может создать и сконфигурировать мост.
Для обеспечения совместной работы не связанных между собой классов прежде всего предназначен паттерн адаптер. Обычно он применяется в уже готовых системах. Мост же участвует в проекте с самого начала и призван поддержать возможность независимого изменения абстракций и их реализаций. источник codelab.ru codelab.ru оригинал


Реализации: java(10)   +добавить реализацию

1) Command.java, code #442[автор:this]
2) CommandImpl.java, code #443[автор:this]
3) UpdateCommand.java, code #444[автор:this]
4) AddCommand.java, code #445[автор:this]
5) FileCommand.java, code #446[автор:this]
6) UpdateFileCommand.java, code #447[автор:this]
7) AddFileCommand.java, code #448[автор:this]
8) SQLCommand.java, code #449[автор:this]
9) SQLUpdateCommand.java, code #450[автор:this]
10) Client.java, code #451[автор:this]


<< назад наверх
распечатать обсудить >>

 
каталог | задачи | паттерны | исходники | стат | форумы | карта сайта | контакты | ссылки 
© 2000-2017 CodeLAB Group
  Все права защищены
Страница сгенерирована за 0.037868 секунд
Количество запросов к БД: 14, gzip: 22.2kb/129.4kb(83%)