【创建模式-蓝本模式(Prototype Pattern)】

目录

https://doc.hutool.cn/pages/Cloneable/#泛型克隆类

The prototype pattern is a creational design pattern in software development. It is used when the types of objects to create is determined by a prototypical instance, which is cloned to produce new objects. This pattern is used to avoid subclasses of an object creator in the client application, like the factory method pattern does, and to avoid the inherent cost of creating a new object in the standard way (e.g., using the 'new' keyword) when it is prohibitively expensive for a given application.

To implement the pattern, the client declares an abstract base class that specifies a pure virtual clone() method. Any class that needs a "polymorphic constructor" capability derives itself from the abstract base class, and implements the clone() operation.

The client, instead of writing code that invokes the "new" operator on a hard-coded class name, calls the clone() method on the prototype, calls a factory method with a parameter designating the particular concrete derived class desired, or invokes the clone() method through some mechanism provided by another design pattern.

The mitotic division of a cell --- resulting in two identical cells --- is an example of a prototype that plays an active role in copying itself and thus, demonstrates the Prototype pattern. When a cell splits, two cells of identical genotype result. In other words, the cell clones itself.[1]

Overview

The prototype design pattern is one of the 23 Gang of Four design patterns that describe how to solve recurring design problems to design flexible and reusable object-oriented software, that is, objects that are easier to implement, change, test, and reuse.[2]: 117

The prototype design pattern solves problems like:[3]

  • How can objects be created so that the specific type of object can be determined at runtime?
  • How can dynamically loaded classes be instantiated?
    Creating objects directly within the class that requires (uses) the objects is inflexible because it commits the class to particular objects at compile-time and makes it impossible to specify which objects to create at run-time.

The prototype design pattern describes how to solve such problems:

  • Define a Prototype object that returns a copy of itself.
  • Create new objects by copying a Prototype object.
    This enables configuration of a class with different Prototype objects, which are copied to create new objects, and even more, Prototype objects can be added and removed at run-time.
    See also the UML class and sequence diagram below.

应用场景

当需要反复构建出同一个类的大量实例,并且这些实例的初始状态都是一样的时候就可以先通过new创建出一个实例,对该实例进行调整(通常就是设置一些参数),这个实例就可以称之为原型,这也是该设计模式叫原型模式的缘由。

我们可以举一个实际的例子(引用自:《设计模式之禅道》),银行批量向客户发送节日邮件,假设一封邮件包括:①称呼、②节日祝福语;那么操作流程应该是这样的:

  1. 创建一个模板,对该模板设置节日祝福语,因为所有的人节日祝福语是相同的;
  2. 为每一个客户依次克隆一个第一步的模板(如果不新建模板的话,多线程并发时是不安全的,通过new创建效率不如克隆),得到一个实例,将该实例传递给一个发送邮件线程;
  3. 发送线程对传递得到的邮件实例,进行称呼设置,然后进行邮件发送;

代码演示

JDK Prototype pattern

java 复制代码
/**
 * 原型模式 VS 构造函数 性能测试
 *
 * @author hipilee
 */
public class JdkCloneable implements Cloneable {
    public int properties1 = 0;

    public JdkCloneable() {
    }

    public JdkCloneable(int properties1) {
        HashMap<Integer, Integer> hashMap = new HashMap(100);
        for (int i = 0; i < 10; i++) {
            hashMap.put(properties1, properties1);
        }

    }

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

    public static void main(String[] args) throws CloneNotSupportedException {
        long start, end;
        start = System.currentTimeMillis();
        JdkCloneable jdkCloneable = new JdkCloneable(1000);
        for (int i = 0; i < 10000000; i++) {
            jdkCloneable.clone();
        }
        end = System.currentTimeMillis();
        System.out.println("原型模式:耗时" + (end - start) / 1000.0 + "毫秒。");

        start = System.currentTimeMillis();
        for (int j = 0; j < 10000000; j++) {
            new JdkCloneable();
        }
        end = System.currentTimeMillis();
        System.out.println("默认构造:耗时" + (end - start) / 1000.0 + "毫秒。");

        start = System.currentTimeMillis();
        for (int j = 0; j < 10000000; j++) {
            new JdkCloneable(j);
        }
        end = System.currentTimeMillis();
        System.out.println("复杂构造:耗时" + (end - start) / 1000.0 + "毫秒。");
    }
}

输出:

原型模式:耗时0.357毫秒。

简单构造:耗时0.008毫秒。

复杂构造:耗时6.016毫秒。

由此我们可以得出,原型模式相对于new关键字构造对象的优势在于:如果new构造调用的构造函数比较耗时时,原型模式才有明显优势;因为原型模式是从内存中拷贝二进制数据不会调用构造函数。

更优实践

通过使用Hutool工具类,来解决jdk自身的Cloneable接口不方便的地方:

  • java.lang.Cloneable接口是个空接口起标记作用,在对类进行克隆时,如果该类只是实现了java.lang.Cloneable接口并没有重写Object的clone()函数,那么会抛出CloneNotSupportedException异常;
  • Object的clone()函数返回的对象是Object,克隆后需要程序员自己强转下类型;
  • jdk的克隆函数是浅拷贝

泛型克隆接口

实现Hutool的cn.hutool.core.clone.Cloneable接口。此接口定义了一个返回泛型的成员方法,这样,实现此接口后会提示必须实现一个public的clone方法,调用父类clone方法即可:

java 复制代码
import cn.hutool.core.clone.CloneRuntimeException;
import cn.hutool.core.clone.Cloneable;
 
/**
 * 猫猫类,使用实现Cloneable方式
 * @author Looly
 *
 */
class Cat implements Cloneable<Cat>{
    private String name = "miaomiao";
    private int age = 2;
 
    @Override
    public Cat clone() {
        try {
            return (Cat) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new CloneRuntimeException(e);
        }
    }
}
相关推荐
明月清风徐徐4 分钟前
Vue实训---6-完成用户退出操作
javascript·vue.js·elementui
五味香18 分钟前
Java学习,字符串搜索
java·c语言·开发语言·python·学习·golang·kotlin
weixin_11223322 分钟前
基于Java图书借阅系统的设计与实现
java·开发语言
占疏32 分钟前
在虚拟机的python中安装配置Jupyter Notebook
开发语言·python·jupyter
大G哥36 分钟前
Flutter如何调用java接口如何导入java包
java·开发语言·flutter
ac-er88881 小时前
PHP如何给图片添加边框效果
开发语言·php
Book思议-1 小时前
模拟KTV点歌系统的程序
java·开发语言
手心里的白日梦1 小时前
多线程与线程互斥
linux·服务器·开发语言·网络
杨荧1 小时前
【开源免费】基于Vue和SpringBoot的图书进销存管理系统(附论文)
前端·javascript·vue.js·spring boot·spring cloud·java-ee·开源
○陈2 小时前
js面试题|[2024-12-10]
java·前端·javascript