Das Abstract Factory Pattern ist ein Entwurfsmuster, das zur Erzeugung von Objekten aus einer Familie verwandter Klassen dient, ohne die konkreten Klassen zu kennen. Statt direkt Objekte zu erstellen, nutzt der Client eine Schnittstelle, die verschiedene konkrete Fabriken bereitstellt. Jede dieser Fabriken ist für die Erstellung einer bestimmten Produktfamilie zuständig. Dieses Muster eignet sich besonders, wenn das System von der Familie von Objekten abhängig ist und diese Objekte dynamisch zur Laufzeit ausgewählt werden müssen.
Was ist das Abstract Factory Pattern?
Das Abstract Factory Pattern ist ein kreatives Entwurfsmuster, das eine Schnittstelle zur Verfügung stellt, um Objekte zu erzeugen. Dabei wird die konkrete Erstellung der Objekte in konkrete Fabriken ausgelagert. Diese Fabriken sind verantwortlich für die Instanziierung von Produkten einer bestimmten Familie. Der Vorteil liegt darin, dass der Client nur mit der Schnittstelle der Fabrik arbeitet, ohne sich um die Implementierung der konkreten Fabriken kümmern zu müssen.
Ein wichtiger Aspekt des Musters ist die Trennung der Produktfamilien. Eine Familie von Produkten umfasst eine Sammlung von Objekten, die zusammenarbeiten und dieselben abstrakten Schnittstellen implementieren. So kann der Client sicherstellen, dass er nur zusammenpassende Produkte einer Familie verwendet.
Struktur des Abstract Factory Patterns
- AbstractFactory: Eine Schnittstelle, die die Methode(n) zur Erstellung von Produkten definiert.
- ConcreteFactory: Diese Klassen implementieren die abstrakte Fabrik und erzeugen konkrete Produkte.
- AbstractProduct: Eine Schnittstelle, die die gemeinsamen Methoden für eine Produktfamilie definiert.
- ConcreteProduct: Konkrete Implementierungen der Produktklassen.
- Client: Der Client nutzt die Fabrik, um Produkte zu erzeugen, ohne ihre konkreten Typen zu kennen.
Beispiel des Abstract Factory Patterns in C++
In diesem Beispiel erstellen wir eine Software zur Verwaltung von Möbeln. Es gibt zwei verschiedene Produktfamilien: moderne Möbel und klassische Möbel. Jede Familie enthält einen Stuhl und einen Tisch. Wir implementieren das Abstract Factory Pattern, um sicherzustellen, dass der Client mit verschiedenen Möbeln arbeiten kann, ohne die konkreten Produktklassen zu kennen.
#include <iostream>
#include <string>
// AbstractProduct: Stuhl
class Chair {
public:
virtual void sit() const = 0;
virtual ~Chair() = default;
};
// ConcreteProduct: Moderner Stuhl
class ModernChair : public Chair {
public:
void sit() const override {
std::cout << "Sitz auf modernem Stuhl." << std::endl;
}
};
// ConcreteProduct: Klassischer Stuhl
class VictorianChair : public Chair {
public:
void sit() const override {
std::cout << "Sitz auf klassischem Stuhl." << std::endl;
}
};
// AbstractProduct: Tisch
class Table {
public:
virtual void use() const = 0;
virtual ~Table() = default;
};
// ConcreteProduct: Moderner Tisch
class ModernTable : public Table {
public:
void use() const override {
std::cout << "Verwende modernen Tisch." << std::endl;
}
};
// ConcreteProduct: Klassischer Tisch
class VictorianTable : public Table {
public:
void use() const override {
std::cout << "Verwende klassischen Tisch." << std::endl;
}
};
// AbstractFactory: Möbel Fabrik
class FurnitureFactory {
public:
virtual Chair* createChair() const = 0;
virtual Table* createTable() const = 0;
virtual ~FurnitureFactory() = default;
};
// ConcreteFactory: Moderne Möbel Fabrik
class ModernFurnitureFactory : public FurnitureFactory {
public:
Chair* createChair() const override {
return new ModernChair();
}
Table* createTable() const override {
return new ModernTable();
}
};
// ConcreteFactory: Klassische Möbel Fabrik
class VictorianFurnitureFactory : public FurnitureFactory {
public:
Chair* createChair() const override {
return new VictorianChair();
}
Table* createTable() const override {
return new VictorianTable();
}
};
// Client-Code
int main() {
// Wählen der Fabrik
FurnitureFactory* factory = new ModernFurnitureFactory();
// Erzeugen der Möbel
Chair* chair = factory->createChair();
Table* table = factory->createTable();
// Nutzung der Möbel
chair->sit();
table->use();
// Aufräumen
delete chair;
delete table;
delete factory;
return 0;
}
Erklärung des C++-Beispiels
In diesem Beispiel haben wir zwei Produktfamilien: moderne Möbel und klassische Möbel. Jede Familie hat zwei Produkte: einen Stuhl und einen Tisch. Die Struktur des Abstract Factory Patterns ist wie folgt:
- AbstractProduct: Die Schnittstellen
Chair
undTable
definieren Methoden, die von allen Produkten implementiert werden müssen. Jedes Produkt muss einesit()
-Methode (für Stühle) und eineuse()
-Methode (für Tische) bereitstellen. - ConcreteProduct:
ModernChair
,VictorianChair
,ModernTable
undVictorianTable
sind konkrete Implementierungen der Produktklassen. Sie bieten spezifische Implementierungen dersit()
– unduse()
-Methoden. - AbstractFactory: Die
FurnitureFactory
-Schnittstelle definiert zwei Methoden:createChair()
undcreateTable()
. Diese Methoden müssen von den konkreten Fabriken implementiert werden. - ConcreteFactory:
ModernFurnitureFactory
undVictorianFurnitureFactory
sind die konkreten Fabriken. Jede von ihnen erstellt Stühle und Tische der entsprechenden Produktfamilie. DiecreateChair()
– undcreateTable()
-Methoden erzeugen jeweils ein Objekt der entsprechenden Familie. - Client: Der Client verwendet die Fabrik, um Stühle und Tische zu erstellen, ohne die konkreten Fabriken zu kennen. Im Beispiel wählt der Client die
ModernFurnitureFactory
, um moderne Möbel zu erzeugen. Der Client nutzt diesit()
– unduse()
-Methoden der Produkte, ohne sich um die Details der konkreten Implementierungen zu kümmern.
Vorteile des Abstract Factory Patterns
- Trennung von Produkten und deren Erstellung: Der Client muss sich nicht mit der Erstellung der konkreten Objekte beschäftigen. Die Fabrik übernimmt diese Verantwortung und stellt die Objekte auf abstrakte Weise bereit.
- Flexibilität bei der Auswahl von Produktfamilien: Der Client kann einfach die Fabrik wechseln, um eine andere Produktfamilie zu verwenden. Dies ermöglicht es, zwischen verschiedenen Produktvarianten zu wechseln, ohne den Code des Clients zu ändern.
- Erleichterung von Erweiterungen: Wenn eine neue Produktfamilie hinzugefügt werden muss, kann dies durch Erstellen einer neuen Fabrik und neuer Produkte erfolgen, ohne dass der bestehende Code geändert werden muss.
- Konsistenz der Produkte: Alle Produkte, die von einer Fabrik erzeugt werden, sind konsistent, da sie zusammenarbeiten und zur gleichen Familie gehören. Dies verhindert Fehler, die durch die Mischung inkompatibler Produkte entstehen könnten.
Nachteile des Abstract Factory Patterns
- Erhöhte Komplexität: Das Muster kann die Anzahl der Klassen erhöhen, da für jede Produktfamilie eine separate Fabrik und konkrete Produktklassen erforderlich sind. Dies kann den Code komplexer machen, insbesondere bei großen Systemen.
- Schwierigkeiten bei der Erweiterung: Wenn zu viele Produktvarianten existieren, kann das Hinzufügen neuer Produktfamilien schwierig werden, da dies Änderungen in vielen Klassen erfordert.
Fazit
Das Abstract Factory Pattern ist ein äußerst nützliches Entwurfsmuster, wenn es darum geht, konsistente Produktfamilien zu erstellen und den Code flexibel zu halten. Es bietet eine saubere Trennung zwischen der Produkterstellung und der Nutzung der Produkte. In C++ lässt sich das Muster elegant implementieren, indem eine Schnittstelle für die Fabriken und Produkte verwendet wird. Es fördert die Erweiterbarkeit und ermöglicht es dem Client, problemlos zwischen verschiedenen Produktfamilien zu wechseln.
Zurück zur Liste: Liste der Design-Pattern