Archetypal model
Basic introduction
- Prototype mode refers to: using prototype instances to specify the type of objects to be created, and copying these prototypes to create new objects.
- Prototype pattern is a kind of creative design pattern, which allows one object to create another customizable object without knowing how to create details.
- How it works: 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, i.e. object. clone()
- Generating an object through new requires a very cumbersome data preparation or access right, so prototype mode can be used.
- Cloning is similar to new, but different from new. New creates new object properties with default values. The property value of the cloned object is exactly the same as that of the prototype object. And the change of the cloned new object will not affect the prototype object. Then, change the value of the cloned object
PS: pay attention to the words: Clone and copy are the same thing!
Prototype pattern UML class diagram
Explain
- Prototype: a prototype class that declares an interface to clone itself
- ConcretePrototype: a concrete prototype class that implements a clone operation
- Client: let a prototype object clone itself to create a new object (same property)
Case analysis
The core of the prototype pattern is cloning, so here's an example of cloning sheep.
Cloning in traditional way
code implementation
//These two notes are lombok's @Data @AllArgsConstructor //All parameter constructor public class Sheep { private String name; private int age; private String color; } //Client public class Client { public static void main(String[] args) { // TODO Auto-generated method stub //Traditional approach 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); //... } }
Analysis
Advantage
It is easy to understand and operate.
shortcoming
- When creating a new object, it is always necessary to retrieve the properties of the original object. If the created object is complex, the efficiency is low
- It is always necessary to reinitialize the object instead of dynamically obtaining the state of the object at runtime, which is not flexible enough
Improvement thinking
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 means that the class can copy and has the ability to copy = > prototype mode
Prototype mode - light copy
Introduction to shallow copy
- For a member variable whose data type is the basic data type, the shallow copy will directly transfer the value, that is, copy the attribute value to a new object.
- For a member variable whose data type refers to a data type, such as an array or a class object, the shallow copy will transfer the reference, that is, copy the reference value (memory address) of the member variable to a new object. Because in fact, this member variable of both objects points to the same instance. In this case, modifying the member variable in one object affects the value of the member variable in another object
- Shallow copy is implemented using the default clone() method
code implementation
@Data @AllArgsConstructor //All parameter constructor public class Sheep implements Cloneable { private String name; private int age; private String color; //Property is an object, how is cloning handled? The default is shallow copy public Sheep friend; //Clone the instance and use the default clone method to complete @Override protected Object clone() { Sheep sheep = null; try { sheep = (Sheep)super.clone(); } catch (Exception e) { System.out.println(e.getMessage()); } return sheep; } } //Client public class Client { public static void main(String[] args) { System.out.println("Prototype pattern completes object creation"); 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()); } }
The output sheep.friend.hashCode() is the same, and the member variable of reference type does not realize the real copy.
Analysis
Because the prototype pattern of the shallow copy implementation is used to create the object instance. The name, age and color attributes of the sheet class can be copied directly, and the objects do not affect each other. However, friend is the object of the class, not the basic data type, but the reference data type, so the copy only points to the same address, so if a change is made, the friend attributes of all copied objects will be changed.
In order to make the member variables of non basic data types copy into new objects, we need to adopt the way of deep copy to realize the prototype pattern.
shortcoming
All variables of the copied object contain the same value as the original object, and all references to other objects still point to the original object.
Prototype mode - deep copy
Basic introduction to deep copy
- Copy member variable values for all basic data types of an object
- Apply 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 the objects that the object can reach. That is to say, to make a deep copy of an object, you need to copy the entire object (including the reference type of the object)
Implementation of deep copy
- Rewrite clone method to realize deep copy
- Deep copy through object serialization (recommended)
Deep and shallow clone map comparison
code implementation
//Object instance being copied in depth @Data @AllArgsConstructor //All parameter constructor public class DeepCloneableTarget implements Serializable, Cloneable { private String cloneName; private String cloneClass; //Because the properties of this class are all strings, we can use the default clone here to complete @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } } //Classes with deep copy public class DeepProtoType implements Serializable, Cloneable{ //String property (basic data type) public String name; // reference type public DeepCloneableTarget deepCloneableTarget; public DeepProtoType() { super(); } //Deep copy mode 1 rewrite clone method //For member variables of reference type, clone them separately again @Override protected Object clone() throws CloneNotSupportedException { Object deep = null; //The basic data types (properties) and strings are cloned here deep = super.clone(); //For properties of reference types, handle them separately DeepProtoType deepProtoType = (DeepProtoType)deep; deepProtoType.deepCloneableTarget = (DeepCloneableTarget)deepCloneableTarget.clone(); return deepProtoType; } //Deep copy - mode 2 is implemented by serializing objects (recommended) public Object deepClone() { //Create a 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); //This object is currently output as an object stream //De serialization 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 { //Closed flow try { bos.close(); oos.close(); bis.close(); ois.close(); } catch (Exception e2) { // TODO: handle exception System.out.println(e2.getMessage()); } } } } public class Client { public static void main(String[] args) throws Exception { DeepProtoType p = new DeepProtoType(); p.name = "Song Jiang"; p.deepCloneableTarget = new DeepCloneableTarget("Daniel", "Calf"); //Mode 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()); //Mode 2 complete deep copy DeepProtoType p3 = (DeepProtoType) p.deepClone(); System.out.println("p.name=" + p.name + "p.deepCloneableTarget=" + p.deepCloneableTarget.hashCode()); System.out.println("p3.name=" + p.name + "p3.deepCloneableTarget=" + p2.deepCloneableTarget.hashCode()); } }
Notes and details of prototype pattern
- When creating new objects is more complex, prototype pattern can be used to simplify the process of creating objects and improve efficiency
- Instead of reinitializing an object, you dynamically get the state of the object at runtime
- If the original object changes (increase or decrease attributes), other cloned objects will change accordingly, without modifying the code
- More complex code may be required for deep cloning
- Disadvantages: each class needs to be equipped with a clone method, which is not difficult for a new class. However, when modifying an existing class, you need to modify its source code, which violates the ocp principle. Please pay attention to this
Application scenarios in development
- Prototype patterns rarely appear alone, usually with factory method patterns, through clone
The factory method creates an object, which is then provided to the caller. - There are two kinds of bean creation in spring: Singleton pattern and prototype pattern. (of course, the prototype pattern needs to be matched with the factory pattern.)