Explore the prototype pattern of java design pattern

Posted by Sinister747 on Sun, 16 Jan 2022 15:21:46 +0100

1. Analyze the disadvantages of traditional methods

Let's first look at the problem of cloned sheep:
Now there is a sheep tom, whose name is tom, age is 1, and color is white. Please write a program to create 10 sheep with exactly the same attributes as tom sheep.

Using traditional methods:
1. Create entity class

package com.xzz;

public class Sheep {
	private String name;
	private int age;
	private String color;

	public Sheep() {
	}

	public Sheep(String name, int age, String color) {
		super();
		this.name = name;
		this.age = age;
		this.color = color;
	}
	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 getColor() {
		return color;
	}
	public void setColor(String color) {
		this.color = color;
	}
	@Override
	public String toString() {
		return "Sheep [name=" + name + ", age=" + age + ", color=" + color + "]";
	}
	
	
}

2. Simulate the client with a class

package com.xzz;

public class Client {
	public static void main(String[] args) {
		//Traditional methods
		Sheep sheep = new Sheep("tom", 1, "white");
		
		Sheep sheep2 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
		Sheep sheep3 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
		Sheep sheep4 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
		Sheep sheep5 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
		//....
		
		System.out.println(sheep);
		System.out.println(sheep2);
		System.out.println(sheep3);
		System.out.println(sheep4);
		System.out.println(sheep5);
		//...
	}

}

As like as two peas can be seen from the above code, if we want to create objects with exactly the same properties, we first need to new an object and assign the values by constructing methods. Then, when we create objects, we get the corresponding attribute values by getter method as the parameters to create other objects. When we want to create many such objects, it is undoubtedly very troublesome, and the data is still static, which is not conducive to later modification and maintenance.

Advantages and disadvantages of traditional methods
1) The advantages are easy to understand, simple and easy to operate.
2) When creating a new object, you always need to retrieve the properties of the original object. If the created object is complex, the efficiency is low.
3) It is always necessary to reinitialize the object rather than dynamically obtain the running state of the object, which is not flexible enough.

Improvement ideas:
In Java, the Object class is the root class of all classes. The Object class provides a clone() method, which can copy a Java Object, but the Java class that needs to implement clone must implement an interface clonable, which indicates that the class can copy and has the ability to copy = > prototype mode

2. Prototype mode

1) Prototype pattern refers to specifying the type of objects to be created with prototype instances, and creating new objects by copying these prototypes.
2) Prototype pattern is a creative design pattern that allows one object to create another customizable object without knowing the details of how to create it.
3) The working principle is: by passing a prototype object to the object to be created, the object to be created is created by requesting the prototype object to copy itself, that is, the object clone()

Schematic structure diagram of prototype pattern - uml class diagram:

Description of schematic structure diagram:
1)Prototype: a prototype class that declares an interface that clones itself
2) ConcretePrototype: a concrete prototype class that implements the operation of cloning itself
3) Client: let a prototype object clone itself to create a new object (the same properties)

Improving cloned sheep with prototype mode:
1. Let's create a kind of entity class to implement the clonable interface and make it a prototype.
2. Rewrite clone method
3. Pass the object in the client clone() clones the object. At this time, it will be cloned according to the properties of the object to be cloned.

package com.xzz.improve;

public class Sheep implements Cloneable {
	private String name;
	private int age;
	private String color;
	private String address = "Mongolian sheep";
	public Sheep friend; //If it is an object, how will cloning be handled
	public Sheep(String name, int age, String color) {
		super();
		this.name = name;
		this.age = age;
		this.color = color;
	}
	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 getColor() {
		return color;
	}
	public void setColor(String color) {
		this.color = color;
	}
	
	
	
	@Override
	public String toString() {
		return "Sheep [name=" + name + ", age=" + age + ", color=" + color + ", address=" + address + "]";
	}
	//Clone the instance using the default clone method
	@Override
	protected Object clone()  {
		Sheep sheep = null;
		try {
			sheep = (Sheep)super.clone();
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		return sheep;
	}
	
	
}

Simulation client

package com.xzz.improve;

public class Client {
	public static void main(String[] args) {
		System.out.println("Prototype mode completes the creation of objects");
		Sheep sheep = new Sheep("tom", 1, "white");
		
		//sheep. Friend = new sheet ("Jack", 2, "black");
		
		Sheep sheep2 = (Sheep)sheep.clone(); //clone
		Sheep sheep3 = (Sheep)sheep.clone(); //clone
		Sheep sheep4 = (Sheep)sheep.clone(); //clone
		Sheep sheep5 = (Sheep)sheep.clone(); //clone

		System.out.println("sheep2 =" + sheep2);
		System.out.println("sheep3 =" + sheep3);
		System.out.println("sheep4 =" + sheep4);
		System.out.println("sheep5 =" + sheep5);
		
		//System.out.println("sheep2 =" + sheep2 + "sheep2.friend=" + sheep2.friend.hashCode());
		//System.out.println("sheep3 =" + sheep3 + "sheep3.friend=" + sheep3.friend.hashCode());
		//System.out.println("sheep4 =" + sheep4 + "sheep4.friend=" + sheep4.friend.hashCode());
		//System.out.println("sheep5 =" + sheep5 + "sheep5.friend=" + sheep5.friend.hashCode());
	}

}


From the above demonstration, we can also see that the prototype pattern can make the program more efficient and scalable.

3. Use of prototype model in spring

1) The creation of prototype bean s in spring is the application of prototype patterns
2) Source code
Create beans files and register entity classes as beans in prototype mode.

Create objects so that the acquired objects have common properties.

4. Discussion - light copy and deep copy

4.1 introduction to shallow copy

1) For member variables whose data type is the basic data type, shallow copy will directly transfer the value, that is, copy the attribute value to a new object.
2) For a member variable whose data type is a reference data type, for example, if the member variable is an array or a class object, the shallow copy will be passed by reference, that is, just copy the reference value (memory address) of the member variable to the new object. In fact, the member variable of both objects points to the same instance. In this case, modifying the member variable in one object will affect the member variable value of another object.
3) The previous clone sheep case is implemented using the default clone() method
sheep = (Sheep) super.clone();

4.2 basic introduction of deep copy

1) Copy the member variable values of all basic data types of the object
2) Apply for storage space for all member variables of reference data type, and copy the object referenced by each member variable of reference data type until the object can reach all objects. In other words, deep copy of an object copies the entire object (including the reference type of the object).
3) Deep copy implementation method 1: rewrite the clone method to implement deep copy
4) Deep copy implementation method 2: realize deep copy through object serialization (recommended)

Code implementation:
First define a class without an application object for later use as a reference object:

package com.xzz.deepclone;

import java.io.Serializable;

public class DeepCloneableTarget implements Serializable, Cloneable {
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	private String cloneName;

	private String cloneClass;

	//constructor 
	public DeepCloneableTarget(String cloneName, String cloneClass) {
		this.cloneName = cloneName;
		this.cloneClass = cloneClass;
	}

	//Because the properties of this class are all String, we can use the default clone here
	@Override
	protected Object clone() throws CloneNotSupportedException {
		return super.clone();
	}
}

Define another class, which uses the previously defined class as the reference object:

package com.xzz.deepclone;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class DeepProtoType implements Serializable, Cloneable{
	
	public String name; //String property
	public DeepCloneableTarget deepCloneableTarget;// reference type
	public DeepProtoType() {
		super();
	}
	
	
	//Deep copy - mode 1 uses clone method
	@Override
	protected Object clone() throws CloneNotSupportedException {
		
		Object deep = null;
		//Here we complete the cloning of basic data types (properties) and strings
		deep = super.clone(); 
		//The properties of reference types are processed separately
		DeepProtoType deepProtoType = (DeepProtoType)deep;
		deepProtoType.deepCloneableTarget  = (DeepCloneableTarget)deepCloneableTarget.clone();
		
		// TODO Auto-generated method stub
		return deepProtoType;
	}
	
	//Deep copy - mode 2 is implemented through object serialization (recommended)
	
	public Object deepClone() {
		
		//Create flow object
		ByteArrayOutputStream bos = null;
		ObjectOutputStream oos = null;
		ByteArrayInputStream bis = null;
		ObjectInputStream ois = null;
		
		try {
			
			//serialize
			bos = new ByteArrayOutputStream();
			oos = new ObjectOutputStream(bos);
			oos.writeObject(this); //Currently, this object is output as an object stream
			
			//Deserialization
			bis = new ByteArrayInputStream(bos.toByteArray());
			ois = new ObjectInputStream(bis);
			DeepProtoType copyObj = (DeepProtoType)ois.readObject();
			
			return copyObj;
			
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
			return null;
		} finally {
			//Close flow
			try {
				bos.close();
				oos.close();
				bis.close();
				ois.close();
			} catch (Exception e2) {
				// TODO: handle exception
				System.out.println(e2.getMessage());
			}
		}
		
	}
	
}

Variables of reference type are used in the above class. Deep copy can be realized in two ways:


Impersonate client:

Precautions and details of prototype mode:
1) When creating a new object is complex, you can use the prototype pattern to simplify the object creation process and improve efficiency.
2) Instead of reinitializing the object, it dynamically obtains the running state of the object
3) If the original object changes (increase or decrease attributes), other cloned objects will also change accordingly without modifying the code.
4) Complex code may be required to implement deep cloning
5) Disadvantages: each class needs to be equipped with a cloning method, which is not very difficult for new classes, but when transforming existing classes, the source code needs to be modified, which violates the ocp principle. This is very noteworthy.

Topics: Java Design Pattern