Design Mode – Prototype Mode (2)
Become as
Hello everyone, I am silent. In this class, we are going to talk a little bit more about the prototype pattern. We are going to talk about deep copy and shallow copy
Antecedents feed
Last time, we talked about Xiao Li’s job search, and we wrote a case study about the prototype pattern. Did you notice that when we used the prototype pattern to create a Resume object, the properties of the class Resume were all strings, in other words, simple types. So let’s say that now we add a property, There needs to be a reference on the resume, which is the object of Witness type. Then, let’s think about this question: when the prototype copy is made again, how will the Witness attribute be handled? Should I make a copy or make a reference to an existing Witness? Realizing this is a matter of practice, so let’s test it with the case code
Case presentation
/ / resume classes
public class Resume implements Cloneable{
private String name;
private String position;
private String salary;
public Witness witness; / / references
// constructor /get/set/toString... omit
}
// Prove human
public class Witness {
private String name;
private String job; / / position
// constructor /get/set/toString... omit
}
// Test using prototype mode
public class Client {
public static void main(String[] args) {
Resume resume = new Resume("Xiao li".Haidian District."Negotiable");
resume.setWitness(new Witness("Uncle"."CTO pig Farm"));
Resume clone = (Resume)resume.clone(); // Clone two copies using prototype mode
Resume clone1 = (Resume)resume.clone();
Resume clone2 = (Resume)resume.clone();
System.out.println(clone.getWitness().hashCode()); / / testSystem.out.println(clone1.getWitness().hashCode()); System.out.println(clone2.getWitness().hashCode()); }}Copy the code
As you can see, we have cloned three copies using prototype mode and found that the hashes of the three member variables witness are the same. That is to say, by default, witness is not copied, but a reference is made to the existing witness. This situation is called shallow copy
Shallow copy
Next, let’s take a look at the introduction of shallow copy
For member variables whose data type is a primitive data type, the shallow copy passes the value directly, by making a copy of the property value to the new object
For a data type is a member of the reference data type variables, such as a member variable is an array, a class of objects and so on, the shallow copy will be for reference, which is just the member variables (memory address) a copy of reference values to the new object, actually two objects of the member variables point to the same instance, this kind of circumstance, Modifying a member variable in one object affects the value of the member variable in another object
Therefore, the case of Xiao Li’s job search mentioned above is a shallow copy, which is implemented using the default Clone () method
What if, in the real world, we want to make a copy of a member variable whose data type is a reference data type? So let’s talk a little bit about deep copy and how deep copy is implemented
Deep copy
The basic introduction of deep copy is as follows
Copy the member variable values of all the base data types of the object
Allocates storage space for all reference data type member variables, and copies the object referenced by each reference data type member variable until the object is reachable to all objects. In other words, to make a deep copy of an object, the entire object (including the reference type of the object) is copied
Deep copy is actually implemented in two ways, either by overriding the Clone () method or by object serialization
Implementation method 1: Rewrite clone() method
The sample code is shown below
public class DeepCloneableTarget implements Serializable.Cloneable {
private static final long serialVersionUID = 1L;
private String cloneName;
private String cloneClass;
public DeepCloneableTarget(String cloneName, String cloneClass) {
this.cloneName = cloneName;
this.cloneClass = cloneClass;
}
// Since the class attributes are String, we use the default clone method to do just that
@Override
protected Object clone(a) throws CloneNotSupportedException {
return super.clone(); }}public class DeepProtoType implements Serializable.Cloneable {
private String name; / / type String
private DeepCloneableTarget deepCloneableTarget; // Reference type
public DeepProtoType(a) {
super(a); }// Deep copy - Method 1: Override the clone method
@Override
protected Object clone(a) throws CloneNotSupportedException {
Object deep = null;
// Copy the basic data type and the String type
deep = super.clone();
// The attributes of the reference type are processed separately
DeepProtoType deepProtoType = (DeepProtoType) deep;
deepProtoType.deepCloneableTarget= (DeepCloneableTarget) deepCloneableTarget.clone();
returndeepProtoType; }}public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
DeepProtoType deepProtoType = new DeepProtoType();
deepProtoType.name = "Xiao li";
deepProtoType.deepCloneableTarget = new DeepCloneableTarget("老李"."Clone class");
// Mode one completes the deep copy
DeepProtoType clone = (DeepProtoType) deepProtoType.clone();
DeepProtoType clone1 = (DeepProtoType) deepProtoType.clone();
System.out.println(clone.deepCloneableTarget.hashCode());
System.out.println(clone1.deepCloneableTarget.hashCode()); // The hash values are different}}Copy the code
As you can see, when tested, the hash values are different, indicating that the entire object (including the reference type of the object) has indeed been copied, implementing deep copy
Clone () calls super.clone() to clone primitives and String primitives in the same way. The clone() method is used to copy the value of the property to the new object, and then the properties of the reference type are processed separately
DeepProtoType uses clone() to copy member values of multiple reference types. What if a reference type is a class that still has member attributes for the reference type?
Cascade processing demonstration
If the reference type is a class, it still has the member attributes of the reference type in it. Again, the idea is to distribute them separately. Let’s say that class DeepProtoType has the member attribute deepCloneableTarget, The DeepCloneableTarget class has member attributes witness, and the clone() method is overridden to complete the deep copy. The sample code is shown in the figure below
// Mode 1 Upgrade: Test the cascading clone
public class DeepProtoType implements Serializable.Cloneable {
public String name; / / type String
public DeepCloneableTarget deepCloneableTarget; // Reference type
public DeepProtoType(a) {
super(a); }// Deep copy - Method 1: Override the clone method for cascading
@Override
protected Object clone(a) throws CloneNotSupportedException {
Object deep = null;
// Copy the basic data type and the String type
deep = super.clone();
// The attributes of the reference type are processed separately
DeepProtoType deepProtoType = (DeepProtoType) deep;
deepProtoType.deepCloneableTarget= (DeepCloneableTarget) deepCloneableTarget.clone();
deepProtoType.deepCloneableTarget.witness = (Witness) deepProtoType.deepCloneableTarget.witness.clone();
return deepProtoType;
}
@Override
public String toString(a) {
return "DeepProtoType{" +
"name='" + name + '\' ' +
", deepCloneableTarget=" + deepCloneableTarget +
'} '; }}public class DeepCloneableTarget implements Serializable.Cloneable {
private static final long serialVersionUID = 1L;
private String cloneName;
private String cloneClass;
public Witness witness; // Add a reference type
public DeepCloneableTarget(String cloneName, String cloneClass) {
this.cloneName = cloneName;
this.cloneClass = cloneClass;
}
// Since the class attributes are String, we use the default clone method to do just that
@Override
protected Object clone(a) throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString(a) {
return "DeepCloneableTarget{" +
"cloneName='" + cloneName + '\' ' +
", cloneClass='" + cloneClass + '\' ' +
", witness=" + witness +
'} '; }}// Prove human
public class Witness implements Serializable.Cloneable {
private String name;
private String job; / / position
public Witness(String name, String job) {
this.name = name;
this.job = job;
}
public String getName(a) {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getJob(a) {
return job;
}
public void setJob(String job) {
this.job = job;
}
@Override
public String toString(a) {
return "Witness{" +
"name='" + name + '\' ' +
", job='" + job + '\' ' +
'} ';
}
@Override
protected Object clone(a) throws CloneNotSupportedException {
return super.clone(); }}// Mode 1 Upgrade: Test the cascading clone
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
DeepProtoType deepProtoType = new DeepProtoType();
deepProtoType.name = "Xiao li";
deepProtoType.deepCloneableTarget = new DeepCloneableTarget("老李"."Clone class");
deepProtoType.deepCloneableTarget.witness = new Witness("References"."Position");
DeepProtoType clone = (DeepProtoType) deepProtoType.clone();
DeepProtoType clone1 = (DeepProtoType) deepProtoType.clone();
System.out.println(clone);
System.out.println(clone1);
System.out.println(clone.deepCloneableTarget.witness.hashCode());
System.out.println(clone1.deepCloneableTarget.witness.hashCode());
// Test hash values are different to implement deep copy}}Copy the code
Implementation method two: through object serialization
Above our way demonstrates a, has realized the deep copy, but at the same time also poses a problem, if there are a lot of members of a reference type attributes, or (reference type is a class, there are still members of a reference type attribute) this cascade, need to deal with step by step, if you want to copy the object structure is complex, implement is very tedious, Is there a one-step solution? Let’s demonstrate the second, using the object serialization form, as shown in the example code
Shown below
public class DeepProtoType implements Serializable.Cloneable {
public String name; / / type String
public DeepCloneableTarget deepCloneableTarget; // Reference type
public DeepProtoType(a) {
super(a); }// Deep copy - Method 2: Serialize objects (recommended)
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);// Serialize the current object as a stream of objects.
// Include the reference type in the output
// deserialize
bis = new ByteArrayInputStream(bos.toByteArray());// Read the output object back in
ois = new ObjectInputStream(bis);
DeepProtoType clone = (DeepProtoType)ois.readObject();
// Take full advantage of serialization and deserialization, output the current object this as a stream of objects, and then read back as an object. The associated reference type is also read back
return clone;
} catch (Exception e) {
e.getMessage();
} finally {
// Close the stream operation
try {
bos.close();
oos.close();
bis.close();
ois.close();
} catch(IOException e) { e.getMessage(); }}return null;
}
@Override
public String toString(a) {
return "DeepProtoType{" +
"name='" + name + '\' ' +
", deepCloneableTarget=" + deepCloneableTarget +
'} '; }}public class DeepCloneableTarget implements Serializable.Cloneable {
/ * * * * /
private static final long serialVersionUID = 1L;
private String cloneName;
private String cloneClass;
public DeepCloneableTarget(String cloneName, String cloneClass) {
this.cloneName = cloneName;
this.cloneClass = cloneClass;
}
// Since the class attributes are String, we use the default clone method to do just that
@Override
protected Object clone(a) throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString(a) {
return "DeepCloneableTarget{" +
"cloneName='" + cloneName + '\' ' +
", cloneClass='" + cloneClass + '\' ' +
'} '; }}// Test method two through object serialization
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
DeepProtoType deepProtoType = new DeepProtoType();
deepProtoType.name = "Xiao li";
deepProtoType.deepCloneableTarget = new DeepCloneableTarget("老李"."Clone class");
DeepProtoType clone = (DeepProtoType)deepProtoType.deepClone();
DeepProtoType clone1 = (DeepProtoType)deepProtoType.deepClone();
System.out.println(clone.toString());
System.out.println(clone1.toString());
System.out.println(clone.deepCloneableTarget.hashCode());
System.out.println(clone1.deepCloneableTarget.hashCode()); // Test for hash inconsistencies}}Copy the code
As you can see, after the test, the hash values are not the same, that is indeed the entire object (including object reference type) have been copied, we also realized the deep copy by means of object serialization, and advantage of the characteristics of serialization and deserialization, the current object this output in the form of object flow, and then read back in the form of objects, This method is recommended because it serializes and deserializes the entire object directly. No matter how complex the structure of the class is, it can be handled by object serialization
Matters needing attention
This is the end of the prototype pattern, let’s summarize the considerations of the prototype pattern, analyze the advantages and disadvantages and application scenarios
advantage
When creating new objects is complicated, the prototype pattern can be used to simplify the object creation process and improve efficiency
Instead of reinitializing the object, the state of the object at run time is dynamically obtained
If the original object changes (adding or subtracting attributes), the other clones will change accordingly, without changing the code
disadvantage
We need to provide a clone method for each class, which is not difficult for a brand new class. However, when modifying an existing class, we need to modify its source code, which will violate the OCP principle. This point needs to be mentioned to our friends
Next day forecast
OK, in the next section, we will enter the learning mode of builder mode. I hope you can feel the interesting part of design mode in the learning process, and learn efficiently and happily. See you next time