【创建模式-原型模式(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接口。

相关推荐
询问QQ:180809514 天前
永磁同步电机参数辨识那些事儿
原型模式
小白勇闯网安圈5 天前
wife_wife、题目名称-文件包含、FlatScience
javascript·python·网络安全·web·原型模式
还是大剑师兰特5 天前
ES6 class相关内容详解
es6·原型模式·大剑师
ZouZou老师5 天前
C++设计模式之原型模式:以家具生产为例
c++·设计模式·原型模式
San30.6 天前
从原型链到“圣杯模式”:JavaScript 继承方案的演进与终极解法
开发语言·javascript·原型模式
AAA阿giao7 天前
深入理解 JavaScript 中的面向对象编程(OOP):从构造函数到原型继承
开发语言·前端·javascript·原型·继承·原型模式·原型链
勇气要爆发10 天前
物种起源—JavaScript原型链详解
开发语言·javascript·原型模式
AAA阿giao10 天前
JavaScript 原型与原型链:从零到精通的深度解析
前端·javascript·原型·原型模式·prototype·原型链
chilavert31810 天前
技术演进中的开发沉思-225 Prototype.js 框架
开发语言·javascript·原型模式
雨中飘荡的记忆10 天前
设计模式之原型模式详解
设计模式·原型模式