Was ist der Unterschied zwischen den Bridge- und Adaptermustern?
Was ist der Unterschied zwischen den Bridge- und Adaptermustern?
Antworten:
"Mit dem Adapter funktionieren die Dinge, nachdem sie entworfen wurden. Mit Bridge funktionieren sie, bevor sie funktionieren. [GoF, S. 219]"
Tatsächlich ist das Adaptermuster nützlich, wenn Sie über vorhandenen Code verfügen, sei es von Drittanbietern oder intern, aber außerhalb Ihrer Kontrolle oder auf andere Weise nicht änderbar, um die gewünschte Schnittstelle zu erfüllen. Zum Beispiel haben wir ein SuperWeaponsArray, das eine feine Reihe von Doomsday-Geräten steuern kann.
public class SuperWeaponsArray {
/*...*/
public void destroyWorld() {
for (Weapon w : armedWeapons) {
w.fire();
}
}
}
Toll. Außer wir erkennen, dass wir ein nukleares Gerät in unserem Arsenal haben, das weit vor der Umstellung auf die Waffenschnittstelle liegt. Aber wir möchten wirklich, dass es hier funktioniert ... also was machen wir ... keilen Sie es ein!
NukeWeaponsAdaptor - basiert auf unserer Nuke-Klasse, exportiert jedoch die Waffenschnittstelle. Süß, jetzt können wir sicher die Welt zerstören. Es scheint ein bisschen kludge zu sein, aber es bringt die Dinge zum Laufen.
Das Bridge- Muster ist etwas, das Sie im Voraus implementieren. Wenn Sie wissen, dass Sie zwei orthogonale Hierarchien haben, können Sie die Schnittstelle und die Implementierung so entkoppeln, dass Sie nicht eine verrückte Anzahl von Klassen erhalten. Angenommen, Sie haben:
MemoryMappedFile- und DirectReadFile-Typen von Dateiobjekten. Angenommen, Sie möchten Dateien aus verschiedenen Quellen lesen können (möglicherweise Linux- oder Windows-Implementierungen usw.). Bridge hilft Ihnen dabei, nicht aufzuwickeln mit:
MemoryMappedWindowsFile MemoryMappedLinuxFile DirectReadWindowsFile DirectReadLinuxFile
http://en.wikipedia.org/wiki/Adapter_pattern
Im Adaptermuster geht es mehr darum, Ihren vorhandenen Code mit einem neueren System oder einer neueren Schnittstelle zum Laufen zu bringen.
Wenn Sie über eine Reihe von Webdienst-APIs nach Unternehmensstandard verfügen, die Sie für die vorhandene Erweiterungsschnittstelle einer anderen Anwendung anbieten möchten, können Sie eine Reihe von Adaptern schreiben, um dies zu tun. Beachten Sie, dass es eine Grauzone gibt und hier mehr darüber, wie Sie das Muster technisch definieren, da andere Muster wie die Fassade ähnlich sind.
http://en.wikipedia.org/wiki/Bridge_pattern
Das Bridge-Muster ermöglicht Ihnen möglicherweise alternative Implementierungen eines Algorithmus oder Systems.
Obwohl dies kein klassisches Beispiel für ein Bridge-Muster ist, stellen Sie sich vor, Sie hätten einige Implementierungen eines Datenspeichers: Einer ist platzsparend, der andere effizient in Bezug auf die Rohleistung ... und Sie haben ein Geschäftsmodell, um sowohl in Ihrer App als auch in Ihrem Framework anzubieten .
In Bezug auf Ihre Frage "Wo kann ich welches Muster verwenden?" Lautet die Antwort, wo immer es für Ihr Projekt sinnvoll ist! Vielleicht sollten Sie eine Klarstellungsbearbeitung anbieten, um die Diskussion darüber zu leiten, wo Sie glauben, dass Sie die eine oder andere verwenden müssen.
Adapter:
UML-Diagramm: aus dem Dofactory- Artikel:
Ziel : Definiert die domänenspezifische Schnittstelle, die der Client verwendet.
Adapter : Passt die Schnittstelle Adaptee an die Zielschnittstelle an.
Adaptee : Definiert eine vorhandene Schnittstelle, die angepasst werden muss.
Client : arbeitet mit Objekten zusammen, die der Zielschnittstelle entsprechen.
Beispiel:
Quadrat und Rechteck sind zwei verschiedene Formen, und das Abrufen der Fläche () von jeder von ihnen erfordert unterschiedliche Methoden. Trotzdem arbeitet Square an der Rechteck-Oberfläche mit der Konvertierung einiger Eigenschaften.
public class AdapterDemo{
public static void main(String args[]){
SquareArea s = new SquareArea(4);
System.out.println("Square area :"+s.getArea());
}
}
class RectangleArea {
public int getArea(int length, int width){
return length * width;
}
}
class SquareArea extends RectangleArea {
int length;
public SquareArea(int length){
this.length = length;
}
public int getArea(){
return getArea(length,length);
}
}
Brücke:
BEARBEITEN: (gemäß @ quasoft Vorschlag)
Sie haben vier Komponenten in diesem Muster.
Abstraktion : Definiert eine Schnittstelle
RefinedAbstraction : Es implementiert die Abstraktion:
Implementierer : Definiert eine Schnittstelle für die Implementierung
ConcreteImplementor : Implementiert die Implementiererschnittstelle.
Code-Auszug:
Gear gear = new ManualGear();
Vehicle vehicle = new Car(gear);
vehicle.addGear();
gear = new AutoGear();
vehicle = new Car(gear);
vehicle.addGear();
In Verbindung stehender Beitrag:
Wann verwenden Sie das Brückenmuster? Wie unterscheidet es sich vom Adaptermuster?
Hauptunterschiede: vom Artikel zur Quellenherstellung
Dieser Beitrag gibt es schon eine ganze Weile. Es ist jedoch wichtig zu verstehen, dass eine Fassade einem Adapter etwas ähnlich ist, aber nicht ganz dasselbe. Ein Adapter "passt" eine vorhandene Klasse an eine normalerweise nicht kompatible Clientklasse an. Angenommen, Sie haben ein altes Workflow-System, das Ihre Anwendung als Client verwendet. Ihr Unternehmen könnte möglicherweise das Workflow-System durch ein neues "inkompatibles" System (in Bezug auf Schnittstellen) ersetzen. In den meisten Fällen können Sie das Adaptermuster verwenden und Code schreiben, der die Schnittstellen der neuen Workflow-Engine tatsächlich aufruft. Eine Brücke wird im Allgemeinen anders verwendet. Wenn Sie tatsächlich ein System haben, das mit verschiedenen Dateisystemen (z. B. lokaler Festplatte, NFS usw.) arbeiten muss, können Sie das Brückenmuster verwenden und eine Abstraktionsschicht erstellen, um mit allen Ihren Dateisystemen zu arbeiten. Dies wäre im Grunde ein einfacher Anwendungsfall für das Brückenmuster. Die Fassade und der Adapter haben jedoch einige Eigenschaften gemeinsamFassaden werden normalerweise verwendet, um eine vorhandene Schnittstelle / Klasse zu vereinfachen . In den frühen Tagen der EJBs gab es keine lokalen Anrufe für EJBs. Entwickler haben den Stub immer erhalten, eingegrenzt und als "Pseudo-Remote" bezeichnet. Dies verursachte häufig Leistungsprobleme (insbesondere, wenn wirklich über das Kabel gerufen wurde). Erfahrene Entwickler würden das Fassadenmuster verwenden, um dem Kunden eine sehr grobkörnige Schnittstelle bereitzustellen. Diese Fassade würde dann wiederum mehrere Aufrufe an verschiedene feinkörnigere Methoden ausführen. Insgesamt wurde dadurch die Anzahl der erforderlichen Methodenaufrufe erheblich reduziert und die Leistung gesteigert.
Brücke ist Adapter verbessert. Bridge enthält einen Adapter und bietet zusätzliche Flexibilität. So werden Elemente aus Ravindras Antwortkarte zwischen Mustern dargestellt:
Adapter | Bridge
-----------|---------------
Target | Abstraction
-----------|---------------
| RefinedAbstraction
|
| This element is Bridge specific. If there is a group of
| implementations that share the same logic, the logic can be placed here.
| For example, all cars split into two large groups: manual and auto.
| So, there will be two RefinedAbstraction classes.
-----------|---------------
Adapter | Implementor
-----------|---------------
Adaptee | ConcreteImplementor
In der oberen Antwort zitiert @James einen Satz aus dem GoF, Seite 219. Ich denke, es lohnt sich, die vollständige Erklärung hier wiederzugeben.
Adapter gegen Brücke
Die Adapter- und Bridge-Muster haben einige gemeinsame Attribute. Beide fördern die Flexibilität, indem sie eine Indirektionsebene für ein anderes Objekt bereitstellen. In beiden Fällen werden Anforderungen von einer anderen als der eigenen Schnittstelle an dieses Objekt weitergeleitet.
Der Hauptunterschied zwischen diesen Mustern liegt in ihren Absichten. Der Adapter konzentriert sich auf die Behebung von Inkompatibilitäten zwischen zwei vorhandenen Schnittstellen. Es wird weder darauf eingegangen, wie diese Schnittstellen implementiert werden, noch wird berücksichtigt, wie sie sich unabhängig voneinander entwickeln könnten. Auf diese Weise können zwei unabhängig gestaltete Klassen zusammenarbeiten, ohne die eine oder andere neu zu implementieren. Bridge hingegen überbrückt eine Abstraktion und ihre (möglicherweise zahlreichen) Implementierungen. Es bietet Clients eine stabile Schnittstelle, auch wenn Sie die Klassen variieren können, die es implementieren. Es ermöglicht auch neue Implementierungen, wenn sich das System weiterentwickelt.
Aufgrund dieser Unterschiede werden Adapter und Bridge häufig an verschiedenen Punkten im Software-Lebenszyklus verwendet. Ein Adapter wird häufig erforderlich, wenn Sie feststellen, dass zwei inkompatible Klassen zusammenarbeiten sollten, um das Replizieren von Code zu vermeiden. Die Kupplung ist unvorhergesehen. Im Gegensatz dazu versteht der Benutzer einer Brücke im Voraus, dass eine Abstraktion mehrere Implementierungen haben muss und beide sich unabhängig voneinander entwickeln können. Mit dem Adaptermuster funktionieren die Dinge nach dem Entwurf. Bridge lässt sie arbeiten, bevor sie es sind. Das bedeutet nicht, dass der Adapter Bridge irgendwie unterlegen ist. Jedes Muster spricht lediglich ein anderes Problem an.
Angenommen, Sie haben eine abstrakte Formklasse mit einer (generischen / abstrakten) Zeichenfunktion und einen Kreis, der die Form implementiert. Das Brückenmuster ist einfach ein bidirektionaler Abstraktionsansatz, um die Implementierung (Zeichnen in Kreis) und die generische / abstrahierte Funktionalität (Zeichnen in der Shape-Klasse) zu entkoppeln.
Was bedeutet das wirklich? Auf den ersten Blick klingt es wie etwas, das Sie bereits gemacht haben (durch Abhängigkeitsinversion). Machen Sie sich also keine Sorgen um eine weniger robuste oder modularere Codebasis. Aber es steckt eine etwas tiefere Philosophie dahinter.
Nach meinem Verständnis kann die Notwendigkeit eines Verwendungsmusters auftreten, wenn ich neue Klassen hinzufügen muss, die eng mit dem aktuellen System verwandt sind (wie RedCircle oder GreenCircle) und die sich nur durch eine einzige Funktionalität (wie Farbe) unterscheiden. Und ich brauche ein Bridge-Muster, insbesondere wenn die vorhandenen Systemklassen (Kreis oder Form) häufig geändert werden sollen und Sie nicht möchten, dass neu hinzugefügte Klassen von diesen Änderungen betroffen sind. Aus diesem Grund wird die generische Zeichenfunktionalität in eine neue Oberfläche abstrahiert, sodass Sie das Zeichenverhalten unabhängig von Form oder Kreis ändern können.