In some systems, there may be a large number of identical or similar objects, and the creation of such objects can be complex and costly. Using the prototype pattern described in this section can solve such problems.

The prototype pattern

The Prototype pattern is defined as follows: Use an already created object to create identical or similar objects by copying the Prototype object. You don’t need to know the details of creating an object.

Prototype pattern structure and implementation

Java provides a Clone () method for objects, so implementing the prototype pattern in Java is simple.

  1. Pattern structure

The archetypal pattern contains the following primary roles.

  • Abstract stereotype class: specifies the interface that concrete stereotype objects must implement, in this caseCloneableInterface.
  • Concrete stereotype class: The Clone () method that implements the abstract stereotype class, which is an object that can be copied.
  • Access classes: Copy new objects using the clone() method in the concrete prototype class.

If, when the clone doesn’t implement Cloneable interface, the compiler will throw a CloneNotSupportedException anomalies.

Prototype structure drawing:

In general, using the prototype pattern (Java Clone ()) requires the following conditions:

  • For any object o, there is O.lone ()! = o. In other words, the clone object is not the same object as the prototype object.
  • For any object o, there is o.lone ().getClass() == o.getclass (). In other words, the clone is of the same type as the prototype.
  • If the equals() method of object O is properly defined, o.lone ().equals(o) should be established.

Code example: Here by writing a Monkey class, and then through the clone method, copy the monkey object.

  • Realizetype (replicable object)
 public class Wukong implements Cloneable{
	@Override
	protected Object clone(a) throws CloneNotSupportedException {
		return super.clone();
	}
	
	private String name;
	
	private int age;

	public String getName(a) {
		return name;
	}

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

	public int getAge(a) {
		return age;
	}

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

	@Override
	public String toString(a) {
		return "[name=" + name + ", age=" + age + "]"; }}Copy the code
  • Use the client
public class Client {

	public static void main(String[] args) throws CloneNotSupportedException {
		Wukong wukong =	new Wukong();
		wukong.setName("Monkey King");
		wukong.setAge(500);
		
		System.out.println("wukong:"+wukong);
		
		Wukong wukong2 =  (Wukong)wukong.clone();
		System.out.println("wukong2:"+wukong2); System.out.println(wukong == wukong2); }}Copy the code

The execution result

Wukong :[name= qi Tian Da Sheng, age=500] wukong2:[name= age=500]
false
Copy the code

The copied objects have the same content, but not the same address. Two objects with exactly the same content, which is exactly what our prototype copy looks like.

Deep copy & shallow copy

Here’s another code example. The object properties we copied above are simple base types.

  • Prototype Object 1

/** * Deep copy target object *@author fengwei
 *
 */
public class DeepCloneOjb implements Serializable.Cloneable {
	private static final long serialVersionUID = 1L;
	
	private String name;
	
	private Integer age;

	public String getName(a) {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
	@Override
	protected Object clone(a) throws CloneNotSupportedException {
		return super.clone();
	}

	@Override
	public String toString(a) {
		return ", DeepCloneOjb hashcode = [" + hashCode() + " ] , data = [name=" + name + ", age=" + age + "]";
	}

	public Integer getAge(a) {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age; }}Copy the code
  • Prototype Object 2
public class DeepPrototype implements Cloneable.Serializable{
	
	private static final long serialVersionUID = 1L;
	
	private String userName;
	
	private DeepCloneOjb deepCloneOjb;
	
	

	public String getUserName(a) {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public DeepCloneOjb getDeepCloneOjb(a) {
		return deepCloneOjb;
	}

	public void setDeepCloneOjb(DeepCloneOjb deepCloneOjb) {
		this.deepCloneOjb = deepCloneOjb;		
	}
	
	
	@Override
	protected Object clone(a) throws CloneNotSupportedException {		
		return super.clone();
	}

	@Override
	public String toString(a) {
		return "DeepPrototype hashcode = "+ hashCode() +", data = [userName=" + userName + ", deepCloneOjb=" + deepCloneOjb + "]"; }}Copy the code
  • Use the client
public static void main(String[] args) throws CloneNotSupportedException {
		DeepCloneOjb deepCloneOjb = new  DeepCloneOjb();
		deepCloneOjb.setName("deepCloneOjb");
		deepCloneOjb.setAge(10);
		
		// Prototype objects
		DeepPrototype deepPrototype =  new DeepPrototype();
		deepPrototype.setUserName("deepPrototype");
		deepPrototype.setDeepCloneOjb(deepCloneOjb);
		
		//clone
		DeepPrototype deepPrototype2 = (DeepPrototype) deepPrototype.clone();
		
		System.out.println("deepPrototype="+deepPrototype);
		System.out.println("deepPrototype2="+deepPrototype2);
	}
Copy the code

Execution Result:

deepPrototype=DeepPrototype hashcode = 2018699554, data =  [userName=deepPrototype, deepCloneOjb=, DeepCloneOjb hashcode = [1311053135 ] , data = [name=deepCloneOjb, age=10]]

deepPrototype2=DeepPrototype hashcode = 118352462, data =  [userName=deepPrototype, deepCloneOjb=, DeepCloneOjb hashcode = [1311053135 ] , data = [name=deepCloneOjb, age=10]]
Copy the code

The result is that the address of the DeepPrototype object is different from that of deepPrototype2, but the address of the internal DeepCloneOjb object points to the same object.

This is the deep-copy problem. Java provides Clone, which only works with simple basic data types, and copies object addresses for reference data types (classes, interfaces, arrays, and other complex types).

Deep copy solution

We can implement deep copy by nesting copies, layer by layer. Clone () = clone(); clone() = clone();

@Override
	protected Object clone(a) throws CloneNotSupportedException {
		// The basic data types (attributes) and strings are cloned
		Object deep = super.clone(); 
		// Handle attributes of the reference type separately
		DeepPrototype deepProtoType = (DeepPrototype)deep;
		
		deepProtoType.deepCloneOjb  = (DeepCloneOjb)deepCloneOjb.clone();
		
		return deepProtoType;
        }       
Copy the code

Execution Result:

deepPrototype=DeepPrototype hashcode = 2018699554, data =  [userName=deepPrototype, deepCloneOjb=, DeepCloneOjb hashcode = [1311053135 ] , data = [name=deepCloneOjb, age=10]]
deepPrototype2=DeepPrototype hashcode = 118352462, data =  [userName=deepPrototype, deepCloneOjb=, DeepCloneOjb hashcode = [1550089733 ] , data = [name=deepCloneOjb, age=10]]
Copy the code

Discover the internal reference type object address is also different!

Effects of cloning on singletons

We know that cloning means making a copy of a target object that is identical in content. For a certain system, if the target object happens to be a singleton object, can we destroy the singleton characteristics by cloning?

Code examples:

public class ReflectClone implements Cloneable {
	
	private static ReflectClone instance = new ReflectClone();
    
	private ReflectClone(a) {}public static ReflectClone getInstance(a) {
        return instance;
    }
    
    @Override
    public ReflectClone clone(a) {
        try {
            return (ReflectClone) super.clone();
        } catch (CloneNotSupportedException e) {
            return null; }}public static void main(String[] args) {
    	// Prototype objects
    	ReflectClone protoType = ReflectClone.getInstance();
        //cloneReflectClone cloneType = protoType.clone(); System.out.println(protoType==cloneType); }}Copy the code

Execution result False

As a result, two different objects are indeed created. In fact, the solution to preventing copy destruction of singletons is very simple, just forbid copy. So our singleton class does not implement the Cloneable interface, so we can either rewrite the clone() method and return the singleton in the clone() method, as follows.

@Override
public ConcretePrototype clone(a) {
    return instance;
}
Copy the code

Application of prototype patterns in source code

ArrayList implements the Cloneable interface and rewrites the Clone method.

public Object clone(a) {
    try {
        @SuppressWarnings("unchecked")
        ArrayList<E> v = (ArrayList<E>) super.clone();
        v.elementData = Arrays.copyOf(elementData, size);
        v.modCount = 0;
        return v;
    } catch (CloneNotSupportedException e) {
        throw newInternalError(); }}Copy the code

As you can see from the above code, the clone() method simply loops through the elements in the List. If the object in collection A is a reference type, would the clone object in ArrayList now be a deep copy or a shallow copy?

Add a new class that uses ArrayList to reference variables and clone the contents of the collection. The code is as follows:

public class DeepPrototypeList implements Cloneable.Serializable{
	
	private static final long serialVersionUID = 1L;
	
	private String userName;
	
	private ArrayList<DeepCloneOjb> deepCloneOjbList;
	
	

	public String getUserName(a) {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	
	public ArrayList<DeepCloneOjb> getDeepCloneOjb(a) {
		return deepCloneOjbList;
	}

	public void setDeepCloneOjb(ArrayList<DeepCloneOjb> deepCloneOjb) {
		this.deepCloneOjbList = deepCloneOjb;
	}

	@Override
	protected Object clone(a) throws CloneNotSupportedException {
		// The basic data types (attributes) and strings are cloned
		Object deep = super.clone(); 
		// Handle attributes of the reference type separately
		DeepPrototypeList deepProtoType = (DeepPrototypeList)deep;
		deepProtoType.deepCloneOjbList = (ArrayList<DeepCloneOjb>) ((ArrayList)deepProtoType.deepCloneOjbList).clone();		
		return deepProtoType;
	}
	
  public Object deepClone(a) {
		
		// Create a stream object
		ByteArrayOutputStream bos = null;
		ObjectOutputStream oos = null;
		ByteArrayInputStream bis = null;
		ObjectInputStream ois = null;
		
		try {
			
			/ / the serialization
			bos = new ByteArrayOutputStream();
			oos = new ObjectOutputStream(bos);
			oos.writeObject(this); // The current object is output as a stream of objects
			
			// deserialize
			bis = new ByteArrayInputStream(bos.toByteArray());
			ois = new ObjectInputStream(bis);
			DeepPrototypeList copyObj = (DeepPrototypeList)ois.readObject();
			
			return copyObj;
			
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		} finally {
			/ / close the flow
			try {
				bos.close();
				oos.close();
				bis.close();
				ois.close();
			} catch(Exception e2) { System.out.println(e2.getMessage()); }}}@Override
	public String toString(a) {
		return "DeepPrototypeList hashcode = "+hashCode()+", [userName=" + userName + ", deepCloneOjbList=" + deepCloneOjbList + "]"; }}Copy the code
  • Using the
public static void main(String[] args) throws CloneNotSupportedException {
		DeepCloneOjb deepCloneOjb = new  DeepCloneOjb();
		deepCloneOjb.setName("deepCloneOjb");
		deepCloneOjb.setAge(10);
        
		ArrayList<DeepCloneOjb> list = new ArrayList<>();
		list.add(deepCloneOjb);
		
		DeepPrototypeList deepPrototypeList =  new DeepPrototypeList();
				deepPrototypeList.setUserName("deepPrototypeList");
		deepPrototypeList.setDeepCloneOjb(list);
		
		System.out.println("DeepPrototypeList =="+deepPrototypeList.toString());
		
		DeepPrototypeList deepPrototypeList2 = (DeepPrototypeList)deepPrototypeList.deepClone();
		System.out.println("Serialization mode deepPrototypeList2=="+deepPrototypeList2.toString());

		DeepPrototypeList deepPrototypeList3 =	(DeepPrototypeList)deepPrototypeList.clone();
		System.out.println("Set provides cloning methods deepPrototypeList3=="+deepPrototypeList3.toString());
	}
Copy the code

Execution Result:

Source object deepPrototypeList== deepPrototypeList hashCode =2018699554, [userName=deepPrototypeList, deepCloneOjbList=[, DeepCloneOjb hashcode = [1311053135 ] , data = [name=deepCloneOjb, age=10DeepPrototypeList2 ==DeepPrototypeList hashCode =2065951873, [userName=deepPrototypeList, deepCloneOjbList=[, DeepCloneOjb hashcode = [1791741888 ] , data = [name=deepCloneOjb, age=10DeepPrototypeList3 ==DeepPrototypeList hashCode =1595428806, [userName=deepPrototypeList, deepCloneOjbList=[, DeepCloneOjb hashcode = [1311053135 ] , data = [name=deepCloneOjb, age=10]]]
Copy the code

As can be seen from the clone method in ArrayList, the object addresses in the collection are still the same. Therefore, it can be concluded that the clone method in ArrayList is a shallow copy.

Is there a way to do deep copy, as the code shows, we can do deep copy by serialization.