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?
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.
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 ...
Wann:
A
/ \
Aa Ab
/ \ / \
Aa1 Aa2 Ab1 Ab2
Refactor zu:
A N
/ \ / \
Aa(N) Ab(N) 1 2
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
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.
Die Absicht von Bridge und Adapter ist unterschiedlich und wir brauchen beide Muster getrennt .
Brückenmuster:
Verwenden Sie das Brückenmuster, wenn:
@ 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:
Hauptunterschiede:
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:
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:
Vehicle
ist eine Abstraktion.Car
und Truck
sind zwei konkrete Implementierungen von Vehicle
.Vehicle
definiert eine abstrakte Methode: addGear()
.Gear
ist die ImplementierungsschnittstelleManualGear
und AutoGear
sind zwei Implementierungen von Gear
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.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 :
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.
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.
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.