The prototype pattern of JAVA and pattern

Posted by raw100 on Mon, 20 Dec 2021 17:16:28 +0100

The beginning of Dr. Yan Hong's Book JAVA and patterns describes the Prototype pattern as follows:

The prototype pattern belongs to the creation pattern of the object. By giving a prototype object to indicate the type of all created objects, and then create more objects of the same type by copying the prototype object. This is the purpose of the model selection.

Structure of prototype pattern

The prototype pattern requires an object to implement an interface that can "clone" itself, so that a new instance can be created by copying an instance object itself. In this way, when you create a new object through a prototype instance, you no longer need to care about the type of the instance itself. As long as you implement the method of cloning yourself, you can obtain a new object through this method instead of creating it through new.

There are two forms of prototype pattern: (1) simple form and (2) registration form. These two forms are only different implementations of prototype pattern.

Prototype pattern in simple form

This form involves three roles:

(1) client role: the client class makes a request to create an object.

(2) Abstract prototype role: This is an abstract role, which is usually implemented by a java interface or Java abstract class. This role gives the interfaces required for all concrete prototype classes.

(3) Concrete Prototype role: the copied object. This role needs to implement the interface required by the Abstract prototype role.

source code

Abstract prototype role

public interface Prototype{
    /**
     * Methods of cloning itself
     * @return An object cloned from itself
     */
    public Object clone();
}

Specific prototype role

public class ConcretePrototype1 implements Prototype {
    public Prototype clone(){
        //The simplest clone is to create a new object of its own. Since there is no attribute, the value will no longer be copied
        Prototype prototype = new ConcretePrototype1();
        return prototype;
    }
}
public class ConcretePrototype2 implements Prototype {
    public Prototype clone(){
        //The simplest clone is to create a new object of its own. Since there is no attribute, the value will no longer be copied
        Prototype prototype = new ConcretePrototype2();
        return prototype;
    }
}

Client role

public class Client {
    /**
     * Hold the prototype interface object you need to use
     */
    private Prototype prototype;
    /**
     * Construct method and pass in the prototype interface object to be used
     */
    public Client(Prototype prototype){
        this.prototype = prototype;
    }
    public void operation(Prototype example){
        //Objects that need to create prototype interfaces
        Prototype copyPrototype = prototype.clone();
        
    }
}

Prototype model of registration form

  

As the second form of prototype pattern, it adds a prototype manager role. The role is to create objects of specific prototype classes and record each created object.

source code

Abstract prototype role

public interface Prototype{
    public Prototype clone();
    public String getName();
    public void setName(String name);
}

Specific prototype role

public class ConcretePrototype1 implements Prototype {
    private String name;
    public Prototype clone(){
        ConcretePrototype1 prototype = new ConcretePrototype1();
        prototype.setName(this.name);
        return prototype;
    }
    public String toString(){
        return "Now in Prototype1 , name = " + this.name;
    }
    @Override
    public String getName() {
        return name;
    }

    @Override
    public void setName(String name) {
        this.name = name;
    }
}
public class ConcretePrototype2 implements Prototype {
    private String name;
    public Prototype clone(){
        ConcretePrototype2 prototype = new ConcretePrototype2();
        prototype.setName(this.name);
        return prototype;
    }
    public String toString(){
        return "Now in Prototype2 , name = " + this.name;
    }
    @Override
    public String getName() {
        return name;
    }

    @Override
    public void setName(String name) {
        this.name = name;
    }
}

The prototype manager role maintains an aggregation as the registration of all prototype objects. This role provides the necessary methods for the outside world to add new prototype objects and obtain the registered prototype objects.

public class PrototypeManager {
    /**
     * It is used to record the correspondence between the prototype number and the prototype instance
     */
    private static Map<String,Prototype> map = new HashMap<String,Prototype>();
    /**
     * Privatize the construction method to avoid external instance creation
     */
    private PrototypeManager(){}
    /**
     * Add or modify a prototype registration to the prototype manager
     * @param prototypeId Prototype number
     * @param prototype    Prototype instance
     */
    public synchronized static void setPrototype(String prototypeId , Prototype prototype){
        map.put(prototypeId, prototype);
    }
    /**
     * Delete a prototype registration from the prototype manager
     * @param prototypeId Prototype number
     */
    public synchronized static void removePrototype(String prototypeId){
        map.remove(prototypeId);
    }
    /**
     * Get the prototype instance corresponding to a prototype number
     * @param prototypeId    Prototype number
     * @return    Prototype instance corresponding to prototype number
     * @throws Exception    If the instance corresponding to the prototype number does not exist, an exception is thrown
     */
    public synchronized static Prototype getPrototype(String prototypeId) throws Exception{
        Prototype prototype = map.get(prototypeId);
        if(prototype == null){
            throw new Exception("The prototype you want to get has not been registered or has been destroyed");
        }
        return prototype;
    }
}

Client role

public class Client {
    public static void main(String[]args){
        try{
            Prototype p1 = new ConcretePrototype1();
            PrototypeManager.setPrototype("p1", p1);
            //Get the prototype to create the object
            Prototype p3 = PrototypeManager.getPrototype("p1").clone();
            p3.setName("Zhang San");
            System.out.println("First instance:" + p3);
            //Someone dynamically switched the implementation
            Prototype p2 = new ConcretePrototype2();
            PrototypeManager.setPrototype("p1", p2);
            //Retrieve the prototype to create the object
            Prototype p4 = PrototypeManager.getPrototype("p1").clone();
            p4.setName("Li Si");
            System.out.println("Second instance:" + p4);
            //Someone cancelled the prototype
            PrototypeManager.removePrototype("p1");
            //Get the prototype again to create the object
            Prototype p5 = PrototypeManager.getPrototype("p1").clone();
            p5.setName("Wang Wu");
            System.out.println("Third example:" + p5);
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

Comparison of two forms

The prototype models of simple form and registration form have their own advantages and disadvantages.

If the number of prototype objects to be created is small and fixed, the first form can be adopted. In this case, the reference of the prototype object can be saved by the client itself.

If the number of prototype objects to be created is not fixed, you can take the second form. In this case, the client does not save the reference to the prototype object, and the task is handed over to the administrator object. Before copying a prototype object, the client can check whether the administrator object already has a prototype object that meets the requirements. If yes, you can get this object reference directly from the administrator class; If not, the client needs to copy the prototype object itself.

Cloning method in Java

All classes in Java are from Java Lang. Object class inherits from lang. Object class, and the Object class provides the protected Object clone() method to copy objects. Of course, subclasses can replace this method to provide replication methods to meet their own needs. A basic problem with Object replication is that objects usually have references to other objects. When you use the clone () method of the Object class to copy an Object, the reference of the Object to other objects will also be copied

The clonable interface provided by the Java language only plays a role in informing the Java virtual machine that the clone() method can be safely used on this class at run time. You can get a copy of an Object by calling the clone() method. Because the Object class itself does not implement the clonable interface, if the class under consideration does not implement the clonable interface, calling the clone() method will throw a clonnotsupportedexception exception.

Conditions for cloning

The clone() method copies a copy of the object and returns it to the caller. The meaning of "copy" is related to how the clone() method is implemented. Generally speaking, the clone() method satisfies the following description:

(1) for any object x, there is: X. clone()= x. In other words, the cloned object is not the same object as the original object.

(2) for any object x, there is: x.clone() Getclass() = = x.getclass(), in other words, the cloned object is of the same type as the original object.

(3) if the equals() method of object x defines it appropriately, then x.clone() Equals (x) should be true.

In the JAVA language API, all classes that provide the clone() method meet the above conditions. The designer of JAVA language should also abide by three conditions when designing his own clone() method. Generally speaking, the first two of the above three conditions are required, and the third is optional.

Shallow cloning and deep cloning

Whether you implement the cloning method yourself or adopt the cloning method provided by Java, there is a problem of shallow cloning and deep cloning.

  • Shallow cloning

It is only responsible for cloning the data passed by value (such as basic data type and String type) without copying the object it references. In other words, all references to other objects still point to the original object.

  • Deep cloning

In addition to shallow cloning of values to be cloned, it is also responsible for cloning data of reference types. Variables that reference other objects will point to the copied new objects instead of the original referenced objects. In other words, deep cloning copies all the objects referenced by the object to be copied, and this copy of the referenced object is called indirect copy.

It is not easy to determine the depth of deep cloning. When you decide to copy an object by deep cloning, you must decide whether to use shallow cloning or continue to use deep cloning for indirectly copied objects. Therefore, when taking deep cloning, you need to decide how deep it is. In addition, in the process of deep cloning, circular references are likely to occur, which must be handled carefully.

Deep cloning using serialization

The process of writing objects to the stream is a serialization process; The process of reading objects from the stream is called deserialization. It should be noted that what is written to the stream is a copy of the object, and the original object still exists in the JVM.

To deeply clone an object in the Java language, you can often first make the object implement the Serializable interface, then write the object (actually just a copy of the object) to a stream (serialization), and then read it back from the stream (deserialization), so as to reconstruct the object.

    public  Object deepClone() throws IOException, ClassNotFoundException{
        //Write object to stream
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);
        //Read it back from the stream
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return ois.readObject();
    }

The premise of this is that the object and all referenced objects inside the object are serializable. Otherwise, you need to carefully investigate whether those non serializable objects can be set to transient, so as to exclude them from the replication process.

Shallow cloning is obviously easier to implement than deep cloning, because all classes in the Java language inherit a clone() method, which is a formal shallow cloning.

Some objects, such as thread objects or Socket objects, cannot be simply copied or shared. Whether shallow cloning or deep cloning is used, as long as such indirect objects are involved, the indirect objects must be set to transient instead of copying; Or the program can create a similar object and use it as a copy.

Sun Dasheng's external body magic

What would happen if sun Dasheng's physical and external skills were implemented using prototype patterns in the Java language? First, the greatest sage, the great sage, plays the role of customer. Qi Tian Da Sheng holds an instance of monkey, and the monkey is the master of Da Sheng. Monkey class has clone() method inherited from java.lang.Object. Therefore, you can copy a monkey instance by calling this cloning method.

Sun Dasheng himself uses the greatestsage class to represent

public class TheGreatestSage {
    private Monkey monkey = new Monkey();
    
    public void change(){
        //Clone the great saint
        Monkey copyMonkey = (Monkey)monkey.clone();
        System.out.println("The birthday of the great saint is:" + monkey.getBirthDate());
        System.out.println("The birthday of the cloned Mahatma is:" + monkey.getBirthDate());
        System.out.println("Is the great saint the same object as the cloned great saint " + (monkey == copyMonkey));
        System.out.println("Is the golden cudgel held by the great sage the same object as the golden cudgel held by the cloned great sage? " + (monkey.getStaff() == copyMonkey.getStaff()));
    }
    
    public static void main(String[]args){
        TheGreatestSage sage = new TheGreatestSage();
        sage.change();
    }
}

The great sage is represented by the Monkey class, which plays a specific prototype role:

public class Monkey implements Cloneable {
    //height
    private int height;
    //weight
    private int weight;
    //birthday
    private Date birthDate;
    //Golden cudgel
    private GoldRingedStaff staff;
    /**
     * Constructor
     */
    public Monkey(){
        this.birthDate = new Date();
        this.staff = new GoldRingedStaff();
    }
    /**
     * Cloning method
     */
    public Object clone(){
        Monkey temp = null;
        try {
            temp = (Monkey) super.clone();
        } catch (CloneNotSupportedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            return temp;
        }
    }
    public int getHeight() {
        return height;
    }
    public void setHeight(int height) {
        this.height = height;
    }
    public int getWeight() {
        return weight;
    }
    public void setWeight(int weight) {
        this.weight = weight;
    }
    public Date getBirthDate() {
        return birthDate;
    }
    public void setBirthDate(Date birthDate) {
        this.birthDate = birthDate;
    }
    public GoldRingedStaff getStaff() {
        return staff;
    }
    public void setStaff(GoldRingedStaff staff) {
        this.staff = staff;
    }
    
}

The great sage also holds an instance of the golden cudgel. The golden cudgel class GoldRingedStaff:

public class GoldRingedStaff {
    private float height = 100.0f;
    private float diameter = 10.0f;
    /**
     * Growth behavior, doubling the length and radius of each call
     */
    public void grow(){
        this.diameter *= 2;
        this.height *= 2;
    }
    /**
     * Reduce the behavior by halving the length and radius of each call
     */
    public void shrink(){
        this.diameter /= 2;
        this.height /= 2;
    }
}

When the greatestsage class is run, the great sage object is created first, and then the great sage object is shallow cloned. The information printed by the program during operation is as follows:

It can be seen that, firstly, the copied Mahatma has the same birthDate as the original Mahatma, and the Mahatma objects are not equal, which indicates that they are clonal; Secondly, the golden cudgel held by the copied great sage is the same object as the golden cudgel held by the original great sage. This shows that the golden cudgel held by the two is one, not two.

As mentioned earlier, it inherits from Java The clone() method of lang. object class is shallow cloning. In other words, all the references to the golden cudgel held by all the incarnations of Qi Tian Da Sheng point to one object, which is inconsistent with the description in journey to the West. To correct this, you need to consider using deep cloning.

In order to achieve deep cloning, all objects that need to be copied need to implement Java io. Serializable interface.

Source code of sun Dasheng:

public class TheGreatestSage {
    private Monkey monkey = new Monkey();
    
    public void change() throws IOException, ClassNotFoundException{
        Monkey copyMonkey = (Monkey)monkey.deepClone();
        System.out.println("The birthday of the great saint is:" + monkey.getBirthDate());
        System.out.println("The birthday of the cloned Mahatma is:" + monkey.getBirthDate());
        System.out.println("Is the great saint the same object as the cloned great saint " + (monkey == copyMonkey));
        System.out.println("Is the golden cudgel held by the great sage the same object as the golden cudgel held by the cloned great sage? " + (monkey.getStaff() == copyMonkey.getStaff()));
    }
    
    public static void main(String[]args) throws IOException, ClassNotFoundException{
        TheGreatestSage sage = new TheGreatestSage();
        sage.change();
    }
}

In the Monkey class of the great sage, there are two cloning methods. One is clone(), that is, shallow cloning; The other is deep clone(), that is, deep clone. In the deep cloning method, the Mahatma object (a copy) is serialized and then deserialized. The deserialized object becomes the result of a deep cloning.

public class Monkey implements Cloneable,Serializable {
    //height
    private int height;
    //weight
    private int weight;
    //birthday
    private Date birthDate;
    //Golden cudgel
    private GoldRingedStaff staff;
    /**
     * Constructor
     */
    public Monkey(){
        this.birthDate = new Date();
        staff = new GoldRingedStaff();
    }
    /**
     * Cloning method
     */
    public Object clone(){
        Monkey temp = null;
        try {
            temp = (Monkey) super.clone();
        } catch (CloneNotSupportedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            return temp;
        }
    }
    public  Object deepClone() throws IOException, ClassNotFoundException{
        //Write object to stream
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);
        //Read it back from the stream
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return ois.readObject();
    }
    public int getHeight() {
        return height;
    }
    public void setHeight(int height) {
        this.height = height;
    }
    public int getWeight() {
        return weight;
    }
    public void setWeight(int weight) {
        this.weight = weight;
    }
    public Date getBirthDate() {
        return birthDate;
    }
    public void setBirthDate(Date birthDate) {
        this.birthDate = birthDate;
    }
    public GoldRingedStaff getStaff() {
        return staff;
    }
    public void setStaff(GoldRingedStaff staff) {
        this.staff = staff;
    }
    
}

As you can see, The great sage holds a golden cudgel (GoldRingedStaff). In the copy of the great sage, this golden cudgel instance is a copy of the golden cudgel object held by the original great sage object. When the great sage object is serialized and deserialized, the golden cudgel object held by it is also serialized and deserialized, which makes the copied great sage's golden cudgel and the gold hoop held by the original great sage object The bar object is two independent objects.

public class GoldRingedStaff implements Serializable{
    private float height = 100.0f;
    private float diameter = 10.0f;
    /**
     * Growth behavior, doubling the length and radius of each call
     */
    public void grow(){
        this.diameter *= 2;
        this.height *= 2;
    }
    /**
     * Reduce the behavior by halving the length and radius of each call
     */
    public void shrink(){
        this.diameter /= 2;
        this.height /= 2;
    }
}

Operation results:

It can be seen from the running results that the golden cudgel of the great sage and the golden cudgel outside his body are different objects. This is because deep cloning is used to copy all the objects referenced by the great saint, including the golden cudgel.

  

Advantages of prototype mode

The prototype pattern allows you to dynamically change specific implementation types at run time. During the operation of the prototype pattern, the customer can register the implementation type that conforms to the prototype interface, or dynamically change the specific implementation type. It seems that the interface has not changed, but in fact, another class instance is running. Because cloning a prototype is like instantiating a class.

Disadvantages of prototype mode

The main disadvantage of the prototype pattern is that each class must be equipped with a cloning method. It is not very difficult for a new class to consider the function of the class in a comprehensive way when it is equipped with the cloning method, but not necessarily easy for an existing class, especially when a class reference does not support serialized indirect objects, or the reference contains a circular structure.

Topics: Java Hibernate Spring