Das Adapter Pattern ist ein strukturelles Entwurfsmuster, das zwei inkompatible Schnittstellen miteinander verbindet. Es ermöglicht, dass Klassen, die nicht direkt zusammenarbeiten können, dennoch zusammenarbeiten. Der Adapter dient als Vermittler und übersetzt die Methodenaufrufe von einer Schnittstelle in die andere. Dabei bleibt die bestehende Implementierung unverändert, und der Adapter bietet eine einheitliche Schnittstelle für den Client.
Was ist das Adapter Pattern?
Das Adapter Pattern wird verwendet, wenn eine Klasse oder ein System eine Schnittstelle erwartet, aber eine andere Schnittstelle vorliegt. Es übersetzt die Aufrufe zwischen zwei inkompatiblen Schnittstellen, sodass diese zusammenarbeiten können. Der Adapter ist dafür verantwortlich, die Methode des einen Systems so umzuwandeln, dass sie mit der Schnittstelle des anderen Systems kompatibel ist.
Dieses Muster wird oft verwendet, wenn ein bestehendes System mit neuen Komponenten oder Bibliotheken integriert werden soll, die nicht mit der aktuellen Architektur übereinstimmen. Der Adapter sorgt dafür, dass das bestehende System weiter funktioniert, ohne dass die ursprüngliche Implementierung geändert werden muss.
Struktur des Adapter Patterns
- Target: Die Schnittstelle, die der Client erwartet. Der Adapter sorgt dafür, dass der Client mit dieser Schnittstelle interagieren kann.
- Adapter: Die Klasse, die die inkompatible Schnittstelle in die gewünschte Ziel-Schnittstelle übersetzt. Sie implementiert die
Target
-Schnittstelle. - Adaptee: Die bestehende, inkompatible Klasse, die in das Zielinterface integriert werden muss.
- Client: Der Client verwendet die
Target
-Schnittstelle, ohne zu wissen, dass ein Adapter verwendet wird.
Beispiel des Adapter Patterns in C++
Stellen wir uns vor, wir haben ein System, das mit einer bestehenden Bibliothek arbeitet, die für das Zeichnen von Formen verantwortlich ist. Das System verwendet eine Schnittstelle Shape
, aber die Bibliothek stellt die Form als OldShape
zur Verfügung, die nicht kompatibel ist. Wir verwenden das Adapter Pattern, um eine Verbindung zwischen diesen beiden Schnittstellen herzustellen.
#include <iostream>
// Ziel-Schnittstelle, die der Client erwartet
class Shape {
public:
virtual void draw() const = 0;
virtual ~Shape() = default;
};
// Adaptee: Die bestehende, inkompatible Klasse
class OldShape {
public:
void render() const {
std::cout << "Zeichne Form mit alter Bibliothek." << std::endl;
}
};
// Adapter: Übersetzt die alte Schnittstelle in die neue
class ShapeAdapter : public Shape {
private:
OldShape* oldShape; // Hält eine Instanz der inkompatiblen Klasse
public:
ShapeAdapter(OldShape* oldShape) : oldShape(oldShape) {}
// Implementiert die Methode der Ziel-Schnittstelle
void draw() const override {
oldShape->render(); // Übersetzt den Aufruf
}
};
// Client-Code
int main() {
OldShape* oldShape = new OldShape(); // Erstellen einer Instanz der alten Form
Shape* shape = new ShapeAdapter(oldShape); // Adapter wird verwendet
shape->draw(); // Aufruf der Ziel-Schnittstelle, die durch den Adapter übersetzt wird
delete oldShape;
delete shape;
return 0;
}
Erklärung des C++-Beispiels
In diesem Beispiel haben wir zwei inkompatible Schnittstellen: die Shape
-Schnittstelle, die der Client erwartet, und die OldShape
-Schnittstelle, die die bestehende Bibliothek bereitstellt. Die Struktur des Adapter Patterns ist folgendermaßen:
- Target: Die
Shape
-Schnittstelle stellt eine Methodedraw()
zur Verfügung, die vom Client erwartet wird. - Adaptee: Die
OldShape
-Klasse stellt eine Methoderender()
zur Verfügung, die aber inkompatibel mit derShape
-Schnittstelle ist. - Adapter: Die
ShapeAdapter
-Klasse implementiert dieShape
-Schnittstelle und enthält eine Instanz derOldShape
-Klasse. Die Methodedraw()
des Adapters ruft intern die Methoderender()
der alten Klasse auf und übersetzt so den Aufruf in die erwartete Form. - Client: Der Client verwendet die
Shape
-Schnittstelle und ruft diedraw()
-Methode auf, ohne zu wissen, dass diese durch einen Adapter in die inkompatibleOldShape
-Schnittstelle übersetzt wird.
Vorteile des Adapter Patterns
- Kompatibilität herstellen: Das Adapter Pattern ermöglicht es, inkompatible Schnittstellen miteinander zu verbinden. Es übersetzt die Anforderungen des Clients in eine Form, die das bestehende System versteht.
- Wiederverwendbarkeit: Der Adapter ermöglicht die Wiederverwendung bestehender Code-Teile, ohne dass diese geändert werden müssen. So bleibt die ursprüngliche Implementierung intakt.
- Erweiterbarkeit: Wenn ein neues System oder eine neue Schnittstelle hinzugefügt werden muss, kann ein Adapter erstellt werden, ohne dass der Client-Code angepasst werden muss.
- Flexibilität: Der Adapter ermöglicht es, verschiedene Systeme zu integrieren, selbst wenn diese unterschiedliche Schnittstellen verwenden. Neue Adapter können für verschiedene Systeme erstellt werden.
Nachteile des Adapter Patterns
- Zusätzliche Komplexität: Das Hinzufügen von Adaptern erhöht die Anzahl der Klassen im System. Dies kann die Komplexität erhöhen, besonders bei vielen inkompatiblen Schnittstellen.
- Leistungseinbußen: Der Adapter fügt eine zusätzliche Abstraktionsebene hinzu, die unter Umständen die Leistung beeinträchtigen kann. Besonders in leistungskritischen Systemen sollte der Einsatz von Adaptern sorgfältig abgewogen werden.
- Schwierigkeiten bei der Wartung: Wenn viele Adapter verwendet werden, kann es schwieriger werden, das System zu warten, da viele Klassen miteinander verbunden sind.
Fazit
Das Adapter Pattern ist ein sehr nützliches Designmuster, das die Integration inkompatibler Schnittstellen ermöglicht. Es wird oft eingesetzt, um die Zusammenarbeit von verschiedenen Systemen oder Bibliotheken zu erleichtern, die ursprünglich nicht kompatibel sind. In C++ kann dieses Muster leicht umgesetzt werden, indem eine Adapter-Klasse implementiert wird, die die Schnittstellenübersetzung übernimmt.
Die Vorteile des Musters liegen in der Flexibilität und Wiederverwendbarkeit, die es bietet. Entwickler können bestehende Implementierungen weiterhin verwenden, ohne Änderungen am ursprünglichen Code vornehmen zu müssen. Allerdings muss der Einsatz von Adaptern sorgfältig abgewogen werden, da sie zusätzliche Komplexität und potenzielle Leistungseinbußen verursachen können.
Zurück zur Liste der Pattern: Liste der Design-Pattern