Bro mønster - Bridge pattern
Den bro mønster er et design mønster bruges i software engineering , der er beregnet til at "frakoble en abstraktion fra dens implementering , så de to kan variere uafhængigt" , blev indført ved bande af fire . Den bro bruger indkapsling , aggregering , og kan bruge arv til separate ansvar i forskellige klasser .
Når en klasse varierer ofte, funktionerne i objektorienteret programmering blive meget nyttigt, fordi ændringer til et program ? S kode kan gøres nemt med minimal forudgående viden om programmet. Bromønsteret er nyttigt, når både klassen og hvad den gør, varierer ofte. Klassen selv kan betragtes som abstraktionen, og hvad klassen kan gøre som implementeringen . Bromønsteret kan også betragtes som to abstraktionslag.
Når der kun er en fast implementering, kaldes dette mønster som Pimpl- idiomet i C ++ - verdenen.
Bromønsteret forveksles ofte med adaptermønsteret og implementeres ofte ved hjælp af objektadaptermønsteret ; f.eks. i Java-koden nedenfor.
Variant: Implementeringen kan afkobles endnu mere ved at udsætte tilstedeværelsen af implementeringen til det punkt, hvor abstraktionen anvendes.
Oversigt
Bridge-designmønsteret er et af de 23 velkendte GoF-designmønstre, der beskriver, hvordan man løser tilbagevendende designproblemer for at designe fleksibel og genanvendelig objektorienteret software, dvs. objekter, der er lettere at implementere, ændre, teste og genbruge.
Hvilke problemer kan Bridge designmønsteret løse?
- En abstraktion og dens implementering skal defineres og udvides uafhængigt af hinanden.
- En kompileringstidsbinding mellem en abstraktion og dens implementering bør undgås, så en implementering kan vælges i løbetid.
Når du bruger underklasse, implementerer forskellige underklasser en abstrakt klasse på forskellige måder. Men en implementering er bundet til abstraktionen ved kompileringstid og kan ikke ændres i løbetid.
Hvilken løsning beskriver Bridge-designmønsteret?
- Adskil en abstraktion (
Abstraction
) fra dens implementering (Implementor
) ved at placere dem i separate klassehierarkier. - Implementere
Abstraction
i form af (ved at delegere til) etImplementor
objekt.
Dette gør det muligt at konfigurere en Abstraction
med et Implementor
objekt ved kørselstid.
Se også Unified Modelling Language- klassen og sekvensdiagrammet nedenfor.
Struktur
UML klasse og sekvensdiagram
I ovenstående Unified Modeling Language- klassediagram Abstraction
implementeres en abstraktion ( ) ikke som sædvanligt i et enkelt arvshierarki. I stedet er der et hierarki for en abstraktion ( Abstraction
) og et separat hierarki for dets implementering ( Implementor
), der gør de to uafhængige af hinanden. Den Abstraction
grænseflade ( operation()
) er implementeret i form af (ved at uddelegere til) Implementor
grænseflade ( imp.operationImp()
).
De UML sekvensdiagram
viser de runtime-interaktioner: Den Abstraction1
objekt delegerer gennemførelsen til Implementor1
objektet (ved at kalde operationImp()
den Implementor1
), som udfører operationen og vender tilbage til Abstraction1
.
Klassediagram
- Abstraktion (abstrakt klasse)
- definerer den abstrakte grænseflade
- fastholder implementeringsreferencen.
- Raffineret abstraktion (normal klasse)
- udvider grænsefladen defineret af Abstraktion
- Implementator (interface)
- definerer grænsefladen til implementeringsklasser
- ConcreteImplementor (normal klasse)
- implementerer Implementor-grænsefladen
Eksempel
C #
Bromønster komponerer objekter i træstruktur. Det afkobler abstraktion fra implementering. Her repræsenterer abstraktion klienten, hvorfra objekterne kaldes. Et eksempel implementeret i C # er givet nedenfor
// 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-klasser er implementeringen, der bruger den samme interfaceorienterede arkitektur til at skabe objekter. På den anden side tager abstraktionen en forekomst af implementeringsklassen og kører sin metode. Således er de fuldstændigt afkoblet fra hinanden.
Krystal
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
Produktion
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;
}
Produktion:
API01.circle at 1.000000:2.000000 - radius: 3.075000 API02.circle at 5.000000:7.000000 - radius: 11.275000
Java
Følgende Java- program definerer en bankkonto, der adskiller kontohandlingerne fra logning af disse operationer.
// 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);
}
}
Det vil sende:
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();
Produktion:
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()
Se også
Referencer
eksterne links
- Bridge i UML og i LePUS3 (et formelt modelleringssprog)
- C # Designmønstre: Bromønsteret . Prøve kapitel . 2002-12-20. Fra: James W. Cooper (2003). C # Design Patterns: A Tutorial . Addison-Wesley . ISBN 0-201-84453-2 .