it-swarm.com.de

Wann verwenden Sie das Brückenmuster? Wie unterscheidet es sich vom Adaptermuster?

Hat jemand jemals das Brückenmuster in einer realen Anwendung verwendet? Wenn ja, wie haben Sie es benutzt? Bin ich es oder ist es nur das Adapter-Pattern mit einer kleinen Abhängigkeitsinjektion, die in die Mischung geworfen wird? Hat es wirklich ein eigenes Muster verdient?

143
Charles Graham

Ein klassisches Beispiel für das Brückenmuster wird bei der Definition von Formen in einer UI-Umgebung verwendet (siehe den Wikipedia-Eintrag Brückenmuster ). Das Brückenmuster ist ein zusammengesetztes aus dem Template und Strategiemuster .

Einige Aspekte des Adaptermusters im Bridge-Muster werden häufig angezeigt. Um jedoch aus diesem Artikel zu zitieren :

Auf den ersten Blick ähnelt das Bridge-Muster dem Adapter-Muster, da eine Klasse zum Konvertieren einer Art von Schnittstelle in eine andere verwendet wird. Mit dem Adaptermuster soll jedoch erreicht werden, dass die Schnittstellen einer oder mehrerer Klassen mit denen einer bestimmten Klasse identisch sind. Das Bridge-Muster dient dazu, die Schnittstelle einer Klasse von ihrer Implementierung zu trennen, sodass Sie die Implementierung variieren oder ersetzen können, ohne den Clientcode zu ändern.

76
shek

Es gibt eine Kombination von Federicos und Johns Antworten.

Wann:

                   ----Shape---
                  /            \
         Rectangle              Circle
        /         \            /      \
BlueRectangle  RedRectangle BlueCircle RedCircle

Refactor zu:

          ----Shape---                        Color
         /            \                       /   \
Rectangle(Color)   Circle(Color)           Blue   Red
235

Das Brückenmuster ist eine Anwendung des alten Ratschlags "Komposition gegenüber Vererbung bevorzugen". Es ist praktisch, wenn Sie verschiedene Zeiten orthogonal zueinander unterteilen müssen. Angenommen, Sie müssen eine Hierarchie farbiger Formen implementieren. Sie würden Shape nicht mit Rectangle und Circle unterordnen und dann Rectangle mit RedRectangle, BlueRectangle und GreenRectangle und das gleiche für Circle, oder? Sie würden bevorzugen zu sagen, dass jede Form hat eine Farbe und eine Hierarchie von Farben zu implementieren, und das ist das Brückenmuster. Nun, ich würde keine "Hierarchie der Farben" implementieren, aber Sie haben die Idee ...

223

Wann:

        A
     /     \
    Aa      Ab
   / \     /  \
 Aa1 Aa2  Ab1 Ab2

Refactor zu:

     A         N
  /     \     / \
Aa(N) Ab(N)  1   2
204
John Sonmez

Adapter und Bridge sind sicherlich verwandt, und die Unterscheidung ist subtil. Es ist wahrscheinlich, dass einige Leute, die glauben, eines dieser Muster zu verwenden, tatsächlich das andere Muster verwenden.

Die Erklärung, die ich gesehen habe, ist, dass Adapter verwendet wird, wenn Sie versuchen, die Schnittstellen einiger inkompatibler Klassen zu vereinheitlichen, die bereits existieren . Der Adapter fungiert als eine Art Übersetzer für Implementierungen, die als Legacy angesehen werden könnten.

Während das Bridge-Muster für Code verwendet wird, der eher auf der grünen Wiese ist. Sie entwerfen die Bridge als abstrakte Schnittstelle für eine Implementierung, die variiert werden muss, definieren jedoch auch die Schnittstelle dieser Implementierungsklassen.

Gerätetreiber sind ein häufig genanntes Beispiel für Bridge, aber ich würde sagen, es ist eine Bridge, wenn Sie die Schnittstellenspezifikation für Gerätehersteller definieren, aber es ist ein Adapter, wenn Sie vorhandene Gerätetreiber verwenden und eine Wrapper-Klasse erstellen Bereitstellung einer einheitlichen Schnittstelle.

In Bezug auf den Code sind die beiden Muster sehr ähnlich. In geschäftlicher Hinsicht sind sie anders.

Siehe auch http://c2.com/cgi/wiki?BridgePattern

29
Bill Karwin

Nach meiner Erfahrung ist Bridge ein häufig wiederkehrendes Muster, da es immer dann die Lösung ist, wenn es gibt zwei orthogonale Dimensionen in der Domäne. Z.B. Formen und Zeichenmethoden, Verhalten und Plattformen, Dateiformate und Serializer usw.

Und ein Tipp: Denken Sie immer an Entwurfsmuster aus der konzeptionellen Perspektive , nicht aus der Perspektive der Implementierung. Aus der richtigen Sicht kann Bridge nicht mit Adapter verwechselt werden, da sie ein anderes Problem lösen und die Komposition der Vererbung überlegen ist, nicht um ihrer selbst willen, sondern um orthogonale Probleme separat zu behandeln.

26
thSoft

Die Absicht von Bridge und Adapter ist unterschiedlich und wir brauchen beide Muster getrennt .

Brückenmuster:

  1. Es ist ein strukturelles Muster
  2. Abstraktion und Implementierung sind zur Kompilierungszeit nicht gebunden
  3. Abstraktion und Implementierung - beide können ohne Auswirkungen auf den Kunden variieren
  4. Verwendet Komposition über Vererbung.

Verwenden Sie das Brückenmuster, wenn:

  1. Sie möchten eine Laufzeitbindung der Implementierung,
  2. Sie haben eine Vielzahl von Klassen, die sich aus einer gekoppelten Schnittstelle und zahlreichen Implementierungen ergeben.
  3. Sie möchten eine Implementierung für mehrere Objekte freigeben,
  4. Sie müssen orthogonale Klassenhierarchien zuordnen.

@ John Sonmez Antwort zeigt deutlich die Wirksamkeit des Brückenmusters bei der Reduzierung der Klassenhierarchie.

Sie können sich auf den folgenden Dokumentationslink beziehen, um einen besseren Einblick in das Brückenmuster mit einem Codebeispiel zu erhalten

Adaptermuster:

  1. Es ermöglicht das Zusammenwirken von zwei nicht zusammenhängenden Schnittstellen durch die verschiedenen Objekte, die möglicherweise dieselbe Rolle spielen.
  2. Es ändert die ursprüngliche Schnittstelle.

Hauptunterschiede:

  1. Adapter lässt Dinge funktionieren, nachdem sie entworfen wurden; Bridge lässt sie arbeiten, bevor sie sind.
  2. Bridge ist von vornherein so konzipiert, dass die Abstraktion und die Implementierung unabhängig voneinander variieren können. Adapter wird nachgerüstet, damit nicht verwandte Klassen zusammenarbeiten.
  3. Die Absicht: Adapter ermöglicht die Zusammenarbeit von zwei unabhängigen Schnittstellen. Bridge ermöglicht es, Abstraktion und Implementierung unabhängig voneinander zu variieren.

Verwandte SE-Frage mit UML-Diagramm und Arbeitscode:

nterschied zwischen Brückenmuster und Adaptermuster

Nützliche Artikel:

Sourcemaking Bridge Musterartikel

Sourcemaking Adapter Musterartikel

journaldev bridge Musterartikel

EDIT:

Beispiel für ein reales Bridge-Muster (gemäß meta.stackoverflow.com-Vorschlag, Beispiel für eine Dokumentationssite in diesem Beitrag, da die Dokumentation zu Sun-set geht)

Das Brückenmuster entkoppelt die Abstraktion von der Implementierung, sodass beide unabhängig voneinander variieren können. Es wurde eher mit Komposition als mit Vererbung erreicht.

Brückenmuster-UML aus Wikipedia:

Bridge pattern UML from Wikipedia

Sie haben vier Komponenten in diesem Muster.

Abstraction: Definiert eine Schnittstelle

RefinedAbstraction: Es implementiert die Abstraktion:

Implementor: Definiert eine Schnittstelle für die Implementierung

ConcreteImplementor: Implementiert die Implementierungsschnittstelle.

The crux of Bridge pattern : Zwei orthogonale Klassenhierarchien mit Komposition (und ohne Vererbung). Die Abstraktionshierarchie und die Implementierungshierarchie können unabhängig voneinander variieren. Implementierung verweist nie auf Abstraktion. Abstraktion enthält Implementierungsschnittstelle als Mitglied (durch Komposition). Diese Komposition reduziert eine weitere Ebene der Vererbungshierarchie.

Real Word Use case:

Verschiedene Fahrzeuge für beide Versionen des manuellen und des automatischen Getriebes aktivieren.

Beispielcode:

/* Implementor interface*/
interface Gear{
    void handleGear();
}

/* Concrete Implementor - 1 */
class ManualGear implements Gear{
    public void handleGear(){
        System.out.println("Manual gear");
    }
}
/* Concrete Implementor - 2 */
class AutoGear implements Gear{
    public void handleGear(){
        System.out.println("Auto gear");
    }
}
/* Abstraction (abstract class) */
abstract class Vehicle {
    Gear gear;
    public Vehicle(Gear gear){
        this.gear = gear;
    }
    abstract void addGear();
}
/* RefinedAbstraction - 1*/
class Car extends Vehicle{
    public Car(Gear gear){
        super(gear);
        // initialize various other Car components to make the car
    }
    public void addGear(){
        System.out.print("Car handles ");
        gear.handleGear();
    }
}
/* RefinedAbstraction - 2 */
class Truck extends Vehicle{
    public Truck(Gear gear){
        super(gear);
        // initialize various other Truck components to make the car
    }
    public void addGear(){
        System.out.print("Truck handles " );
        gear.handleGear();
    }
}
/* Client program */
public class BridgeDemo {    
    public static void main(String args[]){
        Gear gear = new ManualGear();
        Vehicle vehicle = new Car(gear);
        vehicle.addGear();

        gear = new AutoGear();
        vehicle = new Car(gear);
        vehicle.addGear();

        gear = new ManualGear();
        vehicle = new Truck(gear);
        vehicle.addGear();

        gear = new AutoGear();
        vehicle = new Truck(gear);
        vehicle.addGear();
    }
}

ausgabe:

Car handles Manual gear
Car handles Auto gear
Truck handles Manual gear
Truck handles Auto gear

Erläuterung:

  1. Vehicle ist eine Abstraktion.
  2. Car und Truck sind zwei konkrete Implementierungen von Vehicle.
  3. Vehicle definiert eine abstrakte Methode: addGear().
  4. Gear ist die Implementierungsschnittstelle
  5. ManualGear und AutoGear sind zwei Implementierungen von Gear
  6. Vehicle enthält die Schnittstelle implementor, anstatt die Schnittstelle zu implementieren. Compositon der Implementierungsschnittstelle ist der Kern dieses Musters: Abstraktion und Implementierung können unabhängig voneinander variieren.
  7. Car und Truck definieren die Implementierung (neu definierte Abstraktion) für die Abstraktion: addGear(): Sie enthält Gear - Entweder Manual oder Auto

Anwendungsfall (e) für das Brückenmuster :

  1. Abstraktion und Implementierung können sich unabhängig voneinander ändern und sind nicht an sie gebunden Kompilierzeit
  2. Ordnen Sie orthogonale Hierarchien zu - eine für Abstraktion und eine für Implementierung.
20
Ravindra babu

Ich habe das Brückenmuster bei der Arbeit verwendet. Ich programmiere in C++, wo es oft als PIMPL-Idiom (Zeiger auf Implementierung) bezeichnet wird. Es sieht aus wie das:

class A
{
public: 
  void foo()
  {
    pImpl->foo();
  }
private:
  Aimpl *pImpl;
};

class Aimpl
{
public:
  void foo();
  void bar();
};  

In diesem Beispiel enthält class A Die Schnittstelle und class Aimpl Die Implementierung.

Eine Verwendung für dieses Muster besteht darin, nur einige der öffentlichen Mitglieder der Implementierungsklasse verfügbar zu machen, andere jedoch nicht. Im Beispiel kann nur Aimpl::foo() über die öffentliche Schnittstelle von A aufgerufen werden, nicht jedoch Aimpl::bar()

Ein weiterer Vorteil ist, dass Sie Aimpl in einer separaten Header-Datei definieren können, die von den Benutzern von A nicht berücksichtigt werden muss. Sie müssen lediglich eine Forward-Deklaration von Aimpl verwenden, bevor A definiert wird, und die Definitionen aller Memberfunktionen, die auf pImpl verweisen, in die CPP-Datei verschieben. Dies gibt Ihnen die Möglichkeit, den Header Aimpl privat zu halten und die Kompilierungszeit zu verkürzen.

9
Dima

So fügen Sie ein Formbeispiel in den Code ein:

#include<iostream>
#include<string>
#include<cstdlib>

using namespace std;

class IColor
{
public:
    virtual string Color() = 0;
};

class RedColor: public IColor
{
public:
    string Color()
    {
        return "of Red Color";
    }
};

class BlueColor: public IColor
{
public:
    string Color()
    {
        return "of Blue Color";
    }
};


class IShape
{
public:
virtual string Draw() = 0;
};

class Circle: public IShape
{
        IColor* impl;
    public:
        Circle(IColor *obj):impl(obj){}
        string Draw()
        {
            return "Drawn a Circle "+ impl->Color();
        }
};

class Square: public IShape
{
        IColor* impl;
    public:
        Square(IColor *obj):impl(obj){}
        string Draw()
        {
        return "Drawn a Square "+ impl->Color();;
        }
};

int main()
{
IColor* red = new RedColor();
IColor* blue = new BlueColor();

IShape* sq = new Square(red);
IShape* cr = new Circle(blue);

cout<<"\n"<<sq->Draw();
cout<<"\n"<<cr->Draw();

delete red;
delete blue;
return 1;
}

Die Ausgabe ist:

Drawn a Square of Red Color
Drawn a Circle of Blue Color

Beachten Sie die Leichtigkeit, mit der dem System neue Farben und Formen hinzugefügt werden können, ohne dass es aufgrund von Permutationen zu einer Explosion von Unterklassen kommt.

6
NotAgain

für mich ist es ein Mechanismus, mit dem man Schnittstellen austauschen kann. In der realen Welt gibt es möglicherweise eine Klasse, die mehr als eine Schnittstelle verwenden kann. Bridge lässt Sie tauschen.

0
j2emanue