"Design Patterns" Study Notes 6 - Prototype Patterns

definition

In the process of development, you may encounter a situation where you need to create multiple objects for the same class, and most of the properties of these multiple objects have the same value. At this time, if each object is set one by one, it will appear that there is a lot of code repetition, and the prototype pattern can be used to solve this scenario and simplify the code.
The definition in the Prototype Pattern Reference Book is as follows:

Prototype Pattern: Use prototype instances to specify the kinds of objects to create, and create new objects by copying these prototypes. Prototype pattern is an object creation pattern

understand

The prototype pattern can be simply understood that the achievement is clone, copy, and make a copy of an object.
In the process of my previous development, to give the properties of one object to another object, there are usually two ways. For a better explanation, let's define an entity class first:

package patterntest.prototypepattern;

/**
 * class teacher entity class
 *
 * @author tzx
 * @date December 4, 2017
 */

public class HeadmasterModel {
    /**
     * teacher name
     */
    private String name;
    /**
     * Teacher's age
     */
    private int age;
    /**
     * Manage class names
     */
    private String className;

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    @Override
    public String toString() {
        return "HeadmasterModel [name=" + name + ", age=" + age + ", className=" + className + "]";
    }

}

practice one

To give a reference to an object directly to another object, the code is as follows:

HeadmasterModel headmaster = new HeadmasterModel();
headmaster.setAge(32);
headmaster.setClassName("Class 61");
headmaster.setName("张威");
HeadmasterModel headmaster2=headmaster;

Here first there is a headmaster object, and then directly assign the headmaster object to headmaster2.
Students who know a little about java should know that this assignment actually gives a reference to the object. If I change the attribute value of headmaster2, the headmaster will also change, which means that these two objects are actually the same object.
Then the problem with this situation is obvious, if I only want to change the value of headmaster2 without changing the value of headmaster, it seems impossible.

Practice 2

Then in order to solve the above problem, if you want headmaster2 to use some attribute values ​​of headmaster, but also want to change other parts of headmaster2 without affecting headmaster, you can do this:

HeadmasterModel headmaster = new HeadmasterModel();
headmaster.setAge(32);
headmaster.setClassName("Class 61");
headmaster.setName("张威");
student.setHeadmaster(headmaster);
HeadmasterModel headmaster2 = new HeadmasterModel();
headmaster2.setAge(headmaster.getAge());
headmaster2.setName(headmaster.getName());
headmaster2.setClassName("Class 62");

The above code actually creates two objects, then gets the value of the first object, and then sets it to the second object.
In this case, the consistency of some attributes of the two objects is achieved, and if the value of headmaster2 is changed, it will not affect the headmaster, which solves the problem of the first method.
However, the problem with this approach is also obvious. If the various attributes of the object need to be reused multiple times, then code like this needs to be used in every reused place. headmaster2.setAge(headmaster.getAge())The more attributes are reused, the more The more repetitive code, because this way of writing is on the consumer side.

Prototype Pattern Basics

Based on the problems of the above two methods, the prototype mode is ready to emerge. In order to reduce duplication, we need to move the code that reuses attributes from the consumer side to the server side. The server side here is actually the specific implementation class.
The specific method is to add a method to the entity class for external calls. This method will reuse the properties of the current object and assign it to another object of the same type, and then return back. This method is usually called clone, like this:

public HeadmasterModel clone() {
    HeadmasterModel headmasterModel = new HeadmasterModel();
    headmasterModel.setAge(this.age);
    headmasterModel.setClassName(this.className);
    headmasterModel.setName(this.name);
    return headmasterModel;
}

This method looks the same as the processing logic of the second method above, but because it is written in the entity class, it is only necessary to call the clone method with the original object when it is used on the specific consumer side to get the same object as the original object. A new object of properties.
Because a new object is created in the clone method, it is also different from the original object.

Prototype mode promotion

The above implementation is relatively simple, but there are actually simpler ones.
In the above example, we still write the code by ourselves to take out the property value from the original object and assign it to the new object, but in fact, the clone method is implemented in the Object class of java. Subclasses can use the clone method to create new objects.
However, it should be noted here that the object that needs to use the clone method of Object must implement the Cloneable interface, otherwise an exception will be thrown

Therefore, the optimized clone method can be changed to the following:

public HeadmasterModel clone() {
    HeadmasterModel headmasterMode = null;
    try {
        headmasterMode = (HeadmasterModel) super.clone();
    } catch (CloneNotSupportedException e) {
        // e.printStackTrace ();
    }
    return headmasterMode;
}

At the same time, you need to add the implementation of the Cloneable interface to the class:

public class HeadmasterModel implements Cloneable

Prototype mode in depth

Through the above series of analysis, using the prototype mode to clone a new object with the same attribute value as the original object is basically realized, but there is still a problem. For the attributes of non-basic types, the cloning method above actually clones Just a quote.
For example, we have another student class here, the class teacher is one of the attributes of the student class, and the student class also has the clone method:

package patterntest.prototypepattern;

/**
 * Student entity class
 *
 * @author tzx
 * @date December 4, 2017
 */
public class StudentModel {
    /**
     * student name
     */
    private String name;
    /**
     *Student age
     */
    private int age;
    /**
     * Class name
     */
    private String className;
    /**
     * gender
     */
    private String sex;
    /**
     * head teacher
     */
    private HeadmasterModel headmaster;

    public StudentModel clone() {
        StudentModel studentModel = null;
        try {
            studentModel = (StudentModel) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace ();
        }
        return studentModel;
    }

    ...get, set and toString methods omitted

}

Then generate a student object and clone a new object based on that:

public static void main(String[] args) {
    StudentModel student = new StudentModel();
    student.setName("Zhang San");
    student.setAge(12);
    student.setClassName("Class 61");
    student.setSex("男");
    HeadmasterModel headmaster = new HeadmasterModel();
    headmaster.setAge(32);
    headmaster.setClassName("Class 61");
    headmaster.setName("张威");
    student.setHeadmaster(headmaster);
    System.out.println(student.getHeadmaster().hashCode());
    System.out.println(student.clone().getHeadmaster().hashCode());
}

By running the above code, you can see that the hashcode of the headmaster property of the student object before cloning and the hashcode of the headmaster property of the cloned student object are exactly the same, that is to say, the two headmaster objects use the same reference, but the actual object is still the same. the same one.
Then if we change the headmaster attribute of the cloned student object at this time, it is obvious that the headmaster attribute of the object before cloning will also change, which is obviously unreasonable.
Then there are two concepts of prototype mode: shallow clone and deep clone.

Shallow clone is the example above. Although the cloned object is new, the non-basic type properties in the object are still old.
Deep cloning means that not only the cloned object is new, but also the properties of the new object are also new objects.
Since the super.clone() method calls the clone method of Object to achieve shallow clone, so deep clone cannot use this method. Deep clone needs to implement the Serializable interface to serialize the object, and generate a new object through the io stream. The specific code as follows:

public class StudentModel implements Serializable {

    private static final long serialVersionUID = 1L;
    /**
     * student name
     */
    private String name;
    /**
     *Student age
     */
    private int age;
    /**
     * Class name
     */
    private String className;
    /**
     * gender
     */
    private String sex;
    /**
     * head teacher
     */
    private HeadmasterModel headmaster;

    public StudentModel clone() {
        StudentModel studentModel = null;
        ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
        try {
            ObjectOutputStream objectOutputStream=new ObjectOutputStream(byteArrayOutputStream);
            objectOutputStream.writeObject(this);

            ByteArrayInputStream byteArrayInputStream=new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
            ObjectInputStream objectInputStream=new ObjectInputStream(byteArrayInputStream);
            studentModel=(StudentModel)objectInputStream.readObject();
        } catch (ClassNotFoundException e) {
            e.printStackTrace ();
        } catch (IOException e) {
            e.printStackTrace ();
        }
        return studentModel;
    }

    ...get, set and toString methods omitted

}

At this time, if you run the above test main method again, you will find that an exception will be thrown. The reason is that not only the cloned object needs to implement the Serializable interface, but also the attribute object that needs to be deeply cloned in the cloned object needs to be implemented when it is defined. Serializable interface:

public class HeadmasterModel implements Cloneable, Serializable 

Then run the test method again, and you will find that not only the cloned object is different from the original object, but also the non-basic type attributes of the cloned object are no longer the original attribute object. At this time, if you modify the properties of the cloned object, it will no longer affect the object before the clone.

gist

According to the above analysis, we can see that the main points of the prototype mode are as follows:
1. An object is required as a prototype
2. You can implement the clone method by yourself to realize the reuse of the prototype, but if it is a shallow clone, use the clone method of Object. Concise
3. Deep cloning must be implemented by itself
4. The clone method of Object must implement the Cloneable interface
5. When implementing deep cloning, it must be ensured that both the cloned object and the attribute object implement the serializable interface Serializable
6. For better expansion and more conciseness code, consider implementing the clone method in the parent abstract class

Summarize

Through the above analysis and examples, it can be found that the main advantage of the prototype pattern is that when other objects of the same type need to reuse a large number of properties of an object, the code can be more concise and the repetition of the code can be greatly reduced.
The disadvantages of this mode are also obvious. First, the cloned object needs to have a clone method, and the Cloneable and Serializable interfaces need to be implemented according to the situation; secondly, each non-basic type attribute object of the deep cloned object needs to be implemented. All must implement the Serializable interface, which increases the amount of code to some extent.

The demo source code can be downloaded from github: https://github.com/tuzongxun/mypattern

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325447075&siteId=291194637