An overview,

Prototype mode is also a creation mode, which is mainly used for object cloning. It creates new objects by copying a prototype. This new object has the same internal properties as the prototype, and each cloned object is an independent object. If an object reference has many resources and a new object needs to be created, it is more efficient to create it in a cloned mode. Object cloning in prototype mode is divided into deep clone and shallow clone. Deep clone can also be called deep copy, and shallow clone can also be called shallow copy.

Second, the use of

1. Java implementation

To clone an Object, you must go through two steps: implementing the java.lang.cloneable interface and overriding the Clone () method in the java.lang.object class.

ⅰ. Shallow clone (shallow copy)

This starts by creating a new class PersonForJava that can be used to create cloned objects. In the class we create attributes for the basic data types of name, age, and weight, as well as reference type attributes for the list of interests and hobbies and home addresses.

public class PersonForJava implements Cloneable {

    private String name;
    private int age;
    private double weight;
    // List of interests
    private ArrayList<String> hobbyList = new ArrayList<>();
    / / address
    private Address address;

    public PersonForJava(a){
        Log.e("XXX"."Constructor of PersonForJava class called");
    }

    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;
    }

    public double getWeight(a) {
        return weight;
    }

    public void setWeight(double weight) {
        this.weight = weight;
    }

    public void addHobby(String hobby) {
        hobbyList.add(hobby);
    }

    public ArrayList<String> getHobbyList(a) {
        return hobbyList;
    }

    public Address getAddress(a) {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public String showAll(a){
        return "name: " + name + ",age: " + age + ",weight: " + weight + ",hobbyList: " + hobbyList + ",address: " + address.myAddress;
    }

    @Override
    protected Object clone(a) throws CloneNotSupportedException {
        return super.clone(); }}Copy the code

The Address class is simple, with only one Address.

public class Address{
    public String myAddress;
}
Copy the code

Next, create a PersonForJava object naturally and set the values of the related properties:

PersonForJava personForJava1 = new PersonForJava();
personForJava1.setName("Zhang");
personForJava1.setAge(26);
personForJava1.setWeight(141.12);
personForJava1.addHobby("Basketball");
personForJava1.addHobby("Football");
personForJava1.addHobby("Volleyball");
Address address = new Address();
address.myAddress = "Chengdu, Sichuan Province";
personForJava1.setAddress(address);
Copy the code

PersonForJava2: personForJava2: personForJava2

try {
    PersonForJava personForJava2 = (PersonForJava) personForJava1.clone();
    Log.e(TAG, "personForJava1:" + personForJava1);
    Log.e(TAG, "personForJava2:" + personForJava2);
    Log.e(TAG, "personForJava1 ---> " + personForJava1.showAll());
    Log.e(TAG, "personForJava2 ---> " + personForJava2.showAll());
    Log.e(TAG, "PersonForJava1. GetHobbyList () = = personForJava2. GetHobbyList () :" + (personForJava1.getHobbyList() == personForJava2.getHobbyList()));
    Log.e(TAG, "PersonForJava1. GetAddress () = = personForJava2. GetAddress () :" + (personForJava1.getAddress() == personForJava2.getAddress()));
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
    Log.e(TAG, "Object clone failed --1");
}
Copy the code

First print out the object addresses for personForJava1 and personForJava2, then call the showAll method to display all the attributes, resulting in the following output. You can see that the two objects are not the same, but have the same properties. And the constructor is called only once, indicating that object cloning does not create objects through the constructor. Finally, the hobbyList and address attributes are compared to have the same address, which indicates that objects in shallow clones have the same reference type.

Next, modify all the attribute values of the personForJava2 object and call the showAll method to display all the attributes. Rerun the program to get the following output. As can be seen from the figure, the attribute value of the basic data type has not changed, but the reference type value has changed. As can be seen from the previous point, because the different objects cloned are pointing to the same reference type, So when personForJava2 changes the hobbyList and the contents of the Address property, the value in personForJava1 also changes. The reason why the name and age are not changed is because they are value types and do not need to be cloned. After the clone object is cloned, it is returned to the client object directly.

personForJava2.setName("Bill");
personForJava2.setAge(28);
personForJava2.setWeight(136.78);
personForJava2.addHobby("Table tennis");
address.myAddress = "Mianyang, Sichuan Province";
personForJava2.setAddress(address);
Log.e(TAG, "personForJava1 ---> " + personForJava1.showAll());
Log.e(TAG, "personForJava2 ---> " + personForJava2.showAll());
Copy the code

In summary, we can conclude from the above that when the object has the attribute of reference type, when the clone object changes the value of reference type, it will directly affect the value of the prototype object, which is unreliable and has hidden trouble for us, so we need to carry out deep cloning. Here is the full test code:

/** * shallow clone */
 private void shallowClone(a){
    PersonForJava personForJava1 = new PersonForJava();
    personForJava1.setName("Zhang");
    personForJava1.setAge(26);
    personForJava1.setWeight(141.12);
    personForJava1.addHobby("Basketball");
    personForJava1.addHobby("Football");
    personForJava1.addHobby("Volleyball");
    Address address = new Address();
    address.myAddress = "Chengdu, Sichuan Province";
    personForJava1.setAddress(address);

    try {
        PersonForJava personForJava2 = (PersonForJava) personForJava1.clone();
        Log.e(TAG, "personForJava1:" + personForJava1);
        Log.e(TAG, "personForJava2:" + personForJava2);
        Log.e(TAG, "personForJava1 ---> " + personForJava1.showAll());
        Log.e(TAG, "personForJava2 ---> " + personForJava2.showAll());
        Log.e(TAG, "PersonForJava1. GetHobbyList () = = personForJava2. GetHobbyList () :" + (personForJava1.getHobbyList() == personForJava2.getHobbyList()));
        Log.e(TAG, "PersonForJava1. GetAddress () = = personForJava2. GetAddress () :" + (personForJava1.getAddress() == personForJava2.getAddress()));

        Log.e(TAG, "-- -- -- -- -- -- -- -- -- -- -- -- --");

        personForJava2.setName("Bill");
        personForJava2.setAge(28);
        personForJava2.setWeight(136.78);
        personForJava2.addHobby("Table tennis");
        address.myAddress = "Mianyang, Sichuan Province";
        personForJava2.setAddress(address);
        Log.e(TAG, "personForJava1 ---> " + personForJava1.showAll());
        Log.e(TAG, "personForJava2 ---> " + personForJava2.showAll());
    } catch (CloneNotSupportedException e) {
        e.printStackTrace();
        Log.e(TAG, "Object clone failed --1"); }}Copy the code

ⅱ. Deep clone (deep copy)

From the shallow clone, we know that the copy of the base type is complete, but for the reference type, just a copy of the address pointing to the reference type of the prototype object is made. So deep cloning is to solve this problem. In the shallow clone, we simply rewrote the clone() method and returned to call the clone method of the parent class directly, without making specific operations of our own in the method.

Deep clone, when clone reference type member variable, for reference type data member another created an independent memory space, realize the true content of the clone.

If the reference type is a custom class, you need to implement the java.lang.cloneable interface and re-clone () methods in the custom class, such as the Address class. The Array and list system has already overridden the Clone method for us, without us having to do it ourselves.

public class Address implements Cloneable{

    public String myAddress;

    @Override
    protected Object clone(a) throws CloneNotSupportedException {
        return super.clone(); }}Copy the code

Most importantly, in the clone() method of the prototype object, the reference type needs to be cloned as follows.

@Override
protected Object clone(a) throws CloneNotSupportedException {
    PersonForJava personForJava = (PersonForJava) super.clone();
    personForJava.hobbyList = (ArrayList<String>) personForJava.getHobbyList().clone();
    personForJava.address = (Address) personForJava.getAddress().clone();
    return personForJava;

// return super.clone();
}
Copy the code

Next, as before, we naturally create a prototype object and set the property values.

PersonForJava personForJava3 = new PersonForJava();
personForJava3.setName("Fifty");
personForJava3.setAge(22);
personForJava3.setWeight(149.71);
personForJava3.addHobby(Honor of Kings);
personForJava3.addHobby("Crossing the Line of Fire");
personForJava3.addHobby("Fight the Landlord");
Address address = new Address();
address.myAddress = "Dazhou, Sichuan Province";
personForJava3.setAddress(address);
Copy the code

PersonForJava4: personForJava4: personForJava4:

try {
    PersonForJava personForJava4 = (PersonForJava) personForJava3.clone();
    Log.e(TAG, "personForJava3:" + personForJava3);
    Log.e(TAG, "personForJava4:" + personForJava4);
    Log.e(TAG, "personForJava3 ---> " + personForJava3.showAll());
    Log.e(TAG, "personForJava4 ---> " + personForJava4.showAll());
    Log.e(TAG, "PersonForJava3. GetHobbyList () = = personForJava4. GetHobbyList () :" + (personForJava3.getHobbyList() == personForJava4.getHobbyList()));
    Log.e(TAG, "PersonForJava3. GetAddress () = = personForJava4. GetAddress () :" + (personForJava3.getAddress() == personForJava4.getAddress()));
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
    Log.e(TAG, "Object clone failed --2");
}
Copy the code

First print out the object addresses for personForJava3 and personForJava4, then call the showAll method to display all the attributes, resulting in the following output. You can see that the two objects are not the same, but have the same properties. And the constructor is called only once, indicating that object cloning does not create objects through the constructor. Finally, the hobbyList and address attributes are compared to different addresses, which indicates that the object reference types in the clone are no longer the same.

Next, modify all the attribute values of the personForJava4 object and call the showAll method to display all the attributes. Rerun the program to get the following output. As you can see from the figure, when the personForJava4 clone changes the value of the attribute, either for the base type or for the reference type, it does not affect the value of the attribute in personForJava3.

personForJava4.setName("Six sun");
personForJava4.setAge(24);
personForJava4.setWeight(121.13);
personForJava4.addHobby("Dungeons and Warriors.");
Address arrr = personForJava4.getAddress();
arrr.myAddress = "Yibin, Sichuan Province";
personForJava4.setAddress(arrr);
Log.e(TAG, "personForJava3 ---> " + personForJava3.showAll());
Log.e(TAG, "personForJava4 ---> " + personForJava4.showAll());
Copy the code

In summary, we can conclude from the above that the deep-cloned object has its own memory space for reference types, so it does not affect the prototype object. Here is the full test code:

/** ** deep clone */
private void deepClone(a){
    PersonForJava personForJava3 = new PersonForJava();
    personForJava3.setName("Fifty");
    personForJava3.setAge(22);
    personForJava3.setWeight(149.71);
    personForJava3.addHobby(Honor of Kings);
    personForJava3.addHobby("Crossing the Line of Fire");
    personForJava3.addHobby("Fight the Landlord");
    Address address = new Address();
    address.myAddress = "Dazhou, Sichuan Province";
    personForJava3.setAddress(address);

    try {
        PersonForJava personForJava4 = (PersonForJava) personForJava3.clone();
        Log.e(TAG, "personForJava3:" + personForJava3);
        Log.e(TAG, "personForJava4:" + personForJava4);
        Log.e(TAG, "personForJava3 ---> " + personForJava3.showAll());
        Log.e(TAG, "personForJava4 ---> " + personForJava4.showAll());
        Log.e(TAG, "PersonForJava3. GetHobbyList () = = personForJava4. GetHobbyList () :" + (personForJava3.getHobbyList() == personForJava4.getHobbyList()));
        Log.e(TAG, "PersonForJava3. GetAddress () = = personForJava4. GetAddress () :" + (personForJava3.getAddress() == personForJava4.getAddress()));

        Log.e(TAG, "-- -- -- -- -- -- -- -- -- -- -- -- --");

        personForJava4.setName("Six sun");
        personForJava4.setAge(24);
        personForJava4.setWeight(121.13);
        personForJava4.addHobby("Dungeons and Warriors.");
        Address arrr = personForJava4.getAddress();
        arrr.myAddress = "Yibin, Sichuan Province";
        personForJava4.setAddress(arrr);
        Log.e(TAG, "personForJava3 ---> " + personForJava3.showAll());
        Log.e(TAG, "personForJava4 ---> " + personForJava4.showAll());
    } catch (CloneNotSupportedException e) {
        e.printStackTrace();
        Log.e(TAG, "Object clone failed --2"); }}Copy the code

2, Kotlin implementation

ⅰ. Shallow clone (shallow copy)

  1. Implement Cloneable interface

Instead of implementing the java.lang.Cloneable interface in Java, we would implement the Kotlin.cloneable interface in Kotlin. Interface, and override the clone() method, which must manually use the public modifier. The code is posted for reference only. It is important to note that since all classes are in the same package under the same project, the class name is visible in order to distinguish the Java class from kotlin.

class PersonForKotlin : Cloneable {

    var name: String? = null
    var age: Int? = null
    var weight: Double? = null

    var address = AddressForKotlin()
    var hobbyList = arrayListOf<String>()

    fun showAll(a) : String{
        return "name: " + name + ",age: " + age + ",weight: " + weight + ",hobbyList: " + hobbyList + ",address: " + address.myAddress;
    }

    public override fun clone(a): Any {
// var tempPerson = PersonForKotlin()
// try {
// tempPerson = super.clone() as PersonForKotlin
// tempPerson.address = address.clone() as AddressForKotlin
// tempPerson.hobbyList = hobbyList.clone() as ArrayList
      
// } catch (e: Exception) {
// e.printStackTrace()
/ /}
// return tempPerson

        return super.clone()
    }
}
Copy the code

Then there is the AddressForKotlin class:

class AddressForKotlin: Cloneable {

    var myAddress : String? = null

    override fun clone(a): Any {
        return super.clone()
    }
}
Copy the code

Here’s how to test it.

/** * shallow clone */
private fun shallowClone(a){
    val p1 = PersonForKotlin()
    p1.name = "Zhang"
    p1.age = 24
    p1.weight = 133.12
    p1.hobbyList.add("Diving")
    p1.hobbyList.add("Skating")
    p1.hobbyList.add("Gymnastics")
    val address1 = AddressForKotlin()
    address1.myAddress = Wuhan city, Hubei Province
    p1.address = address1

    val p2 = p1.clone() as PersonForKotlin
    println("P1:$p1")
    println("P2:$p2")
    println("p1 ---> " + p1.showAll())
    println("p2 ---> " + p2.showAll())
    println("HobbyList === p2.hobbyList:" + (p1.hobbyList === p2.hobbyList))
    println("HobbyList == p2.hobbyList:" + (p1.hobbyList == p2.hobbyList))
    println("p1.address == p2.address:" + (p1.address == p2.address))
    println("p1.address === p2.address:" + (p1.address === p2.address))

    println("-- -- -- -- -- -- -- -- -- -- -- -- --")

    p2.name = "Bill"
    p2.age = 28
    p2.weight = 136.78
    p2.hobbyList.add("Swimming")
    address1.myAddress = "Yichang, Hubei Province"
    p2.address = address1
    println("p1 ---> " + p1.showAll())
    println("p2 ---> " + p2.showAll())
}
Copy the code

The result is as follows, which you can see is the same as in Java. By cloning, the objects obtained are not the same object, but have the same attributes, and the reference attributes point to the same, so when P2 modifies the attributes of the reference type of the list of interests and home address, P1 will also change.

  1. Use the data of the class

In fact, Kotlin has already made encapsulation for shallow cloning, that is, the use of data class data class copy method. Data classes are also syntactic sugar for various JavaBean objects, so instead of writing a bunch of Get /set methods like Java, you can just add one line of code if you don’t need to add extra methods. The specific use of data classes is not the focus of this article and will not be explained too much. Here we create a data class MyDataClass:

data class MyDataClass(var name: String, var age: Int.var weight: Double.var hobbyList: ArrayList<String>, var address: AddressForKotlin){
    fun showAll(a) : String{
        return Name: ""$name, the age:$ageWeight:$weight, hobbyList:$hobbyList, address:${address.myAddress}"; }}Copy the code

A showAll method was added to the class to print all the data. So let’s test it out.

private fun useDataClass(){
    val hList = arrayListOf("Shoot"."Horse"."Fencing")
    val addr = AddressForKotlin()
    addr.myAddress = "Shu"
    val myDataClass1 = MyDataClass("Zhaoyun",31,153.66, hList,addr) val myDataClass2 = mydataclass1. copy() println("MyDataClass1:${myDataClass1.showAll()}")
    println("MyDataClass2:${myDataClass2.showAll()}")
    println("myDataClass1.hobbyList === myDataClass2.hobbyList:" + (myDataClass1.hobbyList === myDataClass2.hobbyList))
    println("myDataClass1.hobbyList == myDataClass2.hobbyList:" + (myDataClass1.hobbyList == myDataClass2.hobbyList))
    println(Address == myDataclass2.address: + (myDataClass1.address == myDataClass2.address))
    println(Address === myDataclass2.address: + (myDataClass1.address === myDataClass2.address))

    println("-- -- -- -- -- -- -- -- -- -- -- -- --")

    myDataClass2.name = "Tracing the cause"MyDataClass2. Age = 38 myDataClass2. Weight = 145.91 myDataClass2. HobbyList. Add ("Fire")
    addr.myAddress = "吴国"
    myDataClass2.address = addr
    println("MyDataClass1:${myDataClass1.showAll()}")
    println("MyDataClass2:${myDataClass2.showAll()}")}Copy the code

Run the program and get the following output. The copy() method is called from the myDataClass1 object, returning a myDataClass2 object. As you can see from the print-out, myDataClass1 and myDataClass2 are not the same object, but have the same attributes, and the reference type is pointed to the same. When myDataClass2 changes the value of the reference type attribute, myDataClass1 will also change.

ⅱ. Deep clone (deep copy)

Kotlin’s deep clone is the same as Java. If the attribute has a reference type, in the case of the PersonForKotlin class, the clone() method needs to be overridden. There are the AddressForKotlin home address property and the arrayListOf() list property, two referenced properties. We need to rewrite the Clone method for each reference type, which the arrayListOf() system has already done for us, and we need to rewrite the AddressForKotlin class.

public override fun clone(a): Any {
    var tempPerson = PersonForKotlin()
    try {
        tempPerson = super.clone() as PersonForKotlin
        tempPerson.address = address.clone() as AddressForKotlin
        tempPerson.hobbyList = hobbyList.clone() as ArrayList<String>
    } catch (e: Exception) {
        e.printStackTrace()
    }
    return tempPerson

// return super.clone()
}
Copy the code

The specific usage of deep clone and shallow clone is exactly the same, not separate paste code.

class PrototypeModeKotlinActivity : Activity() {
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(r.layout.activity_prototype_mode) // shallowClone() // useDataClass() deepClone()} // private funshallowClone(){
        val p1 = PersonForKotlin()
        p1.name = "Zhang"P1.age = 24 p1.weight = 133.12 p1.hobbylist.add ("Diving")
        p1.hobbyList.add("Skating")
        p1.hobbyList.add("Gymnastics")
        val address1 = AddressForKotlin()
        address1.myAddress = Wuhan city, Hubei Province
        p1.address = address1

        val p2 = p1.clone() as PersonForKotlin
        println("P1:$p1")
        println("P2:$p2")
        println("p1 ---> " + p1.showAll())
        println("p2 ---> " + p2.showAll())
        println("HobbyList === p2.hobbyList:" + (p1.hobbyList === p2.hobbyList))
        println("HobbyList == p2.hobbyList:" + (p1.hobbyList == p2.hobbyList))
        println("p1.address == p2.address:" + (p1.address == p2.address))
        println("p1.address === p2.address:" + (p1.address === p2.address))

        println("-- -- -- -- -- -- -- -- -- -- -- -- --")

        p2.name = "Bill"P2.age = 28 p2.weight = 136.78 p2.hobbylist.add ("Swimming")
        address1.myAddress = "Yichang, Hubei Province"
        p2.address = address1
        println("p1 ---> " + p1.showAll())
        println("p2 ---> " + p2.showAll())
    }

    private fun useDataClass(){
        val hList = arrayListOf("Shoot"."Horse"."Fencing")
        val addr = AddressForKotlin()
        addr.myAddress = "Shu"
        val myDataClass1 = MyDataClass("Zhaoyun",31,153.66, hList,addr) val myDataClass2 = mydataclass1. copy() println("MyDataClass1 = = = myDataClass2:${myDataClass1 === myDataClass2}")
        println("MyDataClass1:${myDataClass1.showAll()}")
        println("MyDataClass2:${myDataClass2.showAll()}")
        println("myDataClass1.hobbyList === myDataClass2.hobbyList:" + (myDataClass1.hobbyList === myDataClass2.hobbyList))
        println("myDataClass1.hobbyList == myDataClass2.hobbyList:" + (myDataClass1.hobbyList == myDataClass2.hobbyList))
        println(Address == myDataclass2.address: + (myDataClass1.address == myDataClass2.address))
        println(Address === myDataclass2.address: + (myDataClass1.address === myDataClass2.address))

        println("-- -- -- -- -- -- -- -- -- -- -- -- --")

        myDataClass2.name = "Tracing the cause"MyDataClass2. Age = 38 myDataClass2. Weight = 145.91 myDataClass2. HobbyList. Add ("Fire")
        addr.myAddress = "吴国"
        myDataClass2.address = addr
        println("MyDataClass1:${myDataClass1.showAll()}")
        println("MyDataClass2:${myDataClass2.showAll()}"} /** ** private fundeepClone(){
        val p3 = PersonForKotlin()
        p3.name = "Zhang"P3.age = 24 p3.weight = 133.12p3.hobbylist.add ("Diving")
        p3.hobbyList.add("Skating")
        p3.hobbyList.add("Gymnastics")
        val address1 = AddressForKotlin()
        address1.myAddress = Wuhan city, Hubei Province
        p3.address = address1

        val p4 = p3.clone() as PersonForKotlin
        println("P3:$p3")
        println("P4.$p4")
        println("p3 ---> " + p3.showAll())
        println("p4 ---> " + p4.showAll())
        println(P4. hobbyList == p4.hobbyList: + (p3.hobbyList === p4.hobbyList))
        println("p3.address == p4.address:" + (p3.address === p4.address))

        println("-- -- -- -- -- -- -- -- -- -- -- -- --")

        p4.name = "Bill"P4.age = 28 p4.weight = 136.78 p4.hobbylist.add ("Swimming")
        p4.address.myAddress = "Yichang, Hubei Province"
        println("p3 ---> " + p3.showAll())
        println("p4 ---> " + p4.showAll())
    }
}
Copy the code

Third, summary

The prototype pattern is to take an object as a prototype, and when a new object needs to be created, a new object is cloned by cloning method. And clone is divided into shallow clone and deep clone. The shallow clone only passes the address pointing, and the new object does not create memory space for the reference data type, so when the clone object changes the value of the reference type, the value in the prototype object will also change. Deep-cloning creates memory space for all objects in the object graph that reference a member variable of a data type, so modifications to the properties of the reference type in the clone do not affect the values in the prototype object. Because of this feature of deep cloning, when there are multiple layers of objects, it is necessary to implement Cloneable interface and rewrite clone method for each layer of objects, so as to achieve multiple layers of object cloning, so the cost is higher than that of shallow cloning.

Github code: github.com/leewell5717…

Four, reference

Java shallow copy and deep copy

Android Prototype Mode (light and dark copy)

Prototype for Android Kotlin design Pattern