- Мост (шаблон проектирования)
-
Шаблон проектирования Мост Bridge Тип: структурный
Описан в Design Patterns Да
Bridge, Мост — шаблон проектирования, используемый в проектировании программного обеспечения чтобы «разделять абстракцию и реализацию так, чтобы они могли изменяться независимо». Шаблон bridge (от англ. — мост) использует инкапсуляцию, агрегирование и может использовать наследование для того, чтобы разделить ответственность между классами.
Содержание
Цель
При частом изменении класса преимущества объектно-ориентированного подхода становятся очень полезными, позволяя делать изменения в программе, обладая минимальными сведениями о реализации программы. Шаблон bridge является полезным там, где часто меняется не только сам класс, но и то, что он делает.
Описание
Когда абстракция и реализация разделены, они могут изменяться независимо. Другими словами, при реализации через паттерн мост, изменение структуры интерфейса не мешает изменению структуры реализации. Рассмотрим такую абстракцию как фигура. Существует множество типов фигур, каждая со своими свойствами и методами. Однако есть что-то, что объединяет все фигуры. Например, каждая фигура должна уметь рисовать себя, масштабироваться и т. п. В то же время рисование графики может отличаться в зависимости от типа ОС, или графической библиотеки. Фигуры должны иметь возможность рисовать себя в различных графических средах, но реализовывать в каждой фигуре все способы рисования или модифицировать фигуру каждый раз при изменении способа рисования непрактично. В этом случае помогает шаблон bridge, позволяя создавать новые классы, которые будут реализовывать рисование в различных графических средах. При использовании такого подхода очень легко можно добавлять как новые фигуры, так и способы их рисования.
Связь, изображаемая стрелкой на диаграммах, может иметь 2 смысла: а) "разновидность", в соответствии с принципом подстановки Б. Лисков и б) одна из возможных реализаций абстракции. Обычно в языках используется наследование для реализации как а), так и б), что приводит к разбуханию иерархий классов.
Мост служит именно для решения этой проблемы: объекты создаются парами из объекта класса иерархии А и иерархии B, наследование внутри иерархии А имеет смысл "разновидность" по Лисков, а для понятия "реализация абстракции" используется ссылка из объекта A в парный ему объект B.
Использование
Архитектура Java AWT полностью основана на этом паттерне - иерархия java.awt.xxx для хэндлов и sun.awt.xxx для реализаций.
Примеры
Пример на Java
Исходный текст на языке Javapackage com.designpatterns.bridge; /* Файл Drawer.java * * */ public interface Drawer { public void drawCircle(int x, int y, int radius); } package com.designpatterns.bridge; /* Файл SmallCircleDrawer.java * * */ public class SmallCircleDrawer implements Drawer{ public static final double radiusMultiplier = 0.25; @Override public void drawCircle(int x, int y, int radius) { System.out.println("Small circle center = " + x + "," + y + " radius = " + radius*radiusMultiplier); } } package com.designpatterns.bridge; /* Файл LargeCircleDrawer.java * * */ public class LargeCircleDrawer implements Drawer{ public static final int radiusMultiplier = 10; @Override public void drawCircle(int x, int y, int radius) { System.out.println("Large circle center = " + x + "," + y + " radius = " + radius*radiusMultiplier); } } package com.designpatterns.bridge; /* Файл Shape.java * * */ public abstract class Shape { protected Drawer drawer; protected Shape(Drawer drawer){ this.drawer = drawer; } public abstract void draw(); public abstract void enlargeRadius(int multiplier); } package com.designpatterns.bridge; /* Файл Circle.java * * */ public class Circle extends Shape{ private int x; private int y; private int radius; public Circle(int x, int y, int radius, Drawer drawer) { super(drawer); setX(x); setY(y); setRadius(radius); } @Override public void draw() { drawer.drawCircle(x, y, radius); } @Override public void enlargeRadius(int multiplier) { radius *= multiplier; } public int getX() { return x; } public int getY() { return y; } public int getRadius() { return radius; } public void setX(int x) { this.x = x; } public void setY(int y) { this.y = y; } public void setRadius(int radius) { this.radius = radius; } } package com.designpatterns.bridge; /* Класс, показывающий работу шаблона проектирования "Мост". * Файл Application.java * * */ public class Application { public static void main (String [] args){ Shape [] shapes = { new Circle(5,10,10, new LargeCircleDrawer()), new Circle(20,30,100, new SmallCircleDrawer())}; for (Shape next : shapes){ next.draw(); } } } // Output Large circle center = 5,10 radius = 100 Small circle center = 20,30 radius = 25.0
Пример на C#
Исходный текст на языке C#using System; namespace Bridge { // MainApp test application class MainApp { static void Main() { Abstraction ab = new RefinedAbstraction(); // Set implementation and call ab.Implementor = new ConcreteImplementorA(); ab.Operation(); // Change implementation and call ab.Implementor = new ConcreteImplementorB(); ab.Operation(); // Wait for user Console.Read(); } } /// <summary> /// Abstraction - абстракция /// </summary> /// <remarks> /// <li> /// <lu>определяем интерфейс абстракции;</lu> /// <lu>хранит ссылку на объект <see cref="Implementor"/></lu> /// </li> /// </remarks> class Abstraction { protected Implementor implementor; // Property public Implementor Implementor { get{ return implementor; } set{ implementor = value; } } public virtual void Operation() { implementor.Operation(); } } /// <summary> /// Implementor - реализатор /// </summary> /// <remarks> /// <li> /// <lu>определяет интерфейс для классов реализации. Он не обязан точно /// соотведствовать интерфейсу класса <see cref="Abstraction"/>. На самом деле оба /// интерфейса могут быть совершенно различны. Обычно интерфейс класса /// <see cref="Implementor"/> представляет только примитивные операции, а класс /// <see cref="Abstraction"/> определяет операции более высокого уровня, /// базирующиеся на этих примитивах;</lu> /// </li> /// </remarks> abstract class Implementor { public abstract void Operation(); } /// <summary> /// RefinedAbstraction - уточненная абстракция /// </summary> /// <remarks> /// <li> /// <lu>расширяет интерфейс, определенный абстракцией <see cref="Abstraction"/></lu> /// </li> /// </remarks> class RefinedAbstraction : Abstraction { public override void Operation() { implementor.Operation(); } } /// <summary> /// ConcreteImplementor - конкретный реализатор /// </summary> /// <remarks> /// <li> /// <lu>содержит конкретную реализацию интерфейса <see cref="Implementor"/></lu> /// </li> /// </remarks> class ConcreteImplementorA : Implementor { public override void Operation() { Console.WriteLine("ConcreteImplementorA Operation"); } } // "ConcreteImplementorB" class ConcreteImplementorB : Implementor { public override void Operation() { Console.WriteLine("ConcreteImplementorB Operation"); } } }
Пример на PHP5
Исходный текст на языке PHP5interface IDrawer { public function drawCircle($x, $y, $radius); } class SmallCircleDrawer implements IDrawer { const RADIUS_MULTIPLIER = 0.25; public function drawCircle($x, $y, $radius) { echo 'Small circle center = ( '.$x.', '.$y.' ) radius = '.($radius * self::RADIUS_MULTIPLIER).'<br>'; } } class LargeCircleDrawer implements IDrawer { const RADIUS_MULTIPLIER = 10; public function drawCircle($x, $y, $radius) { echo 'Large circle center = ( '.$x.', '.$y.' ) radius = '.($radius * self::RADIUS_MULTIPLIER).'<br>'; } } abstract class Shape { protected $drawer; protected function __construct(IDrawer $drawer) { $this->drawer = $drawer; } abstract public function draw(); abstract public function enlargeRadius($multiplier); } class Circle extends Shape { private $x; private $y; private $radius; public function __construct($x, $y, $radius, IDrawer $drawer) { parent::__construct($drawer); $this->x = $x; $this->y = $y; $this->radius = $radius; } public function draw() { $this->drawer->drawCircle($this->x, $this->y, $this->radius); } public function enlargeRadius($multiplier) { $this->radius *= $multiplier; } } $circle = new Circle(5, 10, 10, new LargeCircleDrawer()); $circle->draw(); // Large circle center = ( 5, 10 ) radius = 100 $circle = new Circle(20, 30, 100, new SmallCircleDrawer()); $circle->draw(); // Small circle center = ( 20, 30 ) radius = 25
Пример на CoffeeScript
Исходный текст на языке CoffeeScript# Implementor class IStorage get : (key) -> set : (key, value) -> # ConcreteImplementor class IFlashStorage extends IStorage # ... # ConcreteImplementor class IJavaStorage extends IStorage # ... # ConcreteImplementor class ISessionStorage extends IStorage # ... # ConcreteImplementor class ICookieStorage extends IStorage # ... # ConcreteImplementor class IGhostStorage extends IStorage # ... # Abstraction class AStorage # protected _implementer : if sessionStorage new ISessionStorage else if navigator.plugins["Shockwave Flash"] new IFlashStorage else if navigator.javaEnabled() new IJavaStorage else if navigator.cookieEnabled new ICookieStorage else new IGhostStorage # public load : (key) -> forgot : (key) -> save : (key, value) -> # RefinedAbstraction class InfoStorage extends AStorage load : (key) -> @_implementer.get("Info:#{key}") save : (key, value) -> @_implementer.set("Info:#{key}", value) forgot : (key) -> @_implementer.set("Info:#{key}", null)
Пример JavaScript
Исходный текст на языке JavaScript// Implementor ("интерфейс") function Implementor() { this.operation = function() {}; } // ConcreteImplementor (реализация Implementor) function ConcreteImplementorA() { this.operation = function() { alert("ConcreteImplementorA.operation"); }; } ConcreteImplementorA.prototype = new Implementor(); ConcreteImplementorA.prototype.constructor = ConcreteImplementorA; function ConcreteImplementorB() { this.operation = function() { alert("ConcreteImplementorB.operation"); }; } ConcreteImplementorB.prototype = new Implementor(); ConcreteImplementorB.prototype.constructor = ConcreteImplementorB; // Abstraction function Abstraction() { var implementor; this.getImplementor = function() { // доступ к implementor'у из RefinedAbstraction return implementor; }; this.setImplementor = function(val) { implementor = val; }; this.operation = function() { implementor.operation(); }; } // RefinedAbstraction function RefinedAbstraction() { var abstr = new Abstraction(); this.setImplementor = function(val) { abstr.setImplementor(val); }; this.operation = function() { abstr.operation(); }; } // использование: var refAbstr = new RefinedAbstraction(); refAbstr.setImplementor( new ConcreteImplementorA() ); refAbstr.operation(); // "ConcreteImplementorA.operation" refAbstr.setImplementor( new ConcreteImplementorB() ); refAbstr.operation(); // "ConcreteImplementorB.operation"
Без необходимости перегрузки методов Abstraction, можно значительно упростить RefinedAbstraction:
function RefinedAbstraction() { Abstraction.call(this); }
Так же можно сохранить ссылки на перегружаемые методы сразу после инстанцирования Abstraction:
function RefinedAbstraction() { Abstraction.call(this); var abstr_setImplementor = this.setImplementor; this.setImplementor = function(val) { abstr_setImplementor(val); }; }
Пример на VB.NET
Исходный текст на языке VB.NETNamespace Bridge ' Program - Тестовое приложение Class Program Shared Sub Main() Dim AB As Abstraction = New RefinedAbstraction() ' Установить реализацию и вызвать AB.Implementor = New ConcreteImplementorA() AB.Operation() ' Установить реализацию и вызвать AB.Implementor = New ConcreteImplementorB() AB.Operation() ' Ожидать действия пользователя Console.Read() End Sub End Class ''' <summary> ''' Abstraction - абстракция ''' </summary> ''' <remarks> ''' <li> ''' <lu>определяем интерфейс абстракции;</lu> ''' <lu>хранит ссылку на объект <see cref="Implementor"/></lu> ''' </li> ''' </remarks> Class Abstraction Protected m_implementor As Implementor ' Свойство Public Property Implementor() As Implementor Get Return m_implementor End Get Set(ByVal value As Implementor) m_implementor = value End Set End Property Public Overridable Sub Operation() m_implementor.Operation() End Sub End Class ''' <summary> ''' Implementor - реализатор ''' </summary> ''' <remarks> ''' <li> ''' <lu>определяет интерфейс для классов реализации. Он не обязан точно ''' соотведствовать интерфейсу класса <see cref="Abstraction"/>. На самом деле оба ''' интерфейса могут быть совершенно различны. Обычно интерфейс класса ''' <see cref="Implementor"/> представляет только примитивные операции, а класс ''' <see cref="Abstraction"/> определяет операции более высокого уровня, ''' базирующиеся на этих примитивах;</lu> ''' </li> ''' </remarks> MustInherit Class Implementor Public MustOverride Sub Operation() End Class ''' <summary> ''' RefinedAbstraction - уточненная абстракция ''' </summary> ''' <remarks> ''' <li> ''' <lu>расширяет интерфейс, определенный абстракцией <see cref="Abstraction"/></lu> ''' </li> ''' </remarks> Class RefinedAbstraction Inherits Abstraction Public Overrides Sub Operation() implementor.Operation() End Sub End Class ''' <summary> ''' ConcreteImplementor - конкретный реализатор ''' </summary> ''' <remarks> ''' <li> ''' <lu>содержит конкретную реализацию интерфейса <see cref="Implementor"/></lu> ''' </li> ''' </remarks> Class ConcreteImplementorA Inherits Implementor Public Overrides Sub Operation() Console.WriteLine("ConcreteImplementorA Operation") End Sub End Class ' "ConcreteImplementorB" Class ConcreteImplementorB Inherits Implementor Public Overrides Sub Operation() Console.WriteLine("ConcreteImplementorB Operation") End Sub End Class End Namespace
Литература
- Э. Гамма, Р. Хелм, Р. Джонсон, Дж. Влиссидес Приемы объектно-ориентированного проектирования. Паттерны проектирования = Design Patterns: Elements of Reusable Object-Oriented Software. — СПб: «Питер», 2007. — С. 366. — ISBN 978-5-469-01136-1 (также ISBN 5-272-00355-1)
Ссылки
- Паттерн Bridge (Мост) — назначение, описание, реализация (С++) и результаты применения
Шаблоны проектирования Основные Порождающие Структурные Адаптер • Выделение частного класса данных • Декоратор • Заместитель • Компоновщик • Мост • Приспособленец • Фасад
Поведенческие Интерпретатор • Итератор • Команда • Наблюдатель • Посетитель • Посредник • Состояние • Стратегия • Хранитель • Цепочка обязанностей • Шаблонный метод
Блокировка с двойной проверкой • Однопоточное выполнение • Планировщик Категории:- Шаблоны проектирования
- Структурные шаблоны проектирования
Wikimedia Foundation. 2010.