1. Introduction
Prototype
Refers to the use of prototype instances to develop the types of objects created and to create new objects by copying them
When prototype mode is available
- When a system should be independent of its product creation, composition, and representation
- When the class to instantiate is made at runtime, such as dynamic loading
- To avoid creating a factory class hierarchy parallel to the product class hierarchy
- When an instance of a class can only have one of several different combinations of states.
2. UML Class Diagram
Explain the roles in the diagram
- Prototype: Declare an interface that clones itself
- ConcretePrototype: Implement an operation to clone itself
- Client: Let a prototype clone itself to create a new object
3. Java and prototype
In Java, there is actually a prototype pattern provided by the language itself, and we just need to write the implementation.
We all know that Objects in Java are classes of all objects, and Objects provide us with a clone() method. We just need our classes to implement clone().
As a matter of fact, if you think about it carefully, Object is not equivalent to Prootype in the figure, and the classes we want to support cloning are not equivalent to Concrete Prototype. Now that the framework is set up, we can write the most important methods well.
⚠️ Note: The only thing to note is that we need classes that want to clone themselves to inherit the Cloneable interface, indicating that they support cloning.
The code is shown in the diagram:
/** * @author ZhongJing </p> * @Description </p> */ @Data @NoArgsConstructor @AllArgsConstructor @ToString public class Person implements Cloneable { /** * Full name */ private String name; /** * Age */ private String age; /** * Gender */ private String gender; /** * Pets */ private Pets pets; @Override protected Object clone() throws CloneNotSupportedException { Person person = null; person = (Person) super.clone(); return person; } }
So we've written a cloning method for Person to see how it works
/** * @author ZhongJing </p> * @Description </p> */ public class Client { public static void main(String[] args) { Person person = new Person(); person.setName("Zhang San"); person.setAge(18); person.setGender("male"); Pets pets = new Pets(); pets.setName("Prosperous Money"); pets.setAge(5); person.setPets(pets); System.out.println("Prototype:" + person); try { Person personClone = (Person) person.clone(); System.out.println("Clone:" + personClone); } catch (CloneNotSupportedException e) { e.printStackTrace(); } } }
The results are as follows:
Prototype: Person(name=Zhang San, age=18, gender=male, pets=Pets(name=Prosperous Money, age=5)) Clone: Person(name=Zhang San, age=18, gender=male, pets=Pets(name=Prosperous Money, age=5))
It can be seen that the clone() method of the Object is successful, but there are some problems in calling the clone() method of the Object. If we do not override the clone() method of the Object, it is actually a shallow copy. If the shallow copy is made, the cascading objects will be passed directly by reference to the new clone object. In short, the cascade object in the prototype object and the object cloned from the shallow copy are actually the same object, and the other object changes when we manipulate one of them. This problem is not what we expected with some special needs, as the following code runs:
/** * @author ZhongJing </p> * @Description </p> */ public class Client { public static void main(String[] args) { Person person = new Person(); person.setName("Zhang San"); person.setAge(18); person.setGender("male"); Pets pets = new Pets(); pets.setName("Prosperous Money"); pets.setAge(5); person.setPets(pets); System.out.println("Prototype:" + person); try { Person personClone = (Person) person.clone(); System.out.println("Clone:" + personClone); // Changing properties of cascading objects pets.setName("Flowers like flowers"); System.out.println("Prototype:" + person); System.out.println("Clone:" + personClone); } catch (CloneNotSupportedException e) { e.printStackTrace(); } } }
Run result:
Prototype: Person(name=Zhang San, age=18, gender=male, pets=Pets(name=Prosperous Money, age=5)) Clone: Person(name=Zhang San, age=18, gender=male, pets=Pets(name=Prosperous Money, age=5)) Prototype: Person(name=Zhang San, age=18, gender=male, pets=Pets(name=Flowers like flowers, age=5)) Clone: Person(name=Zhang San, age=18, gender=male, pets=Pets(name=Flowers like flowers, age=5))
4. Shallow and Deep Copies
Shallow copy:
In the shallow copy, if the member variable of the prototype object is a value type, a copy will be made to the cloned object. If the member variable of the prototype object is a reference type, the address of the reference object is copied to the cloned object, that is, the member variable of the prototype object and the cloned object point to the same memory address. Simply put, in a shallow copy, when an object is copied, only member variables of the value type it contains and itself are copied, while member objects of the reference type are not.
Deep copy:
In deep cloning, whether the member variable of the prototype object is a value type or a reference type, a copy will be made to the cloned object, and deep cloning will also copy all the reference objects of the prototype object to the cloned object. Simply put, in deep cloning, all member variables contained in an object are copied, in addition to the object itself.
How do you implement shallow copy in Java?
Shallow copy has already been done in Chapter 3, and it also clarifies some drawbacks of shallow copy (can be regarded as drawbacks). I won't go into much detail here
How do I implement deep copy in Java?
There are two ways to implement deep copy in Java
- Override the clone () method, not just call super.clone(); Instead, make your own cloning
- Make the object Serializable and copy the object by re-exporting the object input stream
V. Ways to Deep Copy Java
5.1 Rewrite clone()
The way to override clone is actually very simple, similar to shallow copy, except that we need to make an extra separate clone of the cascaded objects and set them into the cloned objects, but we need to implement the clone() method on the cascaded objects as well. If there are cascading layers (cascading objects as well as cascading objects) to implement the clone () method on the cascading objects at each level, it is known that a cascading object has no dependent objects but is of basic type.
Direct Up Code
/** * @author ZhongJing </p> * @Description </p> */ @Data @NoArgsConstructor @AllArgsConstructor @ToString public class Person implements Cloneable { /** * Full name */ private String name; /** * Age */ private Integer age; /** * Gender */ private String gender; /** * Pets */ private Pets pets; @Override protected Object clone() throws CloneNotSupportedException { Person person = null; // Clone this object first person = (Person) super.clone(); // Extra processing of cascaded objects Pets petsClone = (Pets) person.getPets().clone(); // Clone the set of cascading objects into this object person.setPets(petsClone); return person; } } /** * @author ZhongJing </p> * @Description </p> */ @Data @ToString @NoArgsConstructor @AllArgsConstructor public class Pets implements Cloneable { /** * Name */ private String name; /** * Age */ private Integer age; @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
As shown in the code above, let's experiment to see if changing the cascading properties of a prototype clones the cascading properties of an object
/** * @author ZhongJing </p> * @Description </p> */ public class Client { public static void main(String[] args) { Person person = new Person(); person.setName("Zhang San"); person.setAge(18); person.setGender("male"); Pets pets = new Pets(); pets.setName("Prosperous Money"); pets.setAge(5); person.setPets(pets); System.out.println("Prototype:" + person); try { Person personClone = (Person) person.clone(); System.out.println("Clone:" + personClone); // Changing properties of cascading objects pets.setName("Flowers like flowers"); System.out.println("Prototype:" + person); System.out.println("Clone:" + personClone); } catch (CloneNotSupportedException e) { e.printStackTrace(); } } }
Run result:
Prototype: Person(name=Zhang San, age=18, gender=male, pets=Pets(name=Prosperous Money, age=5)) Clone: Person(name=Zhang San, age=18, gender=male, pets=Pets(name=Prosperous Money, age=5)) Prototype: Person(name=Zhang San, age=18, gender=male, pets=Pets(name=Flowers like flowers, age=5)) Clone: Person(name=Zhang San, age=18, gender=male, pets=Pets(name=Prosperous Money, age=5))
From the results, we can see that this time we changed the cascade properties of the prototype object, the cloned object did not change, which also proves that our deep copy was successful.
5.2 Serialized deep copy
We can also clone objects by serializing them, then writing them to and out of the stream, without implementing the clone() method for each cascade object and without implementing the Cloneable interface for the prototype object, we need to implement the Serializable interface so that the object supports serialization. The advantage of this approach is that only the Serializable interface needs to be implemented in the prototype and cascaded objects, no additional operations are required, and the specific cloning details are only defined in the prototype.
Look directly at the code:
/** * @author ZhongJing </p> * @Description </p> */ @Data @NoArgsConstructor @AllArgsConstructor @ToString public class Person implements Serializable { /** * Full name */ private String name; /** * Age */ private Integer age; /** * Gender */ private String gender; /** * Pets */ private Pets pets; /** * Custom cloning method (deep copy by serialization) * * @return Return cloned objects */ public Object clonePerson() { ByteArrayOutputStream bos = null; ObjectOutputStream oos = null; ByteArrayInputStream bis = null; ObjectInputStream ois = null; try { // Create Output Stream bos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(bos); // Write this class of objects to the stream oos.writeObject(this); // Create Output Stream bis = new ByteArrayInputStream(bos.toByteArray()); ois = new ObjectInputStream(bis); // Write objects out of stream Person personClone = (Person) ois.readObject(); // Return Object return personClone; } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); return null; } finally { if (oos != null) { try { oos.close(); } catch (IOException e) { e.printStackTrace(); } } if (ois != null) { try { ois.close(); } catch (IOException e) { e.printStackTrace(); } } } } } /** * @author ZhongJing </p> * @Description </p> */ @Data @ToString @NoArgsConstructor @AllArgsConstructor public class Pets implements Serializable { /** * Name */ private String name; /** * Age */ private Integer age; }
Let's experiment:
/** * @author ZhongJing </p> * @Description </p> */ public class Client { public static void main(String[] args) { Person person = new Person(); person.setName("Zhang San"); person.setAge(18); person.setGender("male"); Pets pets = new Pets(); pets.setName("Prosperous Money"); pets.setAge(5); person.setPets(pets); System.out.println("Prototype:" + person); Person personClone = (Person) person.clonePerson(); System.out.println("Clone:" + personClone); pets.setName("Flowers like flowers"); System.out.println("Prototype:" + person); System.out.println("Clone:" + personClone); } }
The results are as follows:
Prototype: Person(name=Zhang San, age=18, gender=male, pets=Pets(name=Prosperous Money, age=5)) Clone: Person(name=Zhang San, age=18, gender=male, pets=Pets(name=Prosperous Money, age=5)) Prototype: Person(name=Zhang San, age=18, gender=male, pets=Pets(name=Flowers like flowers, age=5)) Clone: Person(name=Zhang San, age=18, gender=male, pets=Pets(name=Prosperous Money, age=5))
As you can see from the results, we also implemented deep copy using serialization
6. Summary
Because Java supports prototype patterns, prototype patterns are an easy thing to implement in Java.
The main idea of this blog is to distinguish the difference between shallow and deep copies, and the two ways Java implements deep copies. It's not just about design patterns (in my opinion). Well... that's it, Over!