Мост (Bridge)
Другое название
Назначение
Позволяет менять интерфейс и реализацию независимо друг от друга.
Описание
Если для некоторой абстракции возможно несколько реализаций, то применяют наследование. Однако, такой подход плохо работает при наличии N абстракций и M реализаций: необходимо создать N*M классов, каждый из которых для i-той абстракции дает j-тую реализацию.
Например, при реализации библиотеки пользовательского интерфейса с N компонентами - наследниками класса Window - для M операционных систем потребуется полностью реализовывать N*M классов.
Паттерн Мост позволяет свести количество необходимых классов к N+M. Таким образом, при добавлении нового оконного компонента мы должны добавить один класс независимо от количества операционных систем. Аналогично при реализации всех компонент для новой операционной системы мы должны также добавить только один класс.
Основная идея паттерна Мост заключается в следующем: для базовой абстракции (в нашем примере для класса Window) имеется класс WindowImp (реализация), содержащий интерфейс примитивов рисования графических компонент. В классе Window все примитивы рисования реализуются делегированием к операциям класса WindowImp. Теперь в потомках класса Window мы реализуем рисование компонент с помощью базового набора примитивов в классе Window, а примитивы в классе WindowImp реализуются в подклассах для каждой операционной системы.
Связка классов Window - WindowImp и называется Мост.
Использование
Паттерн Мост используется когда
- Необходимо менять реализацию во время выполнения
- И абстракции и реализации должны расширяться новыми подклассами
- При изменениях в реализации абстракции клиентский код, работающий с абстракцией, не должен перекомпилироваться
Реализация
Диаграмма классов для примера Window - WindowImp
Диаграмма классов в общем случае
Участники
- Abstraction - абстракция
Определяет интерфейс абстракции. Дает реализацию этого интерфейса, вызывая методы Implementorа
- RefinedAbstraction - уточненная абстракция
Расширяет интерфейс абстракции. Дает реализацию новых методов как в терминах методов базового класса, так и вызывая методы Implementorа
- Implementor - реализатор
Определяет интерфейс для классов реализации. Он не обязан следовать интерфейсу абстракции. Обычно интерфейс класса Implementor предоставляет только примитивные операции, а класс Abstraction определяет операции более высокого уровня.
- ConcreteImplementor - конкретный реализатор
Содержит реализацию интерфейса класса Implementor
Пример
class MainApp
{
static void Main()
{
Abstraction ab = new RefinedAbstraction();
ab.Implementor = new ConcreteImplementorX();
ab.OperationA();
ab.OperationB();
ab.Implementor = new ConcreteImplementorY();
ab.OperationA();
ab.OperationB();
}
}
class Abstraction
{
public Implementor Implementor
{
get; set;
}
public virtual void OperationA()
{
Implementor.Op1();
}
public virtual void OperationB()
{
Implementor.Op2();
}
}
abstract class Implementor
{
public abstract void Op1();
public abstract void Op2();
}
class RefinedAbstraction : Abstraction
{
public override void OperationB()
{
OperationA();
Implementor.Op2();
}
}
class ConcreteImplementorX : Implementor
{
public override void Op1()
{
Console.WriteLine("ConcreteImplementorX Op1");
}
public override void Op2()
{
Console.WriteLine("ConcreteImplementorX Op2");
}
}
class ConcreteImplementorY : Implementor
{
public override void Op1()
{
Console.WriteLine("ConcreteImplementorY Op1");
}
public override void Op2()
{
Console.WriteLine("ConcreteImplementorY Op2");
}
}
Достоинства и недостатки
- Интерфейс отделен от реализации. Реализацию можно конфигурировать на этапе выполнения.
- Устранение зависимости от реализации на этапе компиляции
- Независимое расширение абстракций и реализаций. Мультиплексирование вариантов внешнего представления и внутренней реализации означает, что можно сочетать каждое внутреннее представление с каждой реализацией.
Варианты
- Инициализация конкретного Implementor в конструкторе Abstraction в зависимости от переданных параметров.