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) et Implementor 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

Et eksempel på UML-klasse og sekvensdiagram for Bridge-designmønsteret.

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

Bridge UML klassediagram.svg

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