Das Prototype Pattern gehört zu den kreativen Entwurfsmustern und dient dazu, Objekte durch Klonen eines bestehenden Objekts zu erzeugen, anstatt sie von Grund auf neu zu instanziieren. Dieses Muster wird häufig verwendet, wenn die Erstellung eines neuen Objekts teuer oder komplex ist und es daher effizienter ist, ein bestehendes Objekt zu kopieren. Die Idee hinter dem Prototype Pattern ist, dass ein Objekt als „Prototyp“ dient und durch Kopieren dieses Prototyps neue Instanzen erzeugt werden.
Was ist das Prototype Pattern?
Im Gegensatz zu anderen Entwurfsmustern, die die Objektinstanziierung auf bestimmte Weise steuern, konzentriert sich das Prototype Pattern auf das Kopieren eines Objekts. Ein Prototyp ist dabei ein Vorlagenobjekt, das alle notwendigen Daten und Strukturen für das zu erstellende Objekt enthält. Anstatt jedes Mal ein neues Objekt mit einem Konstruktor zu erstellen, wird eine Kopie des bestehenden Prototyps erzeugt. Dies ist insbesondere dann vorteilhaft, wenn das Erstellen eines Objekts aufwändig ist.
Das Prototype Pattern bietet eine einfache Möglichkeit, ein neues Objekt zu erzeugen, indem das bestehende Objekt einfach kopiert wird. Dies wird insbesondere dann nützlich, wenn das Erstellen von Objekten mit komplexen Initialisierungen oder langwierigen Berechnungen verbunden ist.
Grundstruktur des Prototype Patterns
Die Struktur des Prototyp Patterns besteht aus mehreren wichtigen Komponenten:
- Prototype: Eine abstrakte Klasse oder Schnittstelle, die die Methode
clone()
deklariert. Diese Methode wird verwendet, um eine exakte Kopie des Objekts zu erstellen. - ConcretePrototype: Eine konkrete Implementierung der
Prototype
-Schnittstelle, die die Methodeclone()
überlädt und dafür sorgt, dass eine exakte Kopie des Objekts erstellt wird. - Client: Der Client-Code, der die
clone()
-Methode verwendet, um neue Instanzen eines Objekts zu erstellen, indem er das Prototypobjekt kopiert.
Beispiel des Prototype Patterns in C++
Das folgende C++-Beispiel veranschaulicht die Funktionsweise des Prototyp Patterns. Wir modellieren ein einfaches Szenario, in dem verschiedene Arten von „Karten“ als Objekte existieren. Jede Karte hat bestimmte Eigenschaften, und durch das Prototyp Pattern können wir neue Karten erzeugen, indem wir bestehende Karten kopieren.
#include <iostream>
#include <string>
// Prototype Interface: Deklariert die clone() Methode
class CardPrototype {
public:
virtual CardPrototype* clone() const = 0;
virtual void display() const = 0;
virtual ~CardPrototype() = default;
};
// ConcretePrototype: Eine konkrete Implementierung der Karte
class CreditCard : public CardPrototype {
private:
std::string cardNumber;
std::string cardHolder;
std::string expirationDate;
public:
CreditCard(const std::string& number, const std::string& holder, const std::string& expiration)
: cardNumber(number), cardHolder(holder), expirationDate(expiration) {}
// Implementierung der clone-Methode
CreditCard* clone() const override {
return new CreditCard(*this); // Klonen der Karte
}
void display() const override {
std::cout << "Kreditkarte:\n"
<< "Nummer: " << cardNumber << "\n"
<< "Inhaber: " << cardHolder << "\n"
<< "Ablaufdatum: " << expirationDate << "\n";
}
};
// ConcretePrototype: Eine andere konkrete Implementierung einer Karte
class DebitCard : public CardPrototype {
private:
std::string cardNumber;
std::string cardHolder;
std::string bankName;
public:
DebitCard(const std::string& number, const std::string& holder, const std::string& bank)
: cardNumber(number), cardHolder(holder), bankName(bank) {}
// Implementierung der clone-Methode
DebitCard* clone() const override {
return new DebitCard(*this); // Klonen der Debitkarte
}
void display() const override {
std::cout << "Debitkarte:\n"
<< "Nummer: " << cardNumber << "\n"
<< "Inhaber: " << cardHolder << "\n"
<< "Bank: " << bankName << "\n";
}
};
// Client-Code
int main() {
// Erstellen einer Kreditkarte und einer Debitkarte
CreditCard* originalCreditCard = new CreditCard("1234 5678 9876 5432", "Max Mustermann", "12/25");
DebitCard* originalDebitCard = new DebitCard("9876 5432 1234 5678", "Julia Schmidt", "Sparkasse");
// Klonen der Kreditkarte und der Debitkarte
CreditCard* clonedCreditCard = static_cast<CreditCard*>(originalCreditCard->clone());
DebitCard* clonedDebitCard = static_cast<DebitCard*>(originalDebitCard->clone());
// Anzeigen der Originale und der Klone
std::cout << "Original Kreditkarte:\n";
originalCreditCard->display();
std::cout << "\nGeklonte Kreditkarte:\n";
clonedCreditCard->display();
std::cout << "\nOriginal Debitkarte:\n";
originalDebitCard->display();
std::cout << "\nGeklonte Debitkarte:\n";
clonedDebitCard->display();
// Aufräumen
delete originalCreditCard;
delete originalDebitCard;
delete clonedCreditCard;
delete clonedDebitCard;
return 0;
}
Erklärung des C++-Beispiels
Im obigen Beispiel haben wir die Prototype-Schnittstelle, die die clone()
-Methode deklariert. Die Methode clone()
ist dafür verantwortlich, eine exakte Kopie des Objekts zu erstellen. Es gibt zwei konkrete Implementierungen des Prototyps:
- CreditCard: Diese Klasse stellt eine Kreditkarte dar. Sie implementiert die Methode
clone()
, die eine Kopie des aktuellen Objekts zurückgibt. - DebitCard: Diese Klasse stellt eine Debitkarte dar, die ebenfalls die Methode
clone()
implementiert.
Im Client-Code werden zunächst zwei Karten erstellt: eine CreditCard
und eine DebitCard
. Anschließend werden diese Karten geklont, und die Details der Original- und der geklonten Karten werden ausgegeben.
Die Methode clone()
in beiden Klassen ermöglicht es, schnell Kopien der bestehenden Karten zu erstellen, ohne sie manuell zu rekonstruieren. Dies spart sowohl Zeit als auch Ressourcen, insbesondere wenn die Erzeugung der Objekte komplex oder teuer ist.
Beispiel des Prototype Patterns in Python
In Python wird das Prototype Pattern normalerweise durch das Kopieren von Objekten implementiert, z.B. unter Verwendung der copy
-Bibliothek oder der __copy__
und __deepcopy__
Methoden.
Hier ist ein einfaches Beispiel, das das Prototype Pattern in Python demonstriert:
import copy
class Prototype:
def clone(self):
"""Erstellt eine Kopie des aktuellen Objekts."""
return copy.deepcopy(self)
class Car(Prototype):
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
def display(self):
"""Zeigt die Fahrzeugdetails an."""
print(f"{self.year} {self.make} {self.model}")
# Hauptprogramm
if __name__ == "__main__":
# Erstellen des Originalobjekts
original_car = Car("VW", "Passat", 2023)
original_car.display()
# Erstellen einer Kopie des Objekts durch das Prototype Pattern
cloned_car = original_car.clone()
cloned_car.display()
# Modifizieren des geklonten Objekts
cloned_car.year = 2024
cloned_car.display()
# Das Originalobjekt bleibt unverändert
original_car.display()
Erklärung des Prototype Patterns in Python
- Prototype-Klasse: Diese Klasse definiert eine Methode
clone
, die das Kopieren des Objekts durch Deepcopy ermöglicht. Das Kopieren erfolgt mithilfe voncopy.deepcopy
, das eine vollständige Kopie des Objekts erstellt, einschließlich der darin enthaltenen Objekte. - Car-Klasse: Diese Klasse erbt von der
Prototype
-Klasse und stellt die spezifischen Eigenschaften eines Fahrzeugs dar. Die Methodedisplay
zeigt die Details des Fahrzeugs an. - Hauptprogramm:
- Ein
original_car
-Objekt wird erstellt. - Das Objekt wird durch den
clone
-Aufruf dupliziert. - Die Eigenschaften des geklonten Objekts werden geändert, aber das Original bleibt unverändert, was das Verhalten des Prototypen zeigt.
- Ein
Ausgabe
2023 VW Passat
2023 VW Passat
2024 VW Passat2023 VW Passat
Vorteile des Prototype Patterns
- Effizienz: Wenn das Erstellen eines Objekts ressourcenintensiv oder zeitaufwendig ist, kann das Kopieren eines bestehenden Objekts viel schneller und effizienter sein. Besonders dann, wenn nur wenige Unterschiede zwischen den Objekten bestehen.
- Flexibilität: Das Prototyp Pattern ermöglicht es, Objekte zur Laufzeit zu klonen und anzupassen. Dadurch können flexibel neue Instanzen erzeugt werden, ohne den gesamten Konstruktionsprozess neu zu durchlaufen.
- Einfachheit: Im Vergleich zu anderen Entwurfsmustern, bei denen der Client eine komplexe Objektinstanziierung durchlaufen muss, vereinfacht das Prototyp Pattern den Prozess, indem es den Klonvorgang zentralisiert und standardisiert.
- Reduzierung der Code-Duplikation: Da das Klonen eines Objekts im Wesentlichen nur das Kopieren der Daten eines bestehenden Objekts bedeutet, wird der Code zur Erstellung neuer Objekte reduziert, was zu weniger Duplikation und damit zu saubererem Code führt.
Nachteile des Prototype Patterns
- Komplexe Objekte: Wenn ein Objekt sehr komplex ist, kann das Klonen schwierig sein, insbesondere wenn es tiefe Kopien von Objekten oder komplexe Abhängigkeiten erfordert. Das Kopieren von Referenzen statt tiefem Kopieren kann zu unerwarteten Problemen führen.
- Abhängigkeiten zwischen Objekten: Wenn Objekte enge Beziehungen zueinander haben, kann das Klonen dazu führen, dass ungewollte Verknüpfungen zwischen den Kopien entstehen, was zu unvorhersehbaren Verhaltensweisen führen kann.
Was ist ein Prototyp im Entwurfsmuster?
Ein Prototyp im Kontext von Entwurfsmustern ist ein Urmodell oder Kopie eines Objekts, von dem neue Objekte durch Kopieren erstellt werden. Es geht darum, ein existierendes Objekt als Vorlage zu verwenden, um neue Instanzen zu erzeugen, anstatt sie von Grund auf neu zu erstellen. Das Prototypenmuster ist besonders nützlich, wenn die Erstellung eines neuen Objekts teuer oder komplex ist und viele Objekte ähnliche Eigenschaften teilen.
Ein Prototyp ist eine Vorlage oder ein Muster, von dem andere Objekte kopiert werden. Statt ein neues Objekt manuell und Schritt für Schritt zu erstellen, wird ein bestehendes Objekt als Ausgangspunkt verwendet und durch Kopieren eine neue Instanz erzeugt. Das Prototyp Pattern basiert auf der Idee der Objektkopie. Dies kann durch flaches Kopieren (Shallow Copy) oder tiefes Kopieren (Deep Copy) geschehen, je nach Bedarf und Komplexität des Objekts.
Das Prototypenmuster wird eingesetzt, wenn
- Die Erzeugung von Objekten teuer ist: Wenn das Erstellen eines Objekts sehr viel Zeit, Ressourcen oder Berechnungsaufwand benötigt, kann es sinnvoll sein, ein bestehendes Objekt zu kopieren und anzupassen.
- Objekte ähnliche Struktur aufweisen: Wenn viele Objekte ähnliche Eigenschaften und Strukturen haben, kann das Kopieren eines Prototyps und das Anpassen nur der spezifischen Attribute eine effizientere Lösung sein.
- Dynamisches Erstellen von Instanzen: In Szenarien, in denen das Erstellen von Objekten zur Laufzeit benötigt wird, kann das Prototypenmuster die Flexibilität erhöhen.
Die Vorteile des Prototypenmusters sind
- Effizienz: Wenn das Erstellen eines Objekts teuer ist, ist das Kopieren eines Prototyps schneller und ressourcenschonender.
- Flexibilität: Neue Objekte können dynamisch erzeugt und angepasst werden, ohne dass der gesamte Erstellungsprozess neu durchlaufen werden muss.
- Vermeidung von Code-Duplikationen: Wenn viele ähnliche Objekte erstellt werden müssen, spart das Prototypenmuster Zeit und Aufwand, da ein bestehendes Objekt als Vorlage verwendet wird.
In der Praxis werden Prototypen in vielen Szenarien genutzt, insbesondere in Softwareprojekten, bei denen komplexe Objekte existieren, die in vielen Varianten benötigt werden, oder viele ähnliche Objekte erzeugt werden müssen, die sich nur in wenigen Attributen unterscheiden. Ein Beispiel hierfür sind grafische Anwendungen, in denen verschiedene Formen (Kreise, Rechtecke, Linien) auf der Benutzeroberfläche gezeichnet werden. Anstatt jede Form von Grund auf neu zu erstellen, kann eine Form als Prototyp gespeichert und dann kopiert werden, um neue Instanzen mit minimalen Änderungen zu erzeugen.
Im Entwurfsmuster ist ein Prototyp einfach ein Objekt, das als Vorlage dient, von der aus neue Objekte durch Kopieren erstellt werden. Dieses Muster hilft dabei, die Erstellungslogik zu vereinfachen und bietet eine effiziente Möglichkeit, neue Instanzen von Objekten zu erzeugen, ohne sie vollständig neu zu bauen.
Fazit
Das Prototype Pattern ist ein wertvolles Entwurfsmuster, wenn es darum geht, Objekte effizient zu erstellen, indem bestehende Instanzen kopiert werden. Besonders in Szenarien, in denen das Erstellen von Objekten ressourcenintensiv ist , kann das Prototyp Pattern die Effizienz und Flexibilität steigern. In C++ lässt sich das Muster elegant implementieren, indem eine clone()
-Methode verwendet wird, die es ermöglicht, bestehende Objekte zu duplizieren und bei Bedarf anzupassen.
Zu der Liste der Design-Pattern: Liste der Design-Pattern