Note source: Shang Silicon Valley Java design pattern (illustration + framework source code analysis)
Prototype mode
1. Cloned sheep problem
Now there is a sheep named Tom, age 1 and color white. Please write a program to create 10 sheep with exactly the same attributes as Tom's sheep
traditional method
public class Sheep { private String name; private Integer age; public Sheep(String name, Integer age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } } public class Client { public static void main(String[] args) { for (int i = 0; i < 10; i++) { Sheep sheep = new Sheep("Tom", 1, "white"); System.out.println(sheep); } } }
Advantages and disadvantages of traditional methods
- 1) The advantages are easy to understand 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
Analysis of improvement ideas
In Java, the Object class is the root class of all classes. The Object class provides a clone method that 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. Basic introduction
-
1) Prototype mode refers to specifying the type of object to be created with prototype instances and creating new objects by copying 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 objects to copy themselves, that is, the object. clone()
-
4) Image understanding: Sun Dasheng pulls out monkey hair and turns into other sun Dasheng
3. Schematic structure diagram (UML class diagram)
Schematic structure diagram description
- 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 and create a new object (with the same properties)
4. Prototype pattern solves the problem of cloned sheep
The prototype pattern is used to improve the traditional mode, so that the program has higher efficiency and scalability
UML class diagram
Core code
public class Sheep implements Cloneable { private String name; private Integer age; private String color; public Sheep(String name, Integer age, String color) { this.name = name; this.age = age; this.color = color; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer 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 + '\'' + '}'; } @Override protected Object clone() { Sheep sheep = null; try { sheep = (Sheep) super.clone(); } catch (Exception e) { e.printStackTrace(); } return sheep; } } public class Client { public static void main(String[] args) { Sheep sheep = new Sheep("Tom", 1, "white"); for (int i = 0; i < 10; i++) { Sheep sheep1 = (Sheep) sheep.clone(); System.out.println(sheep1); } } }
5. JDK source code analysis
In the Spring framework, the prototype pattern is used in the getBean method used when creating the ApplicationContext
6. Light copy and deep copy
Basic 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. 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
- 3) In front of us, cloning sheep is a shallow copy
- 4) Shallow copy is implemented using the default clone method: sheet = (sheet) super. Clone();
Basic introduction to 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 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
- 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
Deep copy mode 1
public class DeepClonableTarget implements Serializable, Cloneable { private String cloneName; private String cloneClass; public DeepClonableTarget(String cloneName, String cloneClass) { this.cloneName = cloneName; this.cloneClass = cloneClass; } public String getCloneName() { return cloneName; } public void setCloneName(String cloneName) { this.cloneName = cloneName; } public String getCloneClass() { return cloneClass; } public void setCloneClass(String cloneClass) { this.cloneClass = cloneClass; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } } public class DeepPrototype implements Serializable, Cloneable { private String name; private DeepClonableTarget deepClonableTarget; public DeepPrototype() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public DeepClonableTarget getDeepClonableTarget() { return deepClonableTarget; } public void setDeepClonableTarget(DeepClonableTarget deepClonableTarget) { this.deepClonableTarget = deepClonableTarget; } @Override protected Object clone() throws CloneNotSupportedException { //Basic data type copy Object object = super.clone(); //Reference type copy DeepPrototype deepPrototype = (DeepPrototype) object; deepPrototype.deepClonableTarget = (DeepClonableTarget) deepClonableTarget.clone(); return object; } } public class DeepTest { public static void main(String[] args) throws CloneNotSupportedException { DeepPrototype prototype = new DeepPrototype(); prototype.setName("Song Jiang"); prototype.setDeepClonableTarget(new DeepClonableTarget("Timely rain", "Timely rain class")); DeepPrototype clone1 = (DeepPrototype) prototype.clone(); DeepPrototype clone2 = (DeepPrototype) prototype.clone(); DeepPrototype clone3 = (DeepPrototype) prototype.clone(); DeepPrototype clone4 = (DeepPrototype) prototype.clone(); DeepPrototype clone5 = (DeepPrototype) prototype.clone(); System.out.println(prototype.getName() + ", " + prototype.getDeepClonableTarget().hashCode()); // Song Jiang, 1554874502 System.out.println(clone1.getName() + ", " + clone1.getDeepClonableTarget().hashCode()); // Song Jiang, 1846274136 System.out.println(clone2.getName() + ", " + clone2.getDeepClonableTarget().hashCode()); // Song Jiang, 1639705018 System.out.println(clone3.getName() + ", " + clone3.getDeepClonableTarget().hashCode()); // Song Jiang, 1627674070 System.out.println(clone4.getName() + ", " + clone4.getDeepClonableTarget().hashCode()); // Song Jiang, 1360875712 System.out.println(clone5.getName() + ", " + clone5.getDeepClonableTarget().hashCode()); // Song Jiang, 1625635731 } }
Deep copy mode 2
public class DeepClonableTarget implements Serializable, Cloneable { private String cloneName; private String cloneClass; public DeepClonableTarget(String cloneName, String cloneClass) { this.cloneName = cloneName; this.cloneClass = cloneClass; } public String getCloneName() { return cloneName; } public void setCloneName(String cloneName) { this.cloneName = cloneName; } public String getCloneClass() { return cloneClass; } public void setCloneClass(String cloneClass) { this.cloneClass = cloneClass; } } public class DeepPrototype implements Serializable, Cloneable { private String name; private DeepClonableTarget deepClonableTarget; public DeepPrototype() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public DeepClonableTarget getDeepClonableTarget() { return deepClonableTarget; } public void setDeepClonableTarget(DeepClonableTarget deepClonableTarget) { this.deepClonableTarget = deepClonableTarget; } public DeepPrototype deepClone() { ByteArrayOutputStream bos = null; ObjectOutputStream oos = null; ByteArrayInputStream bis = null; ObjectInputStream ois = null; try { // serialize bos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(bos); oos.writeObject(this); // Deserialization bis = new ByteArrayInputStream(bos.toByteArray()); ois = new ObjectInputStream(bis); return (DeepPrototype) ois.readObject(); } catch (Exception e) { e.printStackTrace(); return null; } finally { try { if (ois != null) { ois.close(); } if (bis != null) { bis.close(); } if (oos != null) { oos.close(); } if (bos != null) { bos.close(); } } catch (IOException e) { e.printStackTrace(); } } } } public class DeepTest { public static void main(String[] args) throws CloneNotSupportedException { DeepPrototype prototype = new DeepPrototype(); prototype.setName("Song Jiang"); prototype.setDeepClonableTarget(new DeepClonableTarget("Timely rain", "Timely rain class")); DeepPrototype clone1 = prototype.deepClone(); DeepPrototype clone2 = prototype.deepClone(); DeepPrototype clone3 = prototype.deepClone(); DeepPrototype clone4 = prototype.deepClone(); DeepPrototype clone5 = prototype.deepClone(); System.out.println(prototype.getName() + ", " + prototype.getDeepClonableTarget().hashCode()); // Song Jiang, 644117698 System.out.println(clone1.getName() + ", " + clone1.getDeepClonableTarget().hashCode()); // Song Jiang, 317574433 System.out.println(clone2.getName() + ", " + clone2.getDeepClonableTarget().hashCode()); // Song Jiang, 885284298 System.out.println(clone3.getName() + ", " + clone3.getDeepClonableTarget().hashCode()); // Song Jiang, 1389133897 System.out.println(clone4.getName() + ", " + clone4.getDeepClonableTarget().hashCode()); // Song Jiang, 1534030866 System.out.println(clone5.getName() + ", " + clone5.getDeepClonableTarget().hashCode()); // Song Jiang, 664223387 } }
Comparison between mode 1 and mode 2
- When there are few member attributes of object reference type, method 1 is simple; Method 2 is simple when there are many member attributes of the object reference type
- When the member attributes of object reference types often change, method 1 needs to be modified synchronously, and method 2 does not need to be modified
- Recommended usage 2: low coupling, strong maintainability and high expansibility
7. Precautions and details
- 1) Advantages: when creating a new object is complex, you can use the prototype pattern to simplify the object creation process and improve efficiency
- 2) Advantage: instead of reinitializing the object, it dynamically obtains the running state of the object
- 3) Advantages: if the original object changes (increase or decrease attributes), other cloned objects will also change accordingly without modifying the code
- 4) Disadvantages: 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 the source code needs to be modified when transforming existing classes, which violates the OCP principle. Please pay attention to this