Узор моста
Шаблон моста — это шаблон проектирования , используемый в разработке программного обеспечения , который предназначен для «отделения абстракции от ее реализации , чтобы они могли изменяться независимо» , представленный «Бандой четырех» . [1] Мост разные использует инкапсуляцию , агрегацию и может использовать наследование для разделения обязанностей на классы .
Когда класс часто меняется, возможности объектно-ориентированного программирования становятся очень полезными, поскольку изменения в можно коде программы легко внести с минимальными предварительными знаниями о программе. Шаблон моста полезен, когда и класс, и то, что он делает, часто меняются. Сам класс можно рассматривать как абстракцию , а то, что класс может делать, как реализацию . Шаблон моста также можно рассматривать как два уровня абстракции.
Когда существует только одна фиксированная реализация, этот шаблон известен как идиома Pimpl в мире C++ .
Шаблон моста часто путают с шаблоном адаптера , и он часто реализуется с использованием шаблона адаптера объекта ; например, в коде Java ниже.
Вариант: реализацию можно еще больше отделить, отложив присутствие реализации до момента использования абстракции.
Обзор
[ редактировать ]Шаблон проектирования Bridge — один из двадцати трех известных шаблонов проектирования GoF , которые описывают, как решать повторяющиеся проблемы проектирования для разработки гибкого и многократно используемого объектно-ориентированного программного обеспечения, то есть объектов, которые легче реализовать, изменить, протестировать и повторное использование. [1]
Какие проблемы может решить шаблон проектирования Bridge? [2]
- Абстракция и ее реализация должны определяться и расширяться независимо друг от друга.
- Следует избегать привязки во время компиляции между абстракцией и ее реализацией, чтобы реализацию можно было выбрать во время выполнения.
При использовании подклассов разные подклассы реализуют абстрактный класс по-разному. Но реализация привязана к абстракции во время компиляции и не может быть изменена во время выполнения.
Какое решение описывает шаблон проектирования Bridge?
- Отделить абстракцию (
Abstraction
) от его реализации (Implementor
), поместив их в отдельные иерархии классов. - Внедрить
Abstraction
с точки зрения (путем делегирования)Implementor
объект.
Это позволяет настроить Abstraction
с Implementor
объект во время выполнения.
См. также класс Unified Modeling Language и диаграмму последовательности ниже.
Структура
[ редактировать ]Класс UML и диаграмма последовательности
[ редактировать ]
В приведенной выше диаграмме классов Unified Modeling Language абстракция ( Abstraction
) не реализуется, как обычно, в единой иерархии наследования.
Вместо этого существует одна иерархия для
абстракция ( Abstraction
) и отдельную иерархию для ее реализации ( Implementor
), что делает их независимыми друг от друга.
Abstraction
интерфейс ( operation()
) реализуется посредством (путем делегирования)
тот Implementor
интерфейс ( imp.operationImp()
).
Диаграмма UML последовательности
показывает взаимодействие во время выполнения: Abstraction1
объект делегирует реализацию Implementor1
объект (путем вызова operationImp()
на Implementor1
),
который выполняет операцию и возвращается в Abstraction1
.
Диаграмма классов
[ редактировать ]- Абстракция (абстрактный класс)
- определяет абстрактный интерфейс
- поддерживает ссылку на реализацию.
- RefinedAbstraction (обычный класс)
- расширяет интерфейс, определенный абстракцией
- Реализация (интерфейс)
- определяет интерфейс для классов реализации
- ConcreteImplementor (обычный класс)
- реализует интерфейс Implementor

Пример
[ редактировать ]С#
[ редактировать ]Шаблон моста объединяет объекты в древовидную структуру. Он отделяет абстракцию от реализации. Здесь абстракция представляет клиента, из которого будут вызываться объекты. Ниже приведен пример, реализованный на C#.
// Helps in providing truly decoupled architecture
public interface IBridge
{
void Function1();
void Function2();
}
public class Bridge1 : IBridge
{
public void Function1()
{
Console.WriteLine("Bridge1.Function1");
}
public void Function2()
{
Console.WriteLine("Bridge1.Function2");
}
}
public class Bridge2 : IBridge
{
public void Function1()
{
Console.WriteLine("Bridge2.Function1");
}
public void Function2()
{
Console.WriteLine("Bridge2.Function2");
}
}
public interface IAbstractBridge
{
void CallMethod1();
void CallMethod2();
}
public class AbstractBridge : IAbstractBridge
{
public IBridge bridge;
public AbstractBridge(IBridge bridge)
{
this.bridge = bridge;
}
public void CallMethod1()
{
this.bridge.Function1();
}
public void CallMethod2()
{
this.bridge.Function2();
}
}
Классы Bridge — это реализация, которая использует ту же интерфейсно-ориентированную архитектуру для создания объектов. С другой стороны, абстракция берет экземпляр класса реализации и запускает его метод. Таким образом, они полностью отделены друг от друга.
Кристалл
[ редактировать ]abstract class DrawingAPI
abstract def draw_circle(x : Float64, y : Float64, radius : Float64)
end
class DrawingAPI1 < DrawingAPI
def draw_circle(x : Float, y : Float, radius : Float)
"API1.circle at #{x}:#{y} - radius: #{radius}"
end
end
class DrawingAPI2 < DrawingAPI
def draw_circle(x : Float64, y : Float64, radius : Float64)
"API2.circle at #{x}:#{y} - radius: #{radius}"
end
end
abstract class Shape
protected getter drawing_api : DrawingAPI
def initialize(@drawing_api)
end
abstract def draw
abstract def resize_by_percentage(percent : Float64)
end
class CircleShape < Shape
getter x : Float64
getter y : Float64
getter radius : Float64
def initialize(@x, @y, @radius, drawing_api : DrawingAPI)
super(drawing_api)
end
def draw
@drawing_api.draw_circle(@x, @y, @radius)
end
def resize_by_percentage(percent : Float64)
@radius *= (1 + percent/100)
end
end
class BridgePattern
def self.test
shapes = [] of Shape
shapes << CircleShape.new(1.0, 2.0, 3.0, DrawingAPI1.new)
shapes << CircleShape.new(5.0, 7.0, 11.0, DrawingAPI2.new)
shapes.each do |shape|
shape.resize_by_percentage(2.5)
puts shape.draw
end
end
end
BridgePattern.test
Выход
API1.circle at 1.0:2.0 - radius: 3.075 API2.circle at 5.0:7.0 - radius: 11.275
С++
[ редактировать ]#include <iostream>
#include <string>
#include <vector>
class DrawingAPI {
public:
virtual ~DrawingAPI() = default;
virtual std::string DrawCircle(float x, float y, float radius) const = 0;
};
class DrawingAPI01 : public DrawingAPI {
public:
std::string DrawCircle(float x, float y, float radius) const override {
return "API01.circle at " + std::to_string(x) + ":" + std::to_string(y) +
" - radius: " + std::to_string(radius);
}
};
class DrawingAPI02 : public DrawingAPI {
public:
std::string DrawCircle(float x, float y, float radius) const override {
return "API02.circle at " + std::to_string(x) + ":" + std::to_string(y) +
" - radius: " + std::to_string(radius);
}
};
class Shape {
public:
Shape(const DrawingAPI& drawing_api) : drawing_api_(drawing_api) {}
virtual ~Shape() = default;
virtual std::string Draw() const = 0;
virtual float ResizeByPercentage(const float percent) = 0;
protected:
const DrawingAPI& drawing_api_;
};
class CircleShape: public Shape {
public:
CircleShape(float x, float y, float radius, const DrawingAPI& drawing_api)
: Shape(drawing_api), x_(x), y_(y), radius_(radius) {}
std::string Draw() const override {
return drawing_api_.DrawCircle(x_, y_, radius_);
}
float ResizeByPercentage(const float percent) override {
return radius_ *= (1.0f + percent/100.0f);
}
private:
float x_, y_, radius_;
};
int main(int argc, char** argv) {
const DrawingAPI01 api1{};
const DrawingAPI02 api2{};
std::vector<CircleShape> shapes {
CircleShape{1.0f, 2.0f, 3.0f, api1},
CircleShape{5.0f, 7.0f, 11.0f, api2}
};
for (auto& shape: shapes) {
shape.ResizeByPercentage(2.5);
std::cout << shape.Draw() << std::endl;
}
return 0;
}
Выход:
API01.circle at 1.000000:2.000000 - radius: 3.075000 API02.circle at 5.000000:7.000000 - radius: 11.275000
Ява
[ редактировать ]Следующая программа Java определяет банковский счет, который отделяет операции со счетом от регистрации этих операций.
// Logger has two implementations: info and warning
@FunctionalInterface
interface Logger {
void log(String message);
static Logger info() {
return message -> System.out.println("info: " + message);
}
static Logger warning() {
return message -> System.out.println("warning: " + message);
}
}
abstract class AbstractAccount {
private Logger logger = Logger.info();
public void setLogger(Logger logger) {
this.logger = logger;
}
// the logging part is delegated to the Logger implementation
protected void operate(String message, boolean result) {
logger.log(message + " result " + result);
}
}
class SimpleAccount extends AbstractAccount {
private int balance;
public SimpleAccount(int balance) {
this.balance = balance;
}
public boolean isBalanceLow() {
return balance < 50;
}
public void withdraw(int amount) {
boolean shouldPerform = balance >= amount;
if (shouldPerform) {
balance -= amount;
}
operate("withdraw " + amount, shouldPerform);
}
}
public class BridgeDemo {
public static void main(String[] args) {
SimpleAccount account = new SimpleAccount(100);
account.withdraw(75);
if (account.isBalanceLow()) {
// you can also change the Logger implementation at runtime
account.setLogger(Logger.warning());
}
account.withdraw(10);
account.withdraw(100);
}
}
Он выведет:
info: withdraw 75 result true warning: withdraw 10 result true warning: withdraw 100 result false
PHP
[ редактировать ]interface DrawingAPI
{
function drawCircle($x, $y, $radius);
}
class DrawingAPI1 implements DrawingAPI
{
public function drawCircle($x, $y, $radius)
{
echo "API1.circle at $x:$y radius $radius.\n";
}
}
class DrawingAPI2 implements DrawingAPI
{
public function drawCircle($x, $y, $radius)
{
echo "API2.circle at $x:$y radius $radius.\n";
}
}
abstract class Shape
{
protected $drawingAPI;
public abstract function draw();
public abstract function resizeByPercentage($pct);
protected function __construct(DrawingAPI $drawingAPI)
{
$this->drawingAPI = $drawingAPI;
}
}
class CircleShape extends Shape
{
private $x;
private $y;
private $radius;
public function __construct($x, $y, $radius, DrawingAPI $drawingAPI)
{
parent::__construct($drawingAPI);
$this->x = $x;
$this->y = $y;
$this->radius = $radius;
}
public function draw()
{
$this->drawingAPI->drawCircle($this->x, $this->y, $this->radius);
}
public function resizeByPercentage($pct)
{
$this->radius *= $pct;
}
}
class Tester
{
public static function main()
{
$shapes = array(
new CircleShape(1, 3, 7, new DrawingAPI1()),
new CircleShape(5, 7, 11, new DrawingAPI2()),
);
foreach ($shapes as $shape) {
$shape->resizeByPercentage(2.5);
$shape->draw();
}
}
}
Tester::main();
Выход:
API1.circle at 1:3 radius 17.5 API2.circle at 5:7 radius 27.5
Скала
[ редактировать ]trait DrawingAPI {
def drawCircle(x: Double, y: Double, radius: Double)
}
class DrawingAPI1 extends DrawingAPI {
def drawCircle(x: Double, y: Double, radius: Double) = println(s"API #1 $x $y $radius")
}
class DrawingAPI2 extends DrawingAPI {
def drawCircle(x: Double, y: Double, radius: Double) = println(s"API #2 $x $y $radius")
}
abstract class Shape(drawingAPI: DrawingAPI) {
def draw()
def resizePercentage(pct: Double)
}
class CircleShape(x: Double, y: Double, var radius: Double, drawingAPI: DrawingAPI)
extends Shape(drawingAPI: DrawingAPI) {
def draw() = drawingAPI.drawCircle(x, y, radius)
def resizePercentage(pct: Double) { radius *= pct }
}
object BridgePattern {
def main(args: Array[String]) {
Seq (
new CircleShape(1, 3, 5, new DrawingAPI1),
new CircleShape(4, 5, 6, new DrawingAPI2)
) foreach { x =>
x.resizePercentage(3)
x.draw()
}
}
}
Питон
[ редактировать ]"""
Bridge pattern example.
"""
from abc import ABCMeta, abstractmethod
NOT_IMPLEMENTED = "You should implement this."
class DrawingAPI:
__metaclass__ = ABCMeta
@abstractmethod
def draw_circle(self, x, y, radius):
raise NotImplementedError(NOT_IMPLEMENTED)
class DrawingAPI1(DrawingAPI):
def draw_circle(self, x, y, radius):
return f"API1.circle at {x}:{y} - radius: {radius}"
class DrawingAPI2(DrawingAPI):
def draw_circle(self, x, y, radius):
return f"API2.circle at {x}:{y} - radius: {radius}"
class DrawingAPI3(DrawingAPI):
def draw_circle(self, x, y, radius):
return f"API3.circle at {x}:{y} - radius: {radius}"
class Shape:
__metaclass__ = ABCMeta
drawing_api = None
def __init__(self, drawing_api):
self.drawing_api = drawing_api
@abstractmethod
def draw(self):
raise NotImplementedError(NOT_IMPLEMENTED)
@abstractmethod
def resize_by_percentage(self, percent):
raise NotImplementedError(NOT_IMPLEMENTED)
class CircleShape(Shape):
def __init__(self, x, y, radius, drawing_api):
self.x = x
self.y = y
self.radius = radius
super(CircleShape, self).__init__(drawing_api)
def draw(self):
return self.drawing_api.draw_circle(self.x, self.y, self.radius)
def resize_by_percentage(self, percent):
self.radius *= 1 + percent / 100
class BridgePattern:
@staticmethod
def test():
shapes = [
CircleShape(1.0, 2.0, 3.0, DrawingAPI1()),
CircleShape(5.0, 7.0, 11.0, DrawingAPI2()),
CircleShape(5.0, 4.0, 12.0, DrawingAPI3()),
]
for shape in shapes:
shape.resize_by_percentage(2.5)
print(shape.draw())
BridgePattern.test()
См. также
[ редактировать ]Ссылки
[ редактировать ]- ^ Jump up to: а б Гамма, Эрих ; Хелм, Ричард; Джонсон, Ральф ; Влиссидес, Джон (1994). Шаблоны проектирования: элементы объектно-ориентированного программного обеспечения многократного использования . Аддисон-Уэсли. п. 151 . ISBN 0-201-63361-2 .
- ^ «Шаблон проектирования моста — проблема, решение и применимость» . w3sDesign.com . Проверено 12 августа 2017 г.
- ^ «Шаблон проектирования моста — структура и сотрудничество» . w3sDesign.com . Проверено 12 августа 2017 г.
Внешние ссылки
[ редактировать ]
- Мост в UML и LePUS3 (формальный язык моделирования).
- Шаблоны проектирования C#: шаблон моста . 20 декабря 2002 г.
{{cite book}}
:|work=
игнорируется ( помощь ) От: Джеймс В. Купер (2003). Шаблоны проектирования C#: Учебное пособие . Аддисон-Уэсли . ISBN 0-201-84453-2 .