[Java usage] Java design pattern (three) prototype pattern (Prototype Pattern)

table of Contents

1. Introduction to Prototype Mode

1.1 Introduction to prototype mode

1.2, the role of prototype mode

1.3 Advantages and disadvantages of prototype mode

1.4. Prototype mode usage scenarios

1.5, shallow copy and deep copy

Second, the prototype mode implementation

2.1 Code client

2.2 Shallow copy code

2.3 Deep copy code

Three, the original code analysis


1. Introduction to Prototype Mode

1.1 Introduction to prototype mode

The prototype mode uses the prototype instance to specify the type of object to be created, and creates a new object by copying the prototype object.

Prototype mode provides an interface (clone) for creating new objects through existing objects. The implementation of clone() is related to the specific language and is implemented in C++ through the copy constructor.

The prototype mode is actually to create another customizable object from one object without knowing any details of creation. In the case that the initialized information does not change, cloning is the best method, which not only hides the details of object creation, but also greatly improves performance. Because if you don't use clone, you need to execute the constructor once for each new. If the execution time of the constructor is very long, it is too inefficient to perform the initialization operation multiple times.

The prototype mode must use deep copy when implementing the clone interface.

Prototype mode focuses on creating new class objects from self-assignment, hiding the details of creation.

This mode that does not use the new keyword to generate an object, but achieves it through object copy is called the prototype mode. The general class diagram is shown above.

1.2, the role of prototype mode

Abstract prototype (Prototype) role: specifies the interface that the concrete prototype object must implement (if deep copy is to be provided, there must be provisions for implementing clone).

Concrete Prototype: Derived from the abstract prototype, it is the object used by the client program, that is, the object being copied, which needs to implement the interface required by the abstract prototype role.

Client (Client) role: the client program that uses the prototype object.

1.3 Advantages and disadvantages of prototype mode

advantage:

A. The prototype model hides specific product categories from customers

B. Add and delete products at runtime: The prototype mode allows a new specific product category to be incorporated into the system only through the customer's registration of the prototype instance.

C. Change values ​​to specify new objects: A highly dynamic system allows new behaviors to be defined through object composition. Such as by assigning a value to an object variable and not defining a new class. By instantiating an existing class and registering the instance as a prototype of a client object, you can effectively define a new class of object. Clients can delegate responsibilities to prototypes to show new behaviors.

D. Change the structure to specify new objects: Many applications create objects from components and subcomponents.
E. Reduce the construction of subclasses. Prototype mode clones a prototype instead of requesting a factory method to generate a new object.

F. Use classes to dynamically configure applications. Some runtime environments allow classes to be dynamically loaded into applications.

G. Using the prototype mode to create an object is much better in terms of performance than directly new an object, because the clone method of the Object class is a local method that directly manipulates the binary stream in memory, especially when copying large objects, the performance difference is very obvious .

H. Another advantage of using the prototype mode is to simplify the creation of objects, making it easy to create objects.

Disadvantages:

The main drawback of the prototype mode is that every subclass of the abstract prototype Prototype must implement the clone operation, and it may be difficult to implement the clone function. When the considered class already exists, it is difficult to add a clone operation. When the internal includes some objects that do not support copying or have circular references, it may also be difficult to implement cloning.

1.4. Prototype mode usage scenarios

The main idea of ​​the prototype mode is to clone a new object based on an existing object. Generally, a clone method is provided inside the object, and a copy of the object is returned through the clone method.

Prototype mode usage scenarios:

A. Class initialization needs to digest a lot of resources, this resource includes data, hardware resources, etc.;

B. Generating an object through new requires very tedious data preparation or access permissions, so you can use the prototype mode;

C. When an object needs to be provided to other objects for access, and each caller may need to modify its value, consider using the prototype mode to copy multiple objects for use by the caller;

In actual projects, the prototype mode rarely appears alone, and generally appears together with the factory method mode. An object is created by the clone method, and then the factory method is provided to the caller.

1.5, shallow copy and deep copy

A, shallow copy

All variables of the copied object contain the same value as the original object, and references to other objects still point to the original object. That is, shallow copy is only responsible for the current object instance, and does not copy the referenced object.

B, deep copy

All variables of the copied object contain the same value as the original object, except for variables that reference other objects. Variables that refer to other objects will point to a new object being copied instead of the original referenced object. That is, deep copy copies all the objects referenced by the object to be copied.

How deep a deep copy should go is an uncertain question. When deciding to copy an object in a deep copy mode, you must decide whether to use a shallow copy, a deep copy, or continue to use a deep copy for the indirect copied object. Therefore, when taking a deep copy, you need to decide how deep it is. In addition, in the process of deep copy, the problem of circular references is likely to occur.
    

Second, the prototype mode implementation

The code implementation examples of all design patterns can be viewed on Code Cloud, and those who are interested can check it. Code Cloud address: https://gitee.com/no8g/java-design-patterns

2.1 Code client

public class Client {

    /**
     * 发送账单的数量,这个值是从数据库中获得
     */
    public static final int MAX_COUNT = 10000;

    public static void main(String[] args) {
        // 模拟发送邮件
        int i = 0;

        // 把模板定义出来,这个是从数据库中获得
        Mail mail = new Mail(new AdvTemplate());
        mail.setTail("XX银行版权所有");
        long startTime = System.currentTimeMillis();
        while (i < MAX_COUNT) {
            // 以下是每封邮件不同的地方
            Mail cloneMail = mail.clone();
            cloneMail.setAppellation(getRandString(5) + "先生(女士)");
            cloneMail.setReceiver(getRandString(8) + "@" + getRandString(5) + ".com");

            // 然后发送邮件
            sendMail(cloneMail);
            i++;
        }

        System.out.println("总共耗时: " + (System.currentTimeMillis() - startTime) + "毫秒");
    }

    public static void sendMail(Mail mail) {
        System.out.println("标题:" + mail.getSubject() + "\t收件人:" + mail.getReceiver() + "\t .....发送成功!");
    }

    /**
     * 获得指定长度的随机字符串
     *
     * @return 随机字符串
     */
    public static String getRandString(int maxLength) {

        String source ="abcdefghijklmnopqrskuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        StringBuilder sb = new StringBuilder();
        Random random = new Random();
        for (int i = 0; i < maxLength; i++) {
            sb.append(source.charAt(random.nextInt(source.length())));
        }

        return sb.toString();
    }
}
package com.iot.practice.designpattern.prototype;

/**
 * <p>AdvTemplate 此类用于:</p>
 * <p>@author:hujm</p>
 * <p>@date:2021年02月06日 10:01</p>
 * <p>@remark:在类图中 AdvTemplate 是广告信的模板,一般都是从数据库取出,生成一个 BO 或者是 DTO,我们这里
 * 使用一个静态的值来做代表;Mail 类是一封邮件类,发送机发送的就是这个类,我们先来看看我们的程序:</p>
 */
public class AdvTemplate {

    /**
     * 广告信名称
     */
    private String advSubject = "XX银行国庆信用卡抽奖活动";

    /**
     * 广告信内容
     */
    private String advContent = "国庆抽奖活动通知:只要刷卡就送你1百万!....";

    /**
     * 取得广告信的名称
     *
     * @return 广告信的名称
     */
    public String getAdvSubject() {
        return this.advSubject;
    }

    /**
     * 取得广告信的内容
     *
     * @return 广告信的内容
     */
    public String getAdvContent() {
        return this.advContent;
    }
}
package com.iot.practice.designpattern.prototype;

import lombok.Data;

/**
 * <p>Mail 此类用于:</p>
 * <p>@author:hujm</p>
 * <p>@date:2021年02月06日 10:05</p>
 * <p>@remark:Mail 就是一个业务对象,我们再来看业务场景类是怎么调用的:</p>
 */
@Data
public class Mail implements Cloneable {

    /**
     * 收件人
     */
    private String receiver;

    /**
     * 邮件名称
     */
    private String subject;

    /**
     * 称谓
     */
    private String appellation;

    /**
     * 邮件内容
     */
    private String context;

    /**
     * 邮件的尾部,一般都是加上“XXX版权所有”等信息
     */
    private String tail;

    /**
     * 构造函数
     */
    public Mail(AdvTemplate advTemplate) {
        this.context = advTemplate.getAdvContent();
        this.subject = advTemplate.getAdvSubject();
    }

    /**
     * 增加了一个 Cloneable 接口, Mail 实现了这个接口,在 Mail 类中重写了 clone()方法,我们来看 Mail类的改变:
     *
     * 在 clone()方法上增加了一个注解@Override,没有继承一个类为什么可以重写呢?在 Java 中所有类的
     * 老祖宗是谁?对嘛,Object 类,每个类默认都是继承了这个类,所以这个用上重写是非常正确的。
     *
     * @return Mail
     */
    @Override
    public Mail clone() {

        Mail mail = null;
        try {
            mail = (Mail) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        return mail;
    }
}

2.2 Shallow copy code

package com.iot.practice.designpattern.prototype.shallowcopy;

/**
 * <p>ShallowCopyClient 此类用于:</p>
 * <p>@author:hujm</p>
 * <p>@date:2021年02月06日 14:57</p>
 * <p>@remark:
 * 在 Thing 类中增加一个私有变量 arrayLis,类型为 ArrayList,然后通过 setValue 和 getValue 分别进
 * 行设置和取值,我们来看场景类:
 * </p>
 */
public class ShallowCopyClient {

    public static void main(String[] args) {

        // 产生一个对象
        ShallowCopy shallowCopy = new ShallowCopy();
        // 设置一个值
        shallowCopy.setValue("张三");

        // 拷贝一个对象
        ShallowCopy cloneShallowCopy = shallowCopy.clone();
        cloneShallowCopy.setValue("李四");

        // 运行的结果是:[张三, 李四]
        System.out.println(shallowCopy.getValue());

        /**
         * 怎么会有李四呢?是因为 Java 做了一个偷懒的拷贝动作,Object 类提供的方法 clone() 只是拷贝本对象,
         * 其对象内部的数组、引用对象等都不拷贝,还是指向原生对象的内部元素地址,这种拷贝就叫做浅拷贝,
         * 确实是非常浅,两个对象共享了一个私有变量,你改我改大家都能改,是一个种非常不安全的方式,在实
         * 际项目中使用还是比较少的。你可能会比较奇怪,为什么在 Mail 那个类中就可以使用使用 String 类型,
         * 而不会产生由浅拷贝带来的问题呢?内部的数组和引用对象才不拷贝,其他的原始类型比如
         * int,long,String(Java 就希望你把 String 认为是基本类型,String 是没有 clone() 方法的)等都会被拷贝的。
         */
    }
}
package com.iot.practice.designpattern.prototype.shallowcopy;

import java.util.ArrayList;

/**
 * <p>ShallowCopy 此类用于:</p>
 * <p>@author:hujm</p>
 * <p>@date:2021年02月06日 14:52</p>
 * <p>@remark:</p>
 */
public class ShallowCopy implements Cloneable {

    /**
     * 定义一个私有变量
     */
    private ArrayList<String> arrayList = new ArrayList<>();

    @Override
    public ShallowCopy clone() {

        ShallowCopy shallowCopy = null;

        try {
            shallowCopy = (ShallowCopy) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        return shallowCopy;
    }

    /**
     * 取得arrayList的值
     */
    public ArrayList<String> getValue() {
        return this.arrayList;
    }

    /**
     * 设置List的值
     *
     * @param value 值
     */
    public void setValue(String value) {
        this.arrayList.add(value);
    }
}

2.3 Deep copy code

package com.iot.practice.designpattern.prototype.deepcopy;

/**
 * <p>ShallowCopyClient 此类用于:</p>
 * <p>@author:hujm</p>
 * <p>@date:2021年02月06日 14:57</p>
 * <p>@remark:
 * 在 Thing 类中增加一个私有变量 arrayLis,类型为 ArrayList,然后通过 setValue 和 getValue 分别进
 * 行设置和取值,我们来看场景类:
 * </p>
 */
public class DeepCopyClient {

    public static void main(String[] args) {

        // 产生一个对象
        DeepCopy deepCopy = new DeepCopy();
        // 设置一个值
        deepCopy.setValue("张三");

        // 拷贝一个对象
        DeepCopy cloneDeepCopy = deepCopy.clone();
        cloneDeepCopy.setValue("李四");

        // 运行的结果是:[张三]
        System.out.println(deepCopy.getValue());

        /**
         * 这个实现了完全的拷贝,两个对象之间没有任何的瓜葛了,你修改你的,我修改我的,不相互影响,
         * 这种拷贝就叫做深拷贝,深拷贝还有一种实现方式就是通过自己写二进制流来操作对象,然后实现对象的
         * 深拷贝,这个大家有时间自己实现一下。
         * 深拷贝和浅拷贝建议不要混合使用,一个类中某些引用使用深拷贝某些引用使用浅拷贝,这是一种非
         * 常差的设计,特别是是在涉及到类的继承,父类有几个引用的情况就非常的复杂,建议的方案深拷贝和浅
         * 拷贝分开实现
         */
    }
}
package com.iot.practice.designpattern.prototype.deepcopy;

import java.util.ArrayList;

/**
 * <p>DeepCopy 此类用于:</p>
 * <p>@author:hujm</p>
 * <p>@date:2021年02月06日 15:03</p>
 * <p>@remark:</p>
 */
public class DeepCopy implements Cloneable {

    /**
     * 定义一个私有变量
     */
    private ArrayList<String> arrayList = new ArrayList<>();

    @Override
    public DeepCopy clone() {
        DeepCopy deepCopy = null;

        try {
            deepCopy = (DeepCopy) super.clone();
            this.arrayList = (ArrayList<String>) this.arrayList.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        return deepCopy;
    }

    /**
     * 设置值
     */
    public void setValue(String value) {
        this.arrayList.add(value);
    }

    /**
     * 取值
     */
    public ArrayList<String> getValue() {
        return this.arrayList;
    }
}

Three, the original code analysis

 

end!

Guess you like

Origin blog.csdn.net/weixin_44299027/article/details/113717939