Design pattern - prototype pattern

Keywords: Java Spring unit testing

preface

This is an article written after drinking half a bottle of yellow rice wine at night. I don't know the quality. Recently, I have a little insomnia at night. I fall asleep after 3 o'clock. Basically, I can see the sun every day. At the age of 21, I should have a carefree life with a monthly salary of more than 10000, but I feel very anxious every day. I don't know why! Simply record your life, friends, don't complain, may everything be beautiful! Prototype mode is to specify the type of objects to be created, and create new objects by examining these prototypes! To put it bluntly, it is used to create duplicate objects while ensuring performance (creating complex objects). The prototype mode belongs to the creation model of the three types, which provides the best way to create objects. When the cost of directly creating objects is relatively high, this mode is recommended!

Legacy create object

Object sheet

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

copy object

public static void main(String[] args) {
		// TODO Auto-generated method stub
		//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);
		//...
}

This is more lol, and can be written when copying simple objects, but this basic writing method is not very good when copying complex objects or creating objects with high performance cost

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 instead of dynamically obtaining the running state of the object, which is not flexible enough

Prototype mode

Simple description: specify the type of object to be created with prototype instances, and create new objects by copying these prototypes.
Core idea:
1. To implement the cloning operation, inherit clonable in JAVA and override clone(). In. NET, you can use the MemberwiseClone() method of the Object class to realize the shallow copy of the Object or the deep copy through serialization.
2. The prototype pattern is also used to isolate the coupling relationship between the users of class objects and specific types (mutable classes). It also requires these "mutable classes" to have stable interfaces.

Code demonstration
Object sheet

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

	@Override
	protected Object clone()  {
		
		Sheep sheep = null;
		try {
			sheep = (Sheep)super.clone();
		} catch (Exception e) {
			// TODO: handle exception
			System.out.println(e.getMessage());
		}
		// TODO Auto-generated method stub
		return sheep;
	}
}

copy object

public static void main(String[] args) {
		System.out.println("Prototype mode completes the creation of objects");
		// TODO Auto-generated method stub
		Sheep sheep = new Sheep("tom", 1, "white");
		
		sheep.friend = new Sheep("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 + "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());
	}

Existing problems

  • This method of using the default clone() is a shallow copy
  • 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
  • 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. Because 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

Deep copy

explain:

  • Copy the member variable values of all basic data types of the object
  • 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 all objects reached by the object. That is to say, to make a deep copy of an object, you need to copy the whole object

Implementation method:

  • Deep copy implementation method 1: rewrite the clone method to implement deep copy
  • Deep copy implementation method 2: realize deep copy through object serialization (recommended)

Reference object

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();
	}
}

Prototype object

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());
			}
		}
		
	}
	
}

Copy

public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		DeepProtoType p = new DeepProtoType();
		p.name = "Song Jiang";
		p.deepCloneableTarget = new DeepCloneableTarget("Daniel", "Calf");
		
		//Method 1: complete deep copy
		
//		DeepProtoType p2 = (DeepProtoType) p.clone();
//		
//		System.out.println("p.name=" + p.name + "p.deepCloneableTarget=" + p.deepCloneableTarget.hashCode());
//		System.out.println("p2.name=" + p.name + "p2.deepCloneableTarget=" + p2.deepCloneableTarget.hashCode());
	
		//Method 2: complete deep copy
		DeepProtoType p2 = (DeepProtoType) p.deepClone();
		
		System.out.println("p.name=" + p.name + "p.deepCloneableTarget=" + p.deepCloneableTarget.hashCode());
		System.out.println("p2.name=" + p.name + "p2.deepCloneableTarget=" + p2.deepCloneableTarget.hashCode());
	
	}

be careful

  • When creating a new object is complex, you can use the prototype pattern to simplify the object creation process and improve efficiency
  • Instead of reinitializing the object, it dynamically obtains the running state of the object
  • If the original object changes (increase or decrease attributes), other cloned objects will also change accordingly without modifying the code
  • The implementation of deep cloning may require more complex code

Disadvantages: each class needs to be equipped with a cloning method, which is not very difficult for new classes, but when transforming existing classes, you need to modify their source code, which violates the ocp principle. Please pay attention to this

Posted by cavolks on Wed, 13 Oct 2021 10:05:17 -0700