【创建模式-蓝本模式(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 分钟前
【开题答辩过程】以《基于Java的周边游优选推荐网站的设计与实现》为例,不知道这个选题怎么做的,不知道这个选题怎么开题答辩的可以进来看看
java·开发语言
Two_brushes.4 分钟前
Cmake中寻库文件的路径
开发语言·c++·cmake
Larry_Yanan10 分钟前
Qt安卓开发(三)双摄像头内嵌布局
android·开发语言·c++·qt·ui
wjs202415 分钟前
Kotlin 条件控制
开发语言
我命由我1234515 分钟前
Kotlin 开发 - Kotlin Lambda 表达式返回值
android·java·开发语言·java-ee·kotlin·android studio·android-studio
雨中散步撒哈拉21 分钟前
22、做中学 | 高一下期 | Golang反射
开发语言·golang·状态模式
a努力。25 分钟前
中国电网Java面试被问:Dubbo的服务目录和路由链实现
java·开发语言·jvm·后端·面试·职场和发展·dubbo
2501_9481953425 分钟前
RN for OpenHarmony英雄联盟助手App实战:关于实现
javascript·react native·react.js
JosieBook33 分钟前
【Vue】10 Vue技术——Vue 中的数据代理详解
javascript·vue.js·ecmascript
itwangyang52033 分钟前
人工智能药物设计和生信常用 R 包一键全自动安装脚本
开发语言·人工智能·r语言