Prototyp-Muster - Prototype pattern

Das Prototypmuster ist ein schöpferisches Entwurfsmuster in der Softwareentwicklung . Es wird verwendet, wenn der Typ der zu erstellenden Objekte durch eine prototypische Instanz bestimmt wird , die geklont wird, um neue Objekte zu erzeugen. Dieses Muster wird verwendet, um Unterklassen eines Objekterstellers in der Clientanwendung zu vermeiden , wie dies beim Factory-Methodenmuster der Fall ist, und um die inhärenten Kosten für die Erstellung eines neuen Objekts auf Standardmethode (z. B. mit dem Schlüsselwort ' new ') zu vermeiden, wenn es für eine bestimmte Anwendung zu teuer.

Um das Muster zu implementieren, deklarieren Sie eine abstrakte Basisklasse , die eine reine virtuelle clone()- Methode angibt . Jede Klasse, die eine " polymorphe Konstruktor "-Fähigkeit benötigt, leitet sich von der abstrakten Basisklasse ab und implementiert die clone()- Operation.

Anstatt Code zu schreiben, der den Operator "new" für einen hartcodierten Klassennamen aufruft, ruft der Client die Methode clone() des Prototyps auf, ruft eine Fabrikmethode mit einem Parameter auf , der die gewünschte konkrete abgeleitete Klasse bezeichnet , oder ruft die clone() Methode durch einen Mechanismus, der von einem anderen Entwurfsmuster bereitgestellt wird.

Die mitotische Teilung einer Zelle – die zu zwei identischen Zellen führt – ist ein Beispiel für einen Prototyp, der eine aktive Rolle beim Kopieren selbst spielt und somit das Prototyp-Muster demonstriert. Wenn sich eine Zelle teilt, entstehen zwei Zellen gleichen Genotyps. Mit anderen Worten, die Zelle klont sich selbst.

Überblick

Das Prototyp-Entwurfsmuster ist eines der 23 bekannten GoF-Entwurfsmuster, die beschreiben, wie wiederkehrende Entwurfsprobleme gelöst werden, um flexible und wiederverwendbare objektorientierte Software zu entwerfen, d. h. Objekte, die einfacher zu implementieren, zu ändern, zu testen und Wiederverwendung.

Das Prototyp-Designmuster löst Probleme wie:

  • Wie können Objekte erstellt werden, damit die zu erstellenden Objekte zur Laufzeit angegeben werden können?
  • Wie können dynamisch geladene Klassen instanziiert werden?

Das Erstellen von Objekten direkt innerhalb der Klasse, die die Objekte erfordert (verwendet), ist unflexibel, da es die Klasse zur Kompilierzeit bestimmten Objekten zuordnet und es unmöglich macht, anzugeben, welche Objekte zur Laufzeit erstellt werden sollen.

Das Prototyp-Design-Pattern beschreibt, wie solche Probleme gelöst werden können:

  • Definieren Sie ein PrototypeObjekt, das eine Kopie von sich selbst zurückgibt.
  • Erstellen Sie neue Objekte, indem Sie ein PrototypeObjekt kopieren .

Dies ermöglicht die Konfiguration einer Klasse mit verschiedenen PrototypeObjekten, die kopiert werden, um neue Objekte zu erstellen, und darüber hinaus können PrototypeObjekte zur Laufzeit hinzugefügt und entfernt werden.
Siehe auch das UML-Klassen- und Sequenzdiagramm unten.

Struktur

UML-Klassen- und Sequenzdiagramm

Image
Ein Beispiel für ein UML-Klassen- und Sequenzdiagramm für das Prototyp-Entwurfsmuster.

Im obigen UML -KlassendiagrammClient bezieht sich die Klasse auf die PrototypeSchnittstelle zum Klonen einer Product. Die Product1Klasse implementiert die PrototypeSchnittstelle, indem sie eine Kopie von sich selbst erstellt.
Das UML- Sequenzdiagramm zeigt die Laufzeitinteraktionen: Das ClientObjekt ruft clone()ein prototype:Product1Objekt auf, das eine Kopie von sich selbst (einem product:Product1Objekt) erstellt und zurückgibt .

UML-Klassendiagramm

Image
UML -Klassendiagramm, das das Prototyp-Entwurfsmuster beschreibt

Faustregeln

Manchmal überschneiden sich schöpferische Muster – es gibt Fälle, in denen entweder ein Prototyp oder eine abstrakte Fabrik angemessen wäre. Zu anderen Zeiten ergänzen sie sich: Die abstrakte Fabrik kann eine Reihe von Prototypen speichern, aus denen Produktobjekte geklont und zurückgegeben werden können ( GoF , S. 126). Abstrakte Fabrik, Builder und Prototyp können Singleton in ihren Implementierungen verwenden. (GoF, S. 81, 134). Abstrakte Fabrikklassen werden oft mit Fabrikmethoden implementiert (Erzeugung durch Vererbung ), aber sie können mit Prototypen implementiert werden (Erzeugung durch Delegation ). (GoF, S.95)

Designs beginnen oft mit der Fabrikmethode (weniger kompliziert, anpassbarer, Unterklassen nehmen zu) und entwickeln sich zu abstrakten Fabriken, Prototypen oder Buildern (flexibler, komplexer), wenn der Designer erkennt, wo mehr Flexibilität erforderlich ist. (GoF, S.136)

Prototype erfordert keine Unterklassenbildung, aber einen "initialize"-Vorgang. Die Factory-Methode erfordert eine Unterklassenbildung, jedoch keine Initialisierung. (GoF, S. 116)

Auch Designs, die stark von Verbund- und Dekormustern Gebrauch machen, können oft von Prototype profitieren. (GoF, S. 126)

Die Faustregel könnte sein, dass Sie ein Objekt klonen () müssen, wenn Sie zur Laufzeit ein anderes Objekt erstellen möchten, das eine echte Kopie des Objekts ist, das Sie klonen. Echte Kopie bedeutet, dass alle Attribute des neu erstellten Objekts mit dem Objekt identisch sein sollten, das Sie klonen. Hätten Sie die Klasse stattdessen mit new instanziieren können , erhalten Sie ein Objekt mit allen Attributen als Anfangswerte. Wenn Sie beispielsweise ein System zum Durchführen von Bankkontotransaktionen entwerfen, möchten Sie eine Kopie des Objekts erstellen, das Ihre Kontoinformationen enthält, Transaktionen darauf ausführen und dann das ursprüngliche Objekt durch das geänderte ersetzen. In solchen Fällen sollten Sie clone() anstelle von new verwenden.

Codebeispiele

Pseudocode

Lassen Sie uns eine Vorkommensbrowserklasse für einen Text schreiben. Diese Klasse listet die Vorkommen eines Wortes in einem Text auf. Ein solches Objekt ist teuer in der Herstellung, da die Orte der Vorkommnisse einen kostspieligen Prozess erfordern, um es zu finden. Um ein solches Objekt zu duplizieren, verwenden wir das Prototypmuster:

class WordOccurrences is
    field occurrences is
        The list of the index of each occurrence of the word in the text.

    constructor WordOccurrences(text, word) is
            input: the text in which the occurrences have to be found
            input: the word that should appear in the text
        Empty the occurrences list
        for each textIndex in text
            isMatchingg:= true
            for each wordIndex in word
                if the current word character does not match the current text character then
                    isMatchingg:= false
            if isMatching is true then
                Add the current textIndex into the occurrences list

    method getOneOccurrenceIndex(n) is
            input: a number to point on the nth occurrence.
            output: the index of the nth occurrence.
        Return the nth item of the occurrences field if any.

    method clone() is
            output: a WordOccurrences object containing the same data.
        Call clone() on the super class.
        On the returned object, set the occurrences field with the value of the local occurrences field.
        Return the cloned object.

texte:= "The prototype pattern is a creational design pattern in software development first described in design patterns, the book."
wordw:= "pattern"d
searchEnginen:= new WordOccurrences(text, word)
anotherSearchEngineE:= searchEngine.clone()

(Der Suchalgorithmus ist nicht optimiert; es ist ein grundlegender Algorithmus zur Veranschaulichung der Musterimplementierung)

C#-Beispiel

Aus seinem Prototyp entsteht der konkrete Objekttyp. MemberwiseClone wird in der Clone-Methode verwendet, um eine Kopie von ConcreteFoo1 oder ConcreteFoo2 zu erstellen und zurückzugeben.

public abstract class Foo
{
    // normal implementation

    public abstract Foo Clone();
}

public class ConcreteFoo1 : Foo
{
    public override Foo Clone()
    {
        return (Foo)this.MemberwiseClone(); // Clones the concrete class.
    }
}

public class ConcreteFoo2 : Foo
{
    public override Foo Clone()
    {
        return (Foo)this.MemberwiseClone(); // Clones the concrete class.
    }
}

C++-Beispiel

Eine Diskussion des Entwurfsmusters zusammen mit einer vollständigen veranschaulichenden Beispielimplementierung unter Verwendung des polymorphen Klassendesigns finden Sie in den C++-Anmerkungen .

Java-Beispiel

Dieses Muster erstellt die Art von Objekt, die seinen Prototyp verwendet. Mit anderen Worten, beim Erstellen des Objekts des Prototype-Objekts erstellt die Klasse einen Klon davon und gibt ihn als Prototyp zurück. Die Klonmethode wurde verwendet, um den Prototyp bei Bedarf zu klonen.

// Prototype pattern
public abstract class Prototype implements Cloneable {
    public Prototype clone() throws CloneNotSupportedException{
        return (Prototype) super.clone();
    }
}
	
public class ConcretePrototype1 extends Prototype {
    @Override
    public Prototype clone() throws CloneNotSupportedException {
        return (ConcretePrototype1)super.clone();
    }
}

public class ConcretePrototype2 extends Prototype {
    @Override
    public Prototype clone() throws CloneNotSupportedException {
        return (ConcretePrototype2)super.clone();
    }
}

PHP-Beispiel

// The Prototype pattern in PHP is done with the use of built-in PHP function __clone()

abstract class Prototype
{
    public string $a;
    public string $b;
    
    public function displayCONS(): void
    {
        echo "CONS: {$this->a}\n";
        echo "CONS: {$this->b}\n";
    }
    
    public function displayCLON(): void
    {
        echo "CLON: {$this->a}\n";
        echo "CLON: {$this->b}\n";
    }

    abstract function __clone();
}

class ConcretePrototype1 extends Prototype
{
    public function __construct()
    {
        $this->a = "A1";
        $this->b = "B1";
        
        $this->displayCONS();
    }

    function __clone()
    {
        $this->displayCLON();
    }
}

class ConcretePrototype2 extends Prototype
{
    public function __construct()
    {
        $this->a = "A2";
        $this->b = "B2";
        
        $this->displayCONS();
    }

    function __clone()
    {
        $this->a = $this->a ."-C";
        $this->b = $this->b ."-C";
        
        $this->displayCLON();
    }
}

$cP1 = new ConcretePrototype1();
$cP2 = new ConcretePrototype2();
$cP2C = clone $cP2;

// RESULT: #quanton81

// CONS: A1
// CONS: B1
// CONS: A2
// CONS: B2
// CLON: A2-C
// CLON: B2-C

Python-Beispiel

Python-Version 3.9+

import copy

class Prototype:
    def clone(self):
        return copy.deepcopy(self)

if __name__ == "__main__":
    prototype = Prototype()
    prototype_copy = prototype.clone()
    print(prototype_copy)

Ausgabe:

<__main__.Prototype object at 0x7fae8f4e2940>

Siehe auch

Verweise

Quellen