Java Design Pattern Series (5) Prototype Pattern

Java Design Pattern Series (5) Prototype Pattern

The prototype pattern belongs to the creation pattern of objects. Specify the type of all objects created by giving a prototype object, and then create more objects of the same type by duplicating this prototype object. That's what selection mode is for.

The structure of the prototype pattern

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. In this way, when creating a new object through a prototype instance, you no longer need to care about the type of the instance itself. As long as the method of cloning itself is implemented, you can use this method to obtain a new object without having to create it through new. .

The prototype pattern has two manifestations:

  1. simple form

  2. registration form

2. Simple Form Prototype Pattern

Figure 4-1 Simple form of the prototype pattern

This form involves three roles:

(1) Client role: The client class makes a request to create an object.

(2) Abstract prototype (Prototype) role: This is an abstract role, usually implemented by a Java interface or Java abstract class. This role gives all the interfaces required by the concrete prototype class.

(3) Concrete Prototype role: the object to be copied. This role needs to implement the interfaces required by the abstract prototype role.

source code

(1) Abstract prototype role

public interface Prototype{

    /**
     * 克隆自身的方法
     * @return 一个从自身克隆出来的对象
     */
    public Object clone();
}

(2) Specific archetype roles

public class ConcretePrototype1 implements Prototype {
    public Prototype clone(){
        // 最简单的克隆,新建一个自身对象,由于没有属性就不再复制值了
        Prototype prototype = new ConcretePrototype1();
        return prototype;
    }
}

public class ConcretePrototype2 implements Prototype {
    public Prototype clone(){
        // 最简单的克隆,新建一个自身对象,由于没有属性就不再复制值了
        Prototype prototype = new ConcretePrototype2();
        return prototype;
    }
}

(3) Client role

public class Client {

    /** 持有需要使用的原型接口对象 */
    private Prototype prototype;

    /** 构造方法,传入需要使用的原型接口对象 */
    public Client(Prototype prototype){
        this.prototype = prototype;
    }

    public void operation(Prototype example){
        // 需要创建原型接口的对象
        Prototype copyPrototype = (Prototype) prototype.clone();

    }
}

3. Prototype mode of registration form

Figure 4-2 Prototype pattern of registration form

As the second form of the prototype pattern, it has an additional role of prototype manager (PrototypeManager), the role of which is to create objects of specific prototype classes and record each created object.

source code

(1) Abstract prototype role

public interface Prototype {

    public Prototype clone();
    public String getName();
    public void setName(String name);
}

(2) Specific archetype roles

public class ConcretePrototype1 implements Prototype {

    private String name;

    public Prototype clone() {
        ConcretePrototype1 prototype = new ConcretePrototype1();
        prototype.setName(this.name);
        return prototype;
    }

    public String toString() {
        return "Now in Prototype1 , name = " + this.name;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void setName(String name) {
        this.name = name;
    }
}

public class ConcretePrototype2 implements Prototype {

    private String name;

    public Prototype clone(){
        ConcretePrototype2 prototype = new ConcretePrototype2();
        prototype.setName(this.name);
        return prototype;
    }

    public String toString(){
        return "Now in Prototype2 , name = " + this.name;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void setName(String name) {
        this.name = name;
    }
}

(3) Prototype manager role

The role of the prototype manager maintains an aggregate, as the registration of all prototype objects, this role provides the necessary methods for the outside world to add new prototype objects and obtain the registered prototype objects.

public class PrototypeManager {

    /** 用来记录原型的编号和原型实例的对应关系 */
    private static ConcurrentHashMap<String, Prototype> map = new ConcurrentHashMap<>();

    /** 私有化构造方法,避免外部创建实例 */
    private PrototypeManager() {
    }

    /**
     * 向原型管理器里面添加或是修改某个原型注册
     * @param prototypeId 原型编号
     * @param prototype   原型实例
     */
    public static void setPrototype(String prototypeId, Prototype prototype) {
        map.put(prototypeId, prototype);
    }

    /**
     * 从原型管理器里面删除某个原型注册
     * @param prototypeId 原型编号
     */
    public static void removePrototype(String prototypeId) {
        map.remove(prototypeId);
    }

    /**
     * 获取某个原型编号对应的原型实例
     * @param prototypeId    原型编号
     * @return    原型编号对应的原型实例
     * @throws Exception    如果原型编号对应的实例不存在,则抛出异常
     */
    public static Prototype getPrototype(String prototypeId) throws Exception {
        Prototype prototype = map.get(prototypeId);
        if(prototype == null) {
            throw new Exception("您希望获取的原型还没有注册或已被销毁");
        }
        return prototype;
    }
}

Comparison of the two forms

Prototype patterns in simple form and registered form each have their own strengths and weaknesses.

The first form can be used if the number of prototype objects to be created is small and fixed. In this case, the reference to the prototype object can be kept by the client itself.

The second form can be used if the number of prototype objects to be created is not fixed. In this case, the client does not hold a reference to the prototype object, this task is left to the administrator object. Before copying a prototype object, the client can check to see if the administrator object already has a prototype object that satisfies the requirements. If there is, the object reference can be obtained directly from the administrator class; if not, the client needs to copy the prototype object by itself.

Fourth, the clone method in Java

All classes in Java are inherited from the java.lang.Object class, and the Object class provides the protected Object clone() method to copy objects. Of course, subclasses can also replace this method to provide a copy that meets their own needs. method. A fundamental problem with object duplication is that objects often have references to other objects. When using the clone() method of the Object class to copy an object, references to other objects by this object will also be copied at the same time

The Cloneable interface provided by the Java language has only one function, which is to inform the Java virtual machine at runtime that it is safe to use the clone() method on this class. A clone of an object can be obtained by calling the clone() method. Since the Object class itself does not implement the Cloneable interface, calling the clone() method will throw a CloneNotSupportedException if the class in question does not implement the Cloneable interface.

4.1 Conditions that clones satisfy

The clone() method makes a copy of the object and returns it to the caller. The meaning of the so-called "copy" and how the clone() method is implemented. In general, the clone() method satisfies the following description:

(1) For any object x, there are: x.clone() != x. In other words, the cloned object is not the same object as the original.

(2) For any object x, there are: x.clone().getClass() == x.getClass(), in other words, the cloned object has 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 hold.

In the JAVA language API, all classes that provide the clone() method meet the above conditions. The designers of the JAVA language should also observe three conditions when designing their clone() method. In general, the first two of the three conditions above are required, while the third is optional.

4.2 Shallow and deep clones

Whether you implement the clone method yourself or use the clone method provided by Java, there is a problem of shallow cloning and deep cloning.

Shallow clone

It is only responsible for cloning the data passed by value (such as basic data types, String types), without copying the object it refers to, in other words, all references to other objects still point to the original object.

deep clone

In addition to shallowly cloning the value to be cloned, it is also responsible for cloning the data of the reference type. Variables that refer to other objects will point to the new object that was copied, not the original referenced object. In other words, deep cloning copies all the objects referenced by the object to be copied, and this copying of the referenced object is called indirect copying.

It is not easy to determine how many layers deep clone should go to. When deciding to deep clone an object, you must decide whether to shallow clone or continue to deep clone the indirectly copied object. So when taking a deep clone, you need to decide how deep is deep. In addition, in the process of deep cloning, the problem of circular reference is likely to occur, which must be handled with care.

4.3 Deep cloning using serialization

The process of writing an object to a stream is a serialization process; the process of reading an object from a stream is called a deserialization process. It should be noted that what is written to the stream is a copy of the object, and the original object still exists in the JVM.

To deeply clone an object in the Java language, you can often make the object implement the Serializable interface, then write the object (actually just a copy of the object) to a stream (serialization), and then read it back from the stream (deserialization) , the object can be rebuilt.

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

The premise of this is that the object and all the referenced objects inside the object are serializable. Otherwise, it is necessary to carefully examine whether those non-serializable objects can be set to transient, so as to exclude them from the copying process.

Shallow cloning is obviously easier to implement than deep cloning, because all classes in the Java language inherit a clone() method, and this clone() method does the formal shallow cloning.

Some objects, such as Thread objects or Socket objects, cannot be simply copied or shared. Regardless of whether shallow clone or deep clone is used, as long as such indirect objects are involved, the indirect objects must be set to transient without being copied;

Advantages of Prototype Patterns

The prototype pattern allows to dynamically change the concrete implementation type at runtime. In the prototype mode, the client can register the implementation type that conforms to the prototype interface during operation, and the specific implementation type can also be changed dynamically. It seems that the interface has not changed, but in fact it is already running another class instance. Because cloning a prototype is like instantiating a class.

Disadvantages of Prototype Patterns

The main disadvantage of the prototype pattern is that every class must be equipped with a clone method. Equipping a clone method requires an overall consideration of the functionality of the class, which is not difficult for a brand-new class, but not necessarily easy for an existing class, especially when a class references indirect objects that do not support serialization, or references When there is a loop structure.


Record a little bit every day. Content may not be important, but habits are!

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325652617&siteId=291194637