Zum Inhalt springen

Prototyp (Entwurfsmuster)

aus Wikipedia, der freien Enzyklopädie
Dies ist die aktuelle Version dieser Seite, zuletzt bearbeitet am 11. Januar 2025 um 09:29 Uhr durch imported>Corpophiliac.
(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)

Ein Prototyp ({{Modul:Vorlage:lang}} Modul:Vorlage:lang:103: attempt to index field 'wikibase' (a nil value)) ist ein Entwurfsmuster (engl. {{Modul:Vorlage:lang}} Modul:Multilingual:153: attempt to index field 'data' (a nil value)) aus dem Bereich der Softwareentwicklung und gehört zur Kategorie der Erzeugungsmuster (engl. {{Modul:Vorlage:lang}} Modul:Multilingual:153: attempt to index field 'data' (a nil value)). Neue Instanzen werden auf Grundlage von prototypischen Instanzen („Vorlagen“) erzeugt. Dabei wird die Vorlage kopiert und an neue Bedürfnisse angepasst.<ref>Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides: Entwurfsmuster. 5. Auflage. Addison-Wesley, 1996, ISBN 3-8273-1862-9.</ref> Das Muster ist eines der sogenannten GoF-Muster ({{Modul:Vorlage:lang}} Modul:Multilingual:153: attempt to index field 'data' (a nil value), siehe Viererbande).

Verwendung

Ein Prototyp wird angewendet, wenn

  • die Erzeugung weiterer Instanzen einer Klasse aufwendig (teuer) ist und sich die Objekte ähneln,
  • die zu instanziierenden Klassen erst zur Laufzeit bekannt sind,
  • eine Hierarchie von Fabriken parallel zu einer Hierarchie von Produkten vermieden werden soll, oder,
  • die Objekte einer Klasse nur wenige Zustandskombinationen annehmen können; oder
  • die Bearbeitung von Vorlagen sehr ähnlich oder gleich der von Objekten ist.

Skriptfehler: Ein solches Modul „Vorlage:Siehe auch“ ist nicht vorhanden.

UML-Diagramm

Das folgende Klassendiagramm zeigt die am Entwurfsmuster beteiligten Rollen. Ein Klient ruft die Methode klone() eines Objekts vom Typ Prototyp auf und bekommt entweder ein Objekt vom Typ KonkreterPrototyp1 oder vom Typ KonkreterPrototyp2 zurück, je nachdem, welcher der beiden Typen sich hinter der Schnittstelle Prototyp in dieser einfachen Assoziation (UML) verbirgt.

Klassendiagramm, das die am Entwurfsmuster beteiligten Rollen zeigt.
Klassendiagramm, das die am Entwurfsmuster beteiligten Rollen zeigt.

Akteure

Der Typ Prototyp definiert eine Schnittstelle zur Kopie eines Objektes. Objekte vom Typ KonkreterPrototyp1 oder KonkreterPrototyp2 kopieren sich selbst durch Aufruf ihrer Implementierung der durch die Schnittstelle vorgeschriebenen klone()-Methode. Man unterscheidet zwischen flacher Kopie ({{Modul:Vorlage:lang}} Modul:Multilingual:153: attempt to index field 'data' (a nil value)) und tiefer Kopie ({{Modul:Vorlage:lang}} Modul:Multilingual:153: attempt to index field 'data' (a nil value)). Erstere lässt Verweise auf andere Objekte bestehen, zweiteres kopiert zusätzlich die referenzierten Objekte. In der Regel wird eine tiefe Kopie eines Prototyps hergestellt (außer für nicht-modifizierbare Objekte), die genauen Details müssen aber von Fall zu Fall geklärt werden.

Der Klient erzeugt neue Objekte als Kopie bestehender Objekte und modifiziert sie.

Anmerkung: Die Verwendung einer clone()-Methode ist nicht gleichbedeutend mit der Anwendung eines Prototyp-Musters. Im Prototyp-Muster wird konzeptionell zwischen den Prototypen und den (Nutz-)Objekten unterschieden, es dient also nicht jedes Objekt (einer Klassenhierarchie) als Prototyp. Daher soll beim Kopieren des Prototyps zum Zwecke der Objekterzeugung auch zumindest das „Status=Prototyp“-Flag (o. ä.) verändert werden, und daher gilt nicht mehr, dass der Prototyp gleich dem erzeugten Objekt ist. Sinnvollerweise wird dafür eine eigene Instanzierungsmethode oder eine clone()-Methode mit Parameter verwendet.

Vorteile

Komplexe Objekte lassen sich schneller erzeugen. Neue Unterklassen können zur Laufzeit eingebunden werden. Neue Objekte können durch Variation der Struktur spezifiziert werden. Es gibt keine Erzeuger-Klassenhierarchie parallel zur Klassenhierarchie der Produkte.

Nachteile

Die Erstellung einer Kopie eines Objektes kann aufwendig sein. Jede Unterklasse muss die Kopie-Operation implementieren. Eventuelle Initialisierungen des kopierten Objekts müssen zusätzlich erfolgen.

Verwendung in der Dokumentenverarbeitung

In der Dokumentenverarbeitung (z. B. Microsoft Office, LibreOffice) ist das Prototyp-Muster Standard: Die Vorlagen werden mit denselben Tools wie die eigentlichen Dokumente bearbeitet; zum Erstellen eines Dokuments wird eine Vorlage kopiert und dann weiterbearbeitet. Oft gibt es eine Standard-Vorlage (z. B. „Normal.dot“), die als minimaler Prototyp für Objekte oder andere Vorlagen dienen kann.

Beispiel

Diese C++11 Implementierung basiert auf der vor C++98 Implementierung im Buch Entwurfsmuster.

<syntaxhighlight lang="cpp">

  1. include <iostream>

enum Richtung {Norden, Sueden, Osten, Westen};

class KartenEintrag { public:

 virtual void betrete() = 0;
 virtual KartenEintrag* klone() const = 0;
 virtual ~KartenEintrag() = default;

};

class Raum : public KartenEintrag { public:

 Raum() :raumNr(0) {}
 Raum(int n) :raumNr(n) {}
 void setSeite(Richtung d, KartenEintrag* ms) {
   std::cout << "Raum::setSeite " << d << ' ' << ms << '\n';
 }
 virtual void betrete() {}
 virtual Raum* klone() const {
   return new Raum(*this);
 }
 Raum& operator=(const Raum&) = delete;

private:

 int raumNr;

};

class Wand : public KartenEintrag { public:

 Wand() {}
 virtual void betrete() {}
 virtual Wand* klone() const {
   return new Wand(*this);
 }

};

class Tuer : public KartenEintrag { public:

 Tuer(Raum* r1 = nullptr, Raum* r2 = nullptr)
   :raum1(r1), raum2(r2) {}
 Tuer(const Tuer& andere)
   :raum1(andere.raum1), raum2(andere.raum2) {}
 virtual void betrete() {}
 virtual Tuer* klone() const {
   return new Tuer(*this);
 }
 virtual void initialisiere(Raum* r1, Raum* r2) {
   raum1 = r1;
   raum2 = r2;
 }
 Tuer& operator=(const Tuer&) = delete;

private:

 Raum* raum1;
 Raum* raum2;

};

class Labyrinth { public:

 void fuegeRaumHinzu(Raum* r) {
   std::cout << "Labyrinth::fuegeRaumHinzu " << r << '\n';
 }
 Raum* raumNr(int) const {
   return nullptr;
 }
 virtual Labyrinth* klone() const {
   return new Labyrinth(*this);
 }
 virtual ~Labyrinth() = default;

};

class LabyrinthFabrik { public:

 LabyrinthFabrik() = default;
 virtual ~LabyrinthFabrik() = default;
 virtual Labyrinth* erzeugeLabyrinth() const {
   return new Labyrinth;
 }
 virtual Wand* erzeugeWand() const {
   return new Wand;
 }
 virtual Raum* erzeugeRaum(int n) const {
   return new Raum(n);
 }
 virtual Tuer* erzeugeTuer(Raum* r1, Raum* r2) const {
   return new Tuer(r1, r2);
 }

};

class LabyrinthPrototypFabrik : public LabyrinthFabrik { public:

 LabyrinthPrototypFabrik(Labyrinth* m, Wand* w, Raum* r, Tuer* d)
   :prototypLabyrinth(m), prototypRaum(r),
    prototypWand(w), prototypTuer(d) {}
 virtual Labyrinth* erzeugeLabyrinth() const {
   return prototypLabyrinth->klone();
 }
 virtual Raum* erzeugeRaum(int) const {
   return prototypRaum->klone();
 }
 virtual Wand* erzeugeWand() const {
   return prototypWand->klone();
 }
 virtual Tuer* erzeugeTuer(Raum* r1, Raum* r2) const {
   Tuer* door = prototypTuer->klone();
   door->initialisiere(r1, r2);
   return door;
 }
 LabyrinthPrototypFabrik(const LabyrinthPrototypFabrik&) = delete;
 LabyrinthPrototypFabrik& operator=(const LabyrinthPrototypFabrik&) = delete;

private:

 Labyrinth* prototypLabyrinth;
 Raum* prototypRaum;
 Wand* prototypWand;
 Tuer* prototypTuer;

};

// Wenn baueLabyrinth mit verschiedenen prototypischen Raum-, Tür- und Wandobjekten parametrisiert wird, welche es kopieren und dem Labyrinth hinzufügen kann, dann können Sie den Aufbau des Labyrinths durch Ersetzen dieser prototypischen Objekte verändern. Dies ist ein Beispiel für das Prototypmuster (144).

class LabyrinthSpiel { public:

 Labyrinth* baueLabyrinth(LabyrinthPrototypFabrik& m) {
   Labyrinth* einLabyrinth = m.erzeugeLabyrinth();
   Raum* r1 = m.erzeugeRaum(1);
   Raum* r2 = m.erzeugeRaum(2);
   Tuer* dieTuer = m.erzeugeTuer(r1, r2);
   einLabyrinth->fuegeRaumHinzu(r1);
   einLabyrinth->fuegeRaumHinzu(r2);
   r1->setSeite(Norden, m.erzeugeWand());
   r1->setSeite(Osten, dieTuer);
   r1->setSeite(Sueden, m.erzeugeWand());
   r1->setSeite(Westen, m.erzeugeWand());
   r2->setSeite(Norden, m.erzeugeWand());
   r2->setSeite(Osten, m.erzeugeWand());
   r2->setSeite(Sueden, m.erzeugeWand());
   r2->setSeite(Westen, dieTuer);
   return einLabyrinth;
 }

};

int main() {

 LabyrinthSpiel spiel;
 LabyrinthPrototypFabrik einfacheLabyrinthFabrik
   (new Labyrinth, new Wand, new Raum, new Tuer);
 spiel.baueLabyrinth(einfacheLabyrinthFabrik);

} </syntaxhighlight>

Die Programmausgabe ist ähnlich zu:

<syntaxhighlight lang="cpp"> Labyrinth::fuegeRaumHinzu 0x1353f50 Labyrinth::fuegeRaumHinzu 0x1353f70 Raum::setSeite 0 0x13543c0 Raum::setSeite 2 0x1353f90 Raum::setSeite 1 0x13543e0 Raum::setSeite 3 0x1354400 Raum::setSeite 0 0x1354420 Raum::setSeite 2 0x1354440 Raum::setSeite 1 0x1354460 Raum::setSeite 3 0x1353f90 </syntaxhighlight>

Im JDK wird das Prototyp-Muster nicht explizit verwendet; nur bei der Klasse javax.swing.text.EditorKit ist angeführt, dass neue Instanzen durch Klonen einer anderen Instanz erzeugt werden sollen. Viele Klassen im JDK implementieren allerdings eine öffentliche clone()-Methode, die prinzipiell als Grundlage eines Prototyp-Patterns verwendet werden kann.

Implementierung in .NET-Sprachen

Im .Net-Framework und in .NET bietet die Basisklasse System.Object die geschützte (protected) Methode MemberwiseClone(), die eine flache Kopie des Objekts erzeugt, d. h. wertbehaftete Typen (int, decimal etc.) werden byteweise 1 zu 1 kopiert, bei Referenzen wird nur der Referenzzeiger kopiert und nicht das Objekt selbst, auf das die neue Referenz zeigt. Um das Muster für einen Client nutzbar zu machen, muss der Prototyp noch die Schnittstelle ICloneable mit der Methode clone() implementieren (hier C#): <syntaxhighlight lang="CSharp"> using System; using System.Text;

namespace Prototype {

   abstract class Prototype<T> where T : Prototype<T>
   {
       public T Clone()
       {
           return (T)this.MemberwiseClone();
       }
       public abstract void Print();
       public abstract int X { get; set; }
   }
   class ConcretePrototype : Prototype<ConcretePrototype>
   {
       public ConcretePrototype(int x) { X = x; }
       public override void Print()
       {
           Console.WriteLine(X);
       }
       public override int X { get; set; }
   }
   class Client
   {
       static void Main(string[] args)
       {
           int num = 1000;
           ConcretePrototype tempProt = null;
           ConcretePrototype prot = new ConcretePrototype(num);
           for (int i = 0; i < 10; i++)
           {
               tempProt = prot.Clone();
               tempProt.X *= i;
               tempProt.Print();
           }
       }
   }

} </syntaxhighlight>

Verwandte Entwurfsmuster

Einerseits konkurrieren abstrakte Fabrik und Prototyp miteinander, weil sie beide unterschiedliche Arten von Objekten erzeugen können. Andererseits lassen sie sich miteinander kombinieren, wenn eine abstrakte Fabrik Prototypen erzeugt, die dann anschließend ohne Zuhilfenahme einer Fabrik geklont werden können.

Kompositum und Decorator werden häufig gemeinsam mit Prototypen verwendet.

Weblinks

Einzelnachweise

<references />

<templatestyles src="Erweiterte Navigationsleiste/styles legacy.css" />Vorlage:Klappleiste/Anfang

Erzeugungsmuster

Abstrakte Fabrik | Erbauer | Fabrikmethode | Prototyp | Singleton | Multiton | Objektpool

Strukturmuster

Adapter | Brücke | Decorator | Fassade | Fliegengewicht | Kompositum | Stellvertreter

Verhaltensmuster

Beobachter | Besucher | Interpreter | Iterator | Kommando | Memento | Schablonenmethode | Strategie | Vermittler | Zustand | Zuständigkeitskette | Interceptor | Nullobjekt | Protokollstapel

Muster für objekt-
relationale Abbildung

Datentransferobjekt | Table Data Gateway | Row Data Gateway | Active Record | Unit of Work | Identity Map | Lazy Loading | Identity Field | Dependent Mapping | Embedded Value | Serialized LOB | Inheritance Mapper | Metadata Mapping | Query Object | Command-Query-Responsibility-Segregation

Nachrichten-
übermittlungsmuster
andere

Application Controller | Business Delegate | Data Access Object | Dependency Injection | Extension Interface | Fluent Interface | Inversion of Control (IoC) | Lock | Model View Controller (MVC) | Model View Presenter (MVP) | Model View Update (MVU) | Model View ViewModel (MVVM) | Page Controller | Registry | Remote Facade | Repository | Service Locator | Session State | Table Module | Template View | Threadpool | Transaction Script | Transform View | Two-Step View | Value Object

Vorlage:Klappleiste/Ende