Паттерн Bridge (Мост, Handle/Body) (реализация схемы)
Содержание
- 1. Общие сведения. Необходимость применения паттерна Bridge. Рисунок
- 2. Структура паттерна. Рисунок. Участники паттерна
- 3. Результаты применения (на основе рисунка)
- 4. Особенности реализации паттерна
- 5. Текст программы на языке C++. Реализация структуры
- 6. Применение паттерна Bridge
- Связанные темы
Поиск на других ресурсах:
1. Общие сведения. Необходимость применения паттерна Bridge. Рисунок
Паттерн Bridge относится к паттернам, структурирующим объекты. Основное предназначение паттерна – это отделение абстракции от реализации таким образом, чтобы и абстракцию и реализацию можно было изменять независимо.
Если нужно реализовать абстракцию несколькими способами, то обычно используют наследование. В этом случае в вершине иерархии есть абстрактный класс, содержащий общий интерфейс абстракции. С этого класса наследуются более конкретные классы, обеспечивающие конкретную реализацию.
Однако такой подход не всегда гибок. Причиной тому является строгое привязывание реализации к абстракции. Как следствие, независимая модификация и возможность расширения и повторного использования являются усложненными.
Например. Пусть задан абстрактный класс AbstractClass с двумя конкретными реализациями ImpClass1, ImpClass2, как показано на рисунке 1-а. Пусть возникла необходимость для каждой конкретной реализации добавить новую абстрактную. Эта возможность обеспечивается вводом абстрактного класса AbstractSubClass. Тогда для этой возможности дополнительно следует вводить поддержку реализаций классов ImpClass1, ImpClass2. Соответственно, введенные классы будут иметь имена ImpSubClass1, ImpSubClass2 (рисунок 1-b). Такой подход неудобен и требует повторного определения реализаций подклассов.
Рисунок 1. Расширение абстракции путем добавления классов AbstractSubClass, ImpSubClass1, ImpSubClass2
Также недостатком подхода, изображенного на рисунке 1, является то, что код клиента становится зависимым от конкретной реализации. При создании объекта ImpClass1 происходит привязывание абстракции к ее реализации, а значит, клиент ориентируется именно на этот вариант реализации. Таким образом, усложняется изменение реализации клиента. Это приводит к необходимости перекомпиляции клиентского кода.
⇑
2. Структура паттерна. Рисунок. Участники паттерна
Структура паттерна Bridge изображена на рисунке 2.
Рисунок 2. Структура паттерна Bridge
Дадим краткое описание классов этой структуры являющихся участниками паттерна.
- Абстрактный класс Abstraction представляет собой некоторую абстракцию, определяющую интерфейс абстракции. В этом классе сохраняется ссылка (указатель) на объект типа Implementor. Конкретные реализации абстракций (RefinedAbstraction) наследуются из этого класса.
- Класс RefinedAbstraction является конкретной реализацией абстракции, расширяющей Abstraction.
- Абстрактный класс Implementor – это интерфейс для классов конкретных реализаций (ConcreteImplementorA, ConcreteImplementorB). Этот класс не обязательно должен следовать интерфейсу класса Abstraction (вообще интерфейсы Abstraction и Implementor могут отличаться). Как правило, класс Implementor определяет только примитивные операции. В то же время, класс Abstraction определяет операции более высокого уровня, основанные на использовании операций класса Implementor.
- Классы ConcreteImplementorA, ConcreteImplementorB реализуют интерфейс Implementor. Это конкретные реализации.
⇑
3. Результаты применения (на основе рисунка)
В литературе определяют следующие результаты применения паттерна Bridge.
- Отделение реализации от интерфейса. Реализация не имеет постоянной привязки к интерфейсу. Реализация абстракции может быть конфигурирована при выполнении программы. Здесь нет зависимости между классами Abstraction и Implementor, которая устанавливается на этапе компиляции. Чтобы изменить реализацию, не нужно перекомпилировать класс Abstraction и его клиентов.
Другим преимуществом отделения реализации от интерфейса является то, что система разбивается на слои. Следовательно, улучшается структура системы. Части системы более высокого уровня знают только о классах Abstraction и Implementor.
- Повышается степень расширяемости системы. Иерархии классов Abstraction и Implementor независимы, поэтому их можно без проблем расширять.
- Скрытие деталей реализации от клиентов.
⇑
4. Особенности реализации паттерна
В зависимости от того, какую структуру имеют классы, можно выделить следующие основные моменты при реализации паттерна Bridge.
- Если условие задачи предполагает наличие только одного класса Implementor, возможно, необязательно делать этот класс абстрактным. Это вырожденный случай связи между классами Abstraction и Implementor.
- Выбор создаваемого объекта Implementor может быть выполнен в конструкторе класса Abstraction. Для этого конструктор должен иметь информацию о конкретных классах, реализующих интерфейс Implementor. Способ выбора того или иного конкретного класса определяется условием задачи и может быть реализован по-разному.
- При правильном построении кода можно добиться распределения реализаторов. Это означает, что одна и та же реализация может использоваться несколькими объектами. Чтобы это стало возможным, нужно ввести счетчик ссылок на объект и в операции присвоения ссылок (operator=()) выполнять проверку количества ссылок.
⇑
5. Текст программы на языке C++. Реализация структуры
В примере показывается реализация паттерна Bridge, структура которого изображена на рисунке 2.
// Паттерн Bridge (Міст) #include <iostream> using namespace std; // Класс, являющийся интерфейсом реализации class Implementor abstract { public: virtual void OperationImp() abstract; }; // Конкретная реализация 1 class ConcreteImplementorA : public Implementor { public: void OperationImp() override { // здесь выполняется какая то работа cout << "ConcreteImplementorA::OperationImp()" << endl; } }; // Конкретная реализация 2 class ConcreteImplementorB : public Implementor { public: void OperationImp() override { // некоторая работа cout << "ConcreteImplementorB::OperationImp()" << endl; } }; // Класс, который есть интерфейсом абстракции class Abstraction abstract { private: Implementor* imp = nullptr; public: // Конструктор - получает номер реализации Abstraction(int numImp) { // Формируется вид реализации if (numImp == 1) imp = new ConcreteImplementorA; else imp = new ConcreteImplementorB; } // Операция void Operation() { imp->OperationImp(); } // Деструктор virtual ~Abstraction() { if (imp) delete imp; } }; // Уточненная абстракция class RefinedAbstraction : public Abstraction { public: // Конструктор RefinedAbstraction(int numImp) : Abstraction(numImp) { } }; void main() { // Клиент – содержит ссылку на абстракцию Abstraction* obj = nullptr; // Организация меню int numImp; cout << "Enter Implementation (1-2):" << endl; cin >> numImp; // Создать абстракцию obj = new RefinedAbstraction(numImp); // Вызвать операцию obj->Operation(); if (obj) delete obj; }
⇑
6. Применение паттерна Bridge
Паттерн Bridge применяется в следующих случаях.
- Когда следует избежать постоянной привязки абстракции к реализации. Примером этому может служить необходимость выбора реализации во время выполнении программы.
- Когда задача предполагает расширение новыми подклассами как абстракции, так и реализации. Паттерн Bridge позволяет комбинировать различные абстракции и реализации, а также изменять их независимо друг от друга.
- Изменения, вносимые в реализацию абстракции, не должны влиять на клиентов. То есть клиентский код не должен перекомпилироваться.
- Если нужно полностью скрыть реализацию абстракции от клиентов.
- В случае, когда при внесении изменений в условие задачи число классов абстракции начинает возрастать. Здесь возникает необходимость разделения иерархии на две части.
- Когда нужно скрыть от клиента разделение одной реализации между несколькими объектами.
⇑
Связанные темы
- Паттерн Adapter. Обзор и исследование паттерна. Примеры реализации на C++
- Паттерн Composite (Компоновщик). Дерево. Реализация структуры на C++
- Паттерн Facade (Фасад). Реализация структуры на C++
⇑