Einführung in das Prototypmuster

Konzept :

Prototype Pattern ist ein kreatives Designmuster, das die Erstellung neuer Objekte durch Kopieren vorhandener Objekte ermöglicht, ohne auf teure Instanziierungsprozesse angewiesen zu sein. Dieses Muster generiert neue Objekte basierend auf Prototypinstanzen und kann nach Bedarf geändert und angepasst werden.

Merkmale :

  1. Erstellen Sie neue Objekte, indem Sie vorhandene Objekte klonen und so wiederholte Initialisierungsprozesse vermeiden.
  2. Bestehende Prototypen können dynamisch hinzugefügt oder gelöscht werden.
  3. Bietet eine einfache, flexible und effiziente Möglichkeit, mehrere ähnliche Objekte zu erstellen.

Vorteile :

  1. Reduzieren Sie doppelten Code und verbessern Sie die Wiederverwendbarkeit von Code.
  2. Dies vermeidet zeitaufwändige Instanziierungsvorgänge und verbessert die Leistung.
  3. Der Objekterstellungsprozess wird vereinfacht, wodurch das System flexibler wird.

Nachteile :

  1. Sie müssen für jede veränderbare Eigenschaft geeignete Klonmethoden hinzufügen und diese Methoden bei jeder Änderung aktualisieren.
  2. Wenn Zirkelverweise vorhanden sind, müssen Sie auf Endlosschleifen achten, die während des Klonvorgangs auftreten können.

Anwendbare Szenarien :

  1. Das Prototypmuster kann verwendet werden, wenn ein System unabhängig von seinem spezifischen Typ mehrere ähnliche, aber unterschiedliche Konfigurationselemente oder Versionen erstellen muss.
  2. Das Prototypmuster kann verwendet werden, wenn ein System Klasseninstanzen dynamisch laden und konfigurieren muss.

Implementierungsmethode :

Flache Kopie:

Erstellen Sie direkt eine flache Kopie des Zielobjekts und kopieren Sie dabei den Wert des Basisdatentyps und die Adresse des Referenztyps.

Umsetzungsprinzip

  1. Erstellt ein neues Objekt und kopiert alle Feldwerte vom Originalobjekt in das neue Objekt.
  2. Bei Basisdatentypen erfolgt das Kopieren von Werten direkt.
  3. Bei Referenztypen wird nur die Adresse kopiert, ohne dass eine neue Instanz erstellt wird.

Implementierungscode :

import java.util.ArrayList;
import java.util.List;

class Prototype implements Cloneable {
    private int id;
    private String name;
    private List<String> list;

    // 构造函数

    public Prototype(int id, String name, List<String> list) {
        this.id = id;
        this.name = name;
        this.list = list;
    }

    // Getter 和 Setter


    public void setList(List<String> list) {
        this.list = list;
    }

    public List<String> getList(){
        return this.list;
    }

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


public class Main {
    public static void main(String[] args) {
        List<String> originalList = new ArrayList<>();
        originalList.add("item1");
        originalList.add("item2");

        Prototype originalObject = new Prototype(1, "Original", originalList);

        try {
            Prototype clonedObject = originalObject.clone();

            System.out.println(originalObject == clonedObject); // 输出 false
            System.out.println(originalObject.getList() == clonedObject.getList()); // 输出 true

        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

Im obigen Beispiel Prototype implementiert die Klasse  Cloneable die Schnittstelle und überschreibt  clone() die Methoden.  Führen Sie einen flachen Kopiervorgang durch, indem Sie ein neues Klonobjekt aufrufen und zurückgeben.super.clone() 

Als nächstes erstellen wir ein Originalobjekt  originalObjectund führen eine flache Kopie durch, um das geklonte Objekt zu erhalten  clonedObject. Es ist zu beobachten, dass die beiden Objekte nicht gleich sind (da die Referenzadressen unterschiedlich sind), sie jedoch dasselbe Referenztypfeld (d. h. Liste) gemeinsam nutzen, sodass Änderungen an der Liste beide Objekte gleichzeitig beeinflussen.

Probleme
Da die flache Kopie lediglich die Referenz kopiert und keine neue unabhängige Kopie erstellt, kann es zu folgenden Problemen kommen:

  1. Wenn sich der Typ der kopierten Referenz ändert, ist auch die geklonte Kopie davon betroffen.
  2. Wenn sich in einer mehrstufigen verschachtelten Struktur ein bestimmtes Attribut ändert, ändert sich auch das entsprechende Attribut in der geklonten Kopie.

Tiefe Kopie:

Erstellen Sie eine tiefe Kopie des Zielobjekts und kopieren Sie dabei die Werte der Basisdatentypen und das gesamte Objekt der Referenztypen.

Umsetzungsprinzip

  1. Erstellt ein neues Objekt und kopiert alle Feldwerte vom Originalobjekt in das neue Objekt.
  2. Bei Basisdatentypen erfolgt das Kopieren von Werten direkt.
  3. Bei Referenztypen wird beim Klonvorgang eine völlig neue Kopie des Referenztyps erstellt und die Kopie dem neuen Objekt zugewiesen.

Code implementieren

import java.util.ArrayList;
import java.util.List;

class Prototype implements Cloneable {
    private int id;
    private String name;
    private List<String> list;

    // 构造函数

    public Prototype(int id, String name, List<String> list) {
        this.id = id;
        this.name = name;
        this.list = list;
    }

    // Getter 和 Setter


    public List<String> getList() {
        return list;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    @Override
    public Prototype clone() throws CloneNotSupportedException {
        Prototype clonedObject = (Prototype) super.clone();
        clonedObject.list = new ArrayList<>(this.list); // 创建全新副本
        return clonedObject;
    }
}


public class Main {
    public static void main(String[] args) {
        List<String> originalList = new ArrayList<>();
        originalList.add("item1");
        originalList.add("item2");

        Prototype originalObject = new Prototype(1, "Original", originalList);

        try {
            Prototype clonedObject = originalObject.clone();

            System.out.println(originalObject == clonedObject); // 输出 false
            System.out.println(originalObject.getList() == clonedObject.getList()); // 输出 false

        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

Im obigen Beispiel Prototype implementiert die Klasse  Cloneable die Schnittstelle und überschreibt  clone() die Methoden. Führen Sie einen flachen Kopiervorgang durch , indem Sie aufrufen  super.clone() , und führen Sie dann einen tiefen Klon des Referenztypfelds durch.

Als nächstes erstellen wir ein Originalobjekt  originalObjectund führen eine tiefe Kopie durch, um das geklonte Objekt zu erhalten  clonedObject. Es ist zu beobachten, dass die beiden Objekte völlig unabhängig sind (da die Referenzadressen unterschiedlich sind) und ihre Listen ebenfalls unabhängige Kopien sind, sodass Änderungen an den Listen sich nicht gegenseitig beeinflussen.

Obwohl Deep Copy die Probleme löst, die flaches Kopieren verursachen kann, weist es auch die folgenden Probleme auf:

  1. Wenn der kopierte Referenztyp auch andere Referenztypen enthält, muss ein Deep-Cloning-Vorgang rekursiv durchgeführt werden.
  2. Einige Klassen sind möglicherweise nicht serialisierbar oder deserialisierbar, was ein tiefes Kopieren mit Standardmethoden verhindert.

Prototypenregister

Verwenden Sie den Prototyp-Manager zum Speichern und Abrufen von Prototypinstanzen. Sie können den entsprechenden Prototyp anhand des Namens oder der Kennung finden und klonen.

Umsetzungsprinzip

  1. Erstellen Sie eine Prototyp-Registrierungsklasse (normalerweise ein Singleton), um mehrere Prototypobjekte unterschiedlichen Typs zu speichern und zu verwalten.
  2. Fügen Sie der Registrierung eine Methode hinzu, um ein Prototypobjekt mit einem angegebenen Namen zur Registrierung hinzuzufügen, zu löschen oder abzurufen.
  3. Wenn der Client ein neues Objekt erstellen muss, kann er die entsprechende Klonkopie erhalten, indem er die Methode mit dem entsprechenden Namen in der Registrierung aufruft.

Code implementieren

import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
import java.util.Map;

class Prototype implements Cloneable {
    private int id;
    private String name;
    private List<String> list;

    // 构造函数

    public Prototype(int id, String name, List<String> list) {
        this.id = id;
        this.name = name;
        this.list = list;
    }

    // Getter 和 Setter
    public void setName(String name){
        this.name = name;
    }

    public String getName() {
        return this.name;
    }
    public List<String> getList() {
        return list;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    @Override
    public Prototype clone() throws CloneNotSupportedException {
        Prototype clonedObject = (Prototype) super.clone();
        clonedObject.list = new ArrayList<>(this.list); // 创建全新副本
        return clonedObject;
    }
}

class PrototypeRegistry {
    private static PrototypeRegistry instance;
    private Map<String, Prototype> prototypes;

    private PrototypeRegistry() {
        prototypes = new HashMap<>();
    }

    public static synchronized PrototypeRegistry getInstance() {
        if (instance == null) {
            instance = new PrototypeRegistry();
        }
        return instance;
    }

    public void addPrototype(String name, Prototype prototype) {
        prototypes.put(name, prototype);
    }

    public void removePrototype(String name) {
        prototypes.remove(name);
    }

    public Prototype getPrototype(String name) throws CloneNotSupportedException {
        Prototype prototype = prototypes.get(name);
        if (prototype != null) {
            return prototype.clone();
        }
        return null;
    }
}

public class Main {
    public static void main(String[] args) {
// 创建原始对象并添加到注册表
        List<String> originalList = new ArrayList<>();
        originalList.add("item1");
        originalList.add("item2");

        Prototype originalObject = new Prototype(1, "Original", originalList);

        PrototypeRegistry registry = PrototypeRegistry.getInstance();
        registry.addPrototype("object1", originalObject);

// 从注册表获取副本并进行修改
        try {
            Prototype clonedObject = registry.getPrototype("object1");

            if (clonedObject != null) {
                clonedObject.setName("Cloned");

                System.out.println(originalObject.getName()); // 输出 "Original"
                System.out.println(clonedObject.getName()); // 输出 "Cloned"

                System.out.println(originalObject.getList() == clonedObject.getList()); // 输出 false
            }
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

    }
}

Basierend auf dem Deep-Copy-Implementierungscode haben wir PrototypeRegistry eine Klasse hinzugefügt, um die Funktion der Prototyp-Registrierung zu implementieren. Stellen Sie sicher, dass nur eine Registrierungsinstanz vorhanden ist, indem Sie das Singleton-Muster und  HashMap das gespeicherte Prototypobjekt verwenden. Anschließend wird ein primitives Objekt erstellt  originalObject und der Registrierung hinzugefügt. Rufen Sie dann  getPrototype() eine Kopie aus der Registrierung ab, indem Sie eine Methode aufrufen und die Kopie ändern. Es ist zu beobachten, dass die beiden Objekte unabhängig sind und Änderungen an einem Objekt keine Auswirkungen auf das andere Objekt haben.

Serialisierung und Deserialisierung

Serialisiert ein Objekt in einen Bytestream und deserialisiert ihn dann in ein neues Objekt.

Umsetzungsprinzip

  1. Das geklonte Objekt muss die Serializable-Schnittstelle implementieren.
  2. Serialisierung: Verwenden Sie ObjectOutputStream, um ein Objekt in einen Ausgabestream zu schreiben und es in ein Byte-Array zu konvertieren.
  3. Deserialisierung: Verwenden Sie einen ObjectInputStream, um ein Byte-Array aus einem Eingabestream zu lesen und es zurück in das ursprüngliche Objekt zu konvertieren.

Code implementieren

import java.io.*;
import java.util.ArrayList;
import java.util.List;

class Prototype implements Serializable {
    private int id;
    private String name;
    private List<String> list;

    // 构造函数

    public Prototype(int id, String name, List<String> list) {
        this.id = id;
        this.name = name;
        this.list = list;
    }

    // Getter 和 Setter


    public List<String> getList() {
        return list;
    }

    public Prototype deepClone() {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(this);

            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            return (Prototype) ois.readObject();

        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }

        return null;
    }
}

public class Main {
    public static void main(String[] args) {
        List<String> originalList = new ArrayList<>();
        originalList.add("item1");
        originalList.add("item2");

        Prototype originalObject = new Prototype(1, "Original", originalList);

        Prototype clonedObject = originalObject.deepClone();

        System.out.println(originalObject == clonedObject); // 输出 false
        System.out.println(originalObject.getList() == clonedObject.getList()); // 输出 false

    }
}

Im obigen Beispiel Prototype implementiert die Klasse  Serializable die Schnittstelle und stellt eine  deepClone() Methode zum Erstellen einer tiefen Kopie bereit. Bei dieser Methode wird das Objekt zunächst in den Ausgabestream geschrieben und in ein Byte-Array konvertiert. Anschließend wird das Byte-Array aus dem Eingabestream gelesen und zurück in das Objekt konvertiert. Dann erstellen wir ein Originalobjekt  originalObject und  deepClone() erstellen eine tiefe Kopie, indem wir eine Methode aufrufen, um das Kopieobjekt abzurufen  clonedObject. Es ist zu beobachten, dass die beiden Objekte unabhängig sind (da die Referenzadressen unterschiedlich sind) und ihre Listen ebenfalls unabhängige Kopien sind, sodass Änderungen an den Listen sich nicht gegenseitig beeinflussen. Es ist zu beachten, dass zusätzliche Maßnahmen zur Behandlung dieser Felder ergriffen werden müssen, wenn die geklonte Klasse Felder enthält, die nicht serialisiert oder deserialisiert werden können (z. B. Threads, Dateihandles usw.).

Guess you like

Origin blog.csdn.net/aidscooler/article/details/132707425