23 design modes-prototype mode [clone sheep, shallow copy, deep copy]

Series of articles

23 design patterns-the purpose of design patterns and the seven principles to follow
23 design patterns-singleton mode [hungry, lazy, double check, static internal classes, enumeration]
23 design patterns-factory mode [Simple factory, factory method, abstract factory]
23 design modes-prototype mode [clone sheep, shallow copy, deep copy]


3. Prototype mode

3.1. Clone sheep in traditional way

Now there is a sheep named Tom, age 2 years old, and color white. We use the program to create 10 sheep with exactly the same attributes as Tom.

The original idea: directly new ten times

Insert picture description here

package design_partten.prototype.type1;

public class Sheep {
    
    
    private String name;
    private int age;
    private String color;
    //省略构造方法、set、get
}

public class Client {
    
    
    public static void main(String[] args) {
    
    
        Sheep sheep1 = new Sheep("Tom", 3, "白色");
        Sheep sheep2 = new Sheep(sheep1.getName(), sheep1.getAge(), sheep1.getColor());
        Sheep sheep3 = new Sheep(sheep1.getName(), sheep1.getAge(), sheep1.getColor());
        Sheep sheep4 = new Sheep(sheep1.getName(), sheep1.getAge(), sheep1.getColor());
        Sheep sheep5 = new Sheep(sheep1.getName(), sheep1.getAge(), sheep1.getColor());
        //...
    }
}

Advantages and disadvantages:

  • Better understanding, simple and easy to operate

  • When creating a new object, you always need to re-acquire the properties of the original object. If the created object is more complex, the efficiency is very low.

  • The object always needs to be reinitialized, and the runtime state of the object cannot be dynamically obtained (for example, when a new attribute is added to the original class, the code of the new class must be changed), which is not flexible enough.

  • Improvement ideas:

    The Object class in Java is the root class of all classes. It provides the clone() method, which can make a copy of a Java object. We need to implement the Cloneable interface, which means that this class can be copied and has the ability to copy. This leads to the prototype model.

 

3.2, clone sheep in prototype mode

  • Prototype mode (Prototype): Use prototype instances to specify the types of objects to be created, and create new objects by copying these prototypes.

  • How it works: Pass a prototype object to the object to be created, and the object to be created requests to copy themselves to implement the creation, that is, the object.clone()

Insert picture description here

Sheep

public class Sheep implements Cloneable {
    
    
    private String name;
    private int age;
    private String color;
    private String address="内蒙古";
	//省略构造方法、set、get、toString
    @Override
    protected Object clone() throws CloneNotSupportedException {
    
    
        Sheep sheep = null;
        try {
    
    
            sheep = (Sheep)super.clone();
        }catch (Exception e){
    
    
            e.getStackTrace();
        }
        return sheep;
    }
}

test

public class Client {
    
    
    public static void main(String[] args) {
    
    
        Sheep sheep1 = new Sheep("Tom", 3, "白色");
        Sheep sheep2 = (Sheep)sheep1.clone();
        Sheep sheep3 = (Sheep)sheep1.clone();
        //...
        System.out.println(sheep2);
    }
}

result:

Insert picture description here

Conclusion: Cloning sheep through the prototype mode is more flexible than the traditional method, it will directly clone the value of the attribute.

 

3.3. Source code analysis of the prototype pattern in Spring

In dependency injection, we can choose the prototype mode.

Insert picture description here

Enter debug and see how we get the bean.

Insert picture description here

There is a bean factory here, go in and take a look.

Insert picture description here

There is also a doGetBean, which should be the most important one. After entering doGetBean, you can see the prototype mode we set inside.

Insert picture description here

He will judge whether it is a singleton, whether it is a prototype

Insert picture description here

 

3.4, deep copy

The member variables of our cloned sheep are all basic types. What if there is an object in the member?

If the member variable is an object, it will not create a new member object if it is copied in the previous way. For example, the following.

public class Sheep implements Cloneable {
    
    
    private String name;
    private int age;
    private String color;
    private String address="内蒙古";
    private Sheep friend;
    //....
}

public class Client {
    
    
    public static void main(String[] args) {
    
    
        Sheep sheep1 = new Sheep("Tom", 3, "白色");
        sheep1.setFriend(new Sheep("Bob", 2, "黑色"));
        Sheep sheep2 = (Sheep) sheep1.clone();
        Sheep sheep3 = (Sheep) sheep1.clone();
        //...
        System.out.println(sheep1.getFriend().hashCode());
        System.out.println(sheep2.getFriend().hashCode());
        System.out.println(sheep3.getFriend().hashCode());
    }
}

Insert picture description here

The friends of the three sheep are all the same object, their reference addresses are the same, this is called shallow copy (the default use of clone() is shallow copy), in this case, modifying the friend of one clone will affect other clones. . In contrast, when we copy, the members of its object type are also copied, which is called deep copy.

Basic introduction of deep copy:

  • Copy the member variable values ​​of all basic data types of the object
  • Apply for storage space for all member variables of the reference data type, and copy the objects referenced by each member variable of the reference data type. In other words, a deep copy of an object will copy all member variables.
  • Deep copy implementation: 1. Re-clone() method; 2. Realize through object serialization.

Method 1: Re-clone() method (I am too lazy to write set and get here, just declare the variable as public)

package design_partten.prototype.type1.improve;

class Bird implements Cloneable{
    
    
    public String name;

    public Bird(String name) {
    
    
        this.name = name;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
    
    
        return super.clone();
    }
}

public class Sheep implements Cloneable {
    
    
    public String name;
    public int age;
    public String color;
    public Bird friend;

    public Sheep(String name, int age, String color, Bird friend) {
    
    
        this.name = name;
        this.age = age;
        this.color = color;
        this.friend = friend;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
    
    

        //1. 先拷贝基本数据类型的成员变量
        Sheep sheep = (Sheep) super.clone();

        //2. 拷贝引用类型的成员变量(即对引用类型成员再进行一次拷贝,然后赋值给刚刚拷贝的克隆羊)
        Bird friend = (Bird) this.friend.clone();
        sheep.friend = friend;

        return sheep;
    }
}

test:

public class Client {
    
    
    public static void main(String[] args) throws CloneNotSupportedException {
    
    
        Sheep sheep1 = new Sheep("Tom", 3, "白色", new Bird("鸟"));
        Sheep sheep2 = (Sheep) sheep1.clone();
        Sheep sheep3 = (Sheep) sheep1.clone();
        //...
        System.out.println(sheep1.friend.hashCode());
        System.out.println(sheep2.friend.hashCode());
        System.out.println(sheep3.friend.hashCode());
    }
}

result:

Insert picture description here

Evaluation: Deep copy is achieved by re-clone() method. Although it looks simple and has less code, it is because we have only one member variable of reference type. If many member variables are of reference type, then each of our variables Have to write. Not recommended.

Method 2: Realize through object serialization (recommended)

//在Sheep类添加该方法,且Sheep和Bird实现Serialized接口
public Object deepClone(){
    
    
    //创建字节数组输出流对象
    ByteArrayOutputStream bos = null;
    ObjectOutputStream oos = null;
    ByteArrayInputStream bis = null;
    ObjectInputStream ois = null;

    try {
    
    
        //序列化
        bos = new ByteArrayOutputStream();
        oos = new ObjectOutputStream(bos);
        oos.writeObject(this);//当前这个对象以对象流的方式输出

        //反序列化
        bis = new ByteArrayInputStream(bos.toByteArray());
        ois = new ObjectInputStream(bis);
        Sheep sheep = (Sheep)ois.readObject();

        return sheep;
    }catch (Exception e){
    
    
        e.getStackTrace();
        return null;
    }finally {
    
    
        //关闭流
        try {
    
    
            ois.close();
            bis.close();
            oos.close();
            bos.close();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }
}

 

3.5 Summary

  • When creating a new object is more complicated, you can use the prototype mode to simplify the creation process of the object, while also improving efficiency.
  • There is no need to reinitialize the object, but dynamically obtain the runtime state of the object, that is, if the properties of the object change later, the object after the property change will be obtained by cloning, rather than the original object.
  • If the original object changes (increase or decrease attributes), other cloned objects will also change accordingly, without modifying the code.
  • Disadvantages: need to increase the clone method, which is no problem for a new class, but for a previously created class, this violates the OCP principle!

Guess you like

Origin blog.csdn.net/qq_39763246/article/details/114582595