Design Patterns - Prototype Patterns

1 Definition

The prototype pattern is to use a class instance to specify the kind of object to be created, and to create new objects by copying these prototypes. The prototype pattern requires an object to implement an interface that can "clone" itself, so that a new instance can be created by copying an instance object itself.

2 class diagram

 Abstract prototype class (Prototype): It is the interface that declares the clone method and is the common parent class of all concrete prototype classes. It can be an interface, an abstract class or even a concrete implementation class.

Concrete prototype class (ConcretePrototype): It implements the clone method declared in the abstract prototype class, and returns a clone object of its own in the clone method.

Client class (Client): In the client class, using the prototype object only needs to create or directly NEW (instantiate a) prototype object through the factory method, and then obtain multiple identical objects through the clone method of the prototype object. Since the client is programmed for abstract prototype objects, it can also be easily replaced with different types of prototype objects!

3 Pros and cons of prototype mode

As a way to quickly create a large number of identical or similar objects, the prototype mode is widely used in software development. The CTRL+C and CTRL+V operations provided by many software are typical applications of the prototype mode!

3.1 Advantages

When the created object instance is more complex, using the prototype pattern can simplify the object creation process!

Good extensibility, because the abstract prototype class is used when writing the prototype mode, the specific prototype class can be read through the configuration when the client is programming.

You can use deep copy to save the state of an object and use prototype mode to copy. Just skip to when you need to recover to a certain point. For example, our idea species has historical versions, and git also has such operations. perfectly worked!

3.2 Disadvantages

Each class needs to be equipped with a copy method, and the copy method is located in a class. When the existing class is modified, the source code needs to be modified, which violates the open-closed principle.

When implementing deep copying, it is necessary to write more complex code, and when there are multiple nested references between objects, in order to implement deep copying, the classes corresponding to each layer of objects must support deep copying, which is relatively troublesome to implement.

4 Shallow copy and deep copy

Shallow copy Deep copy is the concept of copying for reference types. When using a class instance to call the clone method to generate a new object, the value type (eight basic types, byte, short, int, long, char, double, float, boolean) is copied directly. When the attribute value of the new object changes, the old The object doesn't change with it.

For the reference object type, if it is a shallow copy, the new class instance and the original class instance are the same for the reference object type, that is, if the value of the reference type of the new object is changed, the value of the reference type of the old object will also change. A deep copy is a completely separate copy of the referenced object, and no longer points to the original referenced objects. Changes to the referenced object by the new instance will not affect the old class instance.

The String type is not a basic type, it is a reference type, but the String type is an immutable type. The value it points to is constant. Changing its value by the cloned object actually changes the pointer of the cloned object's String type member, which will not affect the The value of the cloned object and its pointer.

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
}

For example, there are now two classes, Teacher and Student, and the Student class is referenced in the Teacher class.

@Data
public class Teacher{
    private int number;

    private String name;

    private Student student;
}
@Data
public class Student{

    private String name;
}
public class client{


    Student stu = new Student("zhangsan");
    Teacher t = new Teacher(1, "lisi", stu);

}

The following figure shows the difference between value types and reference types in shallow and deep copies.

5 Java's clone method

Java's Object class has a method clone, which is protected and needs to be overridden to override the clone method. All classes in Java inherit the Object class by default, so all classes can implement the clone method.

 protected native Object clone() throws CloneNotSupportedException;

Java's clone() method makes a copy of the object and returns it to the caller. In general, the clone() method satisfies:
1. For any object x, there is x.clone() !=x//The cloned object is not the same object as the original object
2. For any object x, there is x .clone().getClass()= =x.getClass(), that is, the cloned object is of the same type as the original object
3. If the equals() method of the object x is properly defined, then x.clone().equals(x) should established.

Implementing the clone method is only one of them. You also need to inherit the Cloneable interface, otherwise a CloneNotSupportedException will be thrown.

package java.lang;

/**
 * A class implements the <code>Cloneable</code> interface to
 * indicate to the {@link java.lang.Object#clone()} method that it
 * is legal for that method to make a
 * field-for-field copy of instances of that class.
 * <p>
 * Invoking Object's clone method on an instance that does not implement the
 * <code>Cloneable</code> interface results in the exception
 * <code>CloneNotSupportedException</code> being thrown.
 * <p>
 * By convention, classes that implement this interface should override
 * <tt>Object.clone</tt> (which is protected) with a public method.
 * See {@link java.lang.Object#clone()} for details on overriding this
 * method.
 * <p>
 * Note that this interface does <i>not</i> contain the <tt>clone</tt> method.
 * Therefore, it is not possible to clone an object merely by virtue of the
 * fact that it implements this interface.  Even if the clone method is invoked
 * reflectively, there is no guarantee that it will succeed.
 *
 * @author  unascribed
 * @see     java.lang.CloneNotSupportedException
 * @see     java.lang.Object#clone()
 * @since   JDK1.0
 */
public interface Cloneable {
}

The basic methods of object cloning in Java are as follows:
1. In order to obtain a copy of the object, we can use the clone() method of the Object class.
2. Override the clone() method of the base class in the derived class and declare it as public.
3. In the clone() method of the derived class, call super.clone().
4. Implement the Cloneable interface in the derived class.

6 Code Examples

Monkey King, there are 72 changes, when Sun Wukong changes from the prototype to another form, it is equivalent to a copy of himself, which refers to the type of a golden hoop.

6.1 Sun Wukong prototype SunWuKong

@Data
public class SunWuKong implements Cloneable{

    /**
     * 表示孙悟空几号
     */
    private int number;

    /**
     * 孙悟空技能描述
     */
    private String desc;

    /**
     * 兵器尺寸
     */
    private Staff staff;
    /**
     * 浅复制
     * @return 孙悟空
     * @throws CloneNotSupportedException 不支持复制异常
     */
    @Override
    public Object clone() throws CloneNotSupportedException {
        SunWuKong sunWuKong = (SunWuKong)super.clone();
        return sunWuKong;
    }
}

6.2 Golden Hoop Staff

@Data
@AllArgsConstructor
public class Staff implements Cloneable{

    /**
     * 金箍棒的尺寸
     */
    private int size;
}

 6.3 Main function MainClass

/**
 * @program: design-pattern-learning
 * @author: zgr
 * @create: 2021-09-15 09:48
 **/
public class MainClass {

    public static void main(String[] args) {
        try {
            Staff staff = new Staff(200);
            SunWuKong sunWuKong1 = new SunWuKong();
            sunWuKong1.setNumber(1);
            sunWuKong1.setDesc("我是1号");
            sunWuKong1.setStaff(staff);
            System.out.println(sunWuKong1);
            System.out.println("*****************浅拷贝*******************");
            SunWuKong sunWuKong2 = (SunWuKong) sunWuKong1.clone();
            System.out.println(sunWuKong2);

            System.out.println("*****************改变属性*******************");
            staff.setSize(300);
            sunWuKong2.setNumber(2);
            sunWuKong2.setDesc("我原来是1号,我现在变了");
            System.out.println(sunWuKong1);
            System.out.println(sunWuKong2);
        }catch (CloneNotSupportedException e){
            System.out.println("对象不能被克隆");
        }
    }
}

6.4 Running Results

6.5 Analysis of results

 Pay attention to the reference object Staff. When we change the staff attribute, the prototype and copy type of Sun Wukong have changed, which means that it is a shallow copy.

7 Deep copy using Java serialization

7.1 Prototype code

The main code remains the same, only need to add the inherited Serializable interface to the Staff class and SunWuKong class, and add the deepClone method to SunWuKong.

@Data
public class SunWuKong implements Cloneable, Serializable{

    /**
     * 表示孙悟空几号
     */
    private int number;

    /**
     * 孙悟空技能描述
     */
    private String desc;

    /**
     * 兵器尺寸
     */
    private Staff staff;
    /**
     * 浅复制
     * @return 孙悟空
     * @throws CloneNotSupportedException 不支持复制异常
     */
    @Override
    public Object clone() throws CloneNotSupportedException {
        SunWuKong sunWuKong = (SunWuKong)super.clone();
        return sunWuKong;
    }

    public Object deepClone()throws IOException, ClassNotFoundException{
        //将对象写到流里
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);
        //从流里读回来
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return ois.readObject();
    }
}
@Data
@AllArgsConstructor
public class Staff implements Cloneable, Serializable {

    /**
     * 金箍棒的尺寸
     */
    private int size;
    
}

7.2 Main function code

public class MainClass {

    public static void main(String[] args) {
        try {
            Staff staff = new Staff(200);
            SunWuKong sunWuKong1 = new SunWuKong();
            sunWuKong1.setNumber(1);
            sunWuKong1.setDesc("我是1号");
            sunWuKong1.setStaff(staff);
            System.out.println(sunWuKong1);
            System.out.println("*****************浅拷贝*******************");
            SunWuKong sunWuKong2 = (SunWuKong) sunWuKong1.clone();

            staff.setSize(300);
            sunWuKong2.setNumber(2);
            sunWuKong2.setDesc("我原来是1号,我现在变2号");
            System.out.println(sunWuKong1);
            System.out.println(sunWuKong2);


            System.out.println("*****************深拷贝*******************");
            SunWuKong sunWuKong3 = (SunWuKong) sunWuKong1.deepClone();
            staff.setSize(400);
            sunWuKong3.setNumber(3);
            sunWuKong3.setDesc("我是深拷贝3号");
            System.out.println(sunWuKong1);
            System.out.println(sunWuKong2);
            System.out.println(sunWuKong3);
        }catch (CloneNotSupportedException e){
            System.out.println("对象不能被克隆");
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

7.3 Running Results

 

7.4 Analysis of results 

After changing the size attribute of staff, the attributes of No. 1 and No. 2 are changed, and No. 3 is still the 300 attribute before cloning, which means that the reference to the object has been copied again. 

8 Quotes

1. "Dahua Design Patterns"

2. JAVA prototype pattern

3. Prototype pattern in java

4. Explain the clone method in Java in detail

9 Source code

https://github.com/airhonor/design-pattern-learning/tree/main/src/com/hz/design/pattern/prototype

Guess you like

Origin blog.csdn.net/honor_zhang/article/details/120412303