【创建模式-原型模式(Prototype Pattern)】

夜雨寄北

唐.李商隐
君问归期未有期,巴山夜雨涨秋池。

何当共剪西窗烛,却话巴山夜雨时。

目的

需要创建某个类时,以某个类的某个具体的、典型的实例作为原型实例;通过拷贝复制该原型实例来创建新的对象。大白话就是不通过new关键字来产生一个对象,而是通过对象复制来创建对象。

动机

  降低通过new关键字创建对象的开销;这里的开销主要源于构造函数内部的构建过程

应用场景

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

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

创建一个模板,对该模板设置节日祝福语,因为所有的人节日祝福语是相同的;

为每一个客户依次克隆一个第一步的模板(如果不新建模板的话,多线程并发时是不安全的,通过new创建效率不如克隆),得到一个实例,将该实例传递给一个发送邮件线程;

发送线程对传递得到的邮件实例,进行称呼设置,然后进行邮件发送;

注意事项

  • 原型模式的构造函数不会被执行,Object类的clone方法的原理是从内存中以二进制流的方式进行拷贝,重新分配一块内存块,所以构造函数不被执行;
  • 深拷贝和浅拷贝,jdk自带的克隆函数是浅拷贝,如果需要深拷贝需要程序员自己实现
  • final修饰的属性和clone冲突,在编译阶段就会报错
  • 要使用clone方法,类成员变量上不要增加final关键字

性能评测

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

泛型克隆类

实现cn.hutool.core.clone.Cloneable接口依旧有不方便之处,就是必须自己实现一个public类型的clone()方法,还要调用父类(Object)的clone方法并处理异常。于是cn.hutool.clone.CloneSupport类产生,这个类帮我们实现了上面的clone方法,因此只要继承此类,不用写任何代码即可使用clone()方法:

java 复制代码
import cn.hutool.core.clone.CloneSupport;

/**
 * 狗狗类,用于继承CloneSupport类
 * @author Looly
 *
 */
public class Dog extends CloneSupport<Dog> {
    private String name = "wangwang";
    private int age = 3;
}

深克隆

我们知道实现Cloneable接口后克隆的对象是浅克隆,要想实现深克隆,请使用:

java 复制代码
ObjectUtil.cloneByStream(obj)

前提是对象必须实现Serializable接口。

相关推荐
想吃火锅100519 天前
【前端手撕】instanceof
前端·javascript·原型模式
UXbot19 天前
帮助企业低门槛开展AI应用开发的平台推荐
前端·低代码·ui·交互·产品经理·原型模式·web app
UXbot19 天前
如何选择适合公司项目的UI设计工具?企业选型指南
前端·低代码·ui·团队开发·原型模式·设计规范·web app
UXbot19 天前
原型设计工具如何帮助新人快速进入产品行业?
前端·低代码·ui·交互·团队开发·原型模式·web app
sunny.day24 天前
js原型与原型链
开发语言·javascript·原型模式·js原型链
UXbot25 天前
AI网页开发工具能替代工具吗?5大平台对比
前端·人工智能·低代码·ui·原型模式·web app
weixin_3077791325 天前
从“大海捞针”到“主动推理”:AI如何重塑云原生故障诊断的根因链
开发语言·人工智能·算法·自动化·原型模式
swordbob25 天前
prototype 注入到 singleton 里,prototype是否还是线程安全的
安全·spring·单例模式·原型模式
isNotNullX1 个月前
企业数据中台建设,ETL工具选错了会踩哪些坑?
数据仓库·etl·原型模式
半个烧饼不加肉1 个月前
JS 底层探究-- 普通函数和构造函数
开发语言·javascript·原型模式