Образец моста - Bridge pattern
Модели моста является шаблон дизайна используется в программной инженерии , который предназначен для «отщепляется в абстракции от его реализации , так что эти два может изменяться независимо» , введенной бандой четыре . Мост использует инкапсуляцию , агрегацию , и может использовать наследование для разделения обязанностей в различные классы .
Когда класс часто меняется, в особенности объектно-ориентированное программирования становится очень полезной , потому что изменения в программу «s код может быть легко с минимальным предварительным знанием о программе. Шаблон моста полезен, когда и класс, и его действия часто меняются. Сам класс можно рассматривать как абстракцию, а то, что он может делать, как реализацию . Образец моста также можно рассматривать как два уровня абстракции.
Когда есть только одна фиксированная реализация, этот шаблон известен как идиома Pimpl в мире C ++ .
Шаблон моста часто путают с шаблоном адаптера и часто реализуют с использованием шаблона адаптера объекта ; например, в коде Java ниже.
Вариант: реализация может быть еще больше отделена, отложив присутствие реализации до точки, в которой используется абстракция.
Обзор
Шаблон проектирования Bridge - один из двадцати трех хорошо известных шаблонов проектирования GoF, которые описывают, как решать повторяющиеся проблемы проектирования для разработки гибкого и многократно используемого объектно-ориентированного программного обеспечения, то есть объектов, которые легче реализовать, изменить, протестировать и повторное использование.
Какие проблемы может решить шаблон проектирования Bridge?
- Абстракция и ее реализация должны определяться и расширяться независимо друг от друга.
- Следует избегать привязки во время компиляции между абстракцией и ее реализацией, чтобы реализацию можно было выбрать во время выполнения.
При использовании подклассов разные подклассы реализуют абстрактный класс по-разному. Но реализация привязана к абстракции во время компиляции и не может быть изменена во время выполнения.
Какое решение описывает шаблон проектирования Bridge?
- Отделите абстракцию (
Abstraction
) от ее реализации (Implementor
), поместив их в отдельные иерархии классов. - Реализуйте
Abstraction
в терминах (путем делегирования)Implementor
объекта.
Это позволяет настроить Abstraction
с Implementor
объектом во время выполнения.
См. Также класс Unified Modeling Language и диаграмму последовательности ниже.
Структура
Схема классов и последовательности UML
На приведенной выше диаграмме классов Unified Modeling Language абстракция ( Abstraction
) не реализована, как обычно, в единой иерархии наследования. Вместо этого существует одна иерархия для абстракции ( Abstraction
) и отдельная иерархия для ее реализации ( Implementor
), что делает их независимыми друг от друга. Abstraction
Интерфейс ( operation()
) реализован в терминах (путем делегирования) на Implementor
интерфейс ( imp.operationImp()
).
В UML - диаграмма последовательности
показывает время выполнения взаимодействия: The Abstraction1
реализации Делегаты объекта к Implementor1
объекту (путем вызова operationImp()
на Implementor1
), который выполняет операцию и возвращается Abstraction1
.
Диаграмма классов
- Абстракция (абстрактный класс)
- определяет абстрактный интерфейс
- поддерживает ссылку на исполнителя.
- RefinedAbstraction (обычный класс)
- расширяет интерфейс, определенный абстракцией
- Разработчик (интерфейс)
- определяет интерфейс для классов реализации
- ConcreteImplementor (обычный класс)
- реализует интерфейс Implementor
пример
C #
Образец моста объединяет объекты в древовидную структуру. Он отделяет абстракцию от реализации. Здесь абстракция представляет клиента, из которого будут вызываться объекты. Пример, реализованный на 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
C ++
#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) {
std::vector<CircleShape> shapes {
CircleShape{1.0f, 2.0f, 3.0f, DrawingAPI01{}},
CircleShape{5.0f, 7.0f, 11.0f, DrawingAPI02{}}
};
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
Scala
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()
}
}
}
Python
"""
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()
Смотрите также
Рекомендации
внешние ссылки
- Мост в UML и LePUS3 (формальный язык моделирования)
- Шаблоны проектирования C #: шаблон "Мост" . Образец главы . 2002-12-20. От: Джеймс У. Купер (2003). Шаблоны проектирования C #: Учебное пособие . Эддисон-Уэсли . ISBN 0-201-84453-2 .