前言
原型模式是一个创建型设计模式。
定义:用原型实例指定创建对象的种类,并通过复制这些原型创建新的对象。
使用场景:
- 类初始化需要消耗非常多的资源,这个资源包括数据、硬件资源,通过原型复制避免这些消耗;
- 通过new产生一个对象需要非常繁琐的数据准备或访问权限;
- 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式复制多个对象供调用者使用,即保护性拷贝。
注意:通过实现Cloneable接口的原型模式在调用clone函数构造实例时并不一定比通过new操作速度快,只有当通过new构造对象较为耗时时或者说成本较高时,通过clone方法才能获取效率上的提升。
 另外,实现原型模式也不一定非要实现cloneable接口,也有其他实现方式。
UML类图:
 
 - Client:客户端用户;
 - Prototype:抽象类或者接口,声明具备clone能力;
 - ConcretePrototype:具体的原型类;
实现示例
以文档拷贝为例,一个文档中包含文字和图片,编辑后的文档为了安全起见,需要进行文档拷贝,然后再在文档副本上进行修改,这里我们就可以使用原型模式来实现。
package com.crystal.essayjoker.clone
/**
 * Word文档类
 * on 2022/12/21
 */
class Word : Cloneable {
    /**
     * 文本
     */
    private var text: String? = null
    /**
     * 多张图片
     */
    private var images: ArrayList<String> = arrayListOf()
    constructor() {
        println("Word Class constructor execute!")
    }
    public override fun clone(): Word {
        val cloneWord = super.clone() as Word
        cloneWord.text = this.text
        cloneWord.images = this.images
        return cloneWord
    }
    fun setText(text: String) {
        this.text = text
    }
    fun getText(): String? {
        return text
    }
    fun addImage(image: String) {
        images.add(image)
    }
    fun getImages(): List<String> {
        return images
    }
    fun showWord() {
        println("-------showWord Start------")
        println("this word text is:$text")
        for (image in images) {
            println("this word image is:$image")
        }
        println("-------showWord End-------")
        println()
    }
}
编写测试类:
object Test {
    @JvmStatic
    fun main(args: Array<String>) {
        val originWord = Word()
        //构造数据
        originWord.setText("this is origin word")
        originWord.addImage("first image")
        originWord.addImage("second image")
        originWord.addImage("third image")
        //展示数据
        originWord.showWord()
        //拷贝文档副本
        val cloneWord = originWord.clone()
        cloneWord.showWord()
        //修改clone word text
        cloneWord.setText("this is clone word")
        cloneWord.showWord()
        //查看原来的word是否改变
        originWord.showWord()
    }
}
最终打印结果如下:
 
 从上面实例我们可以看出:
- 通过clone拷贝对象时并不会执行构造函数;
- 修改clone后的数据并不会影响原始数据?
对于第2条事实真的如此吗?我们尝试修改images对象中的数据:
 val originWord = Word()
        //构造数据
        originWord.setText("this is origin word")
        originWord.addImage("first image")
        originWord.addImage("second image")
        originWord.addImage("third image")
        //展示数据
        originWord.showWord()
        //拷贝文档副本
        val cloneWord = originWord.clone()
        cloneWord.showWord()
        //修改clone word text
        cloneWord.setText("this is clone word")
        cloneWord.addImage("fourth image")  //新增一条数据
        cloneWord.showWord()
        //查看原来的word是否改变
        originWord.showWord()
打印结果如下:
 
 可以看到修改clone中的image数据,原始数据发生了改变!这就引出了两个概念:浅拷贝和深拷贝
浅拷贝和深拷贝
浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝。
深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。
对于上述原型模式的实现只是一个浅拷贝,也称为影子拷贝,实际上只是将副本数据字段引用指向了原始文档的字段,并没有重新构造一个image对象,只是将数据添加到了原来的image对象中;
那想解决上述问题,我们就可以采用深拷贝的方式,修改Word类中clone方法如下:
    public override fun clone(): Word {
        val cloneWord = super.clone() as Word
        cloneWord.text = this.text
        cloneWord.images = this.images.clone() as ArrayList<String> //对image对象进行深拷贝
        return cloneWord
    }
Android源码中的原型模式
对应Intent我们在熟悉不过了,我们直接来看看它的clone方法实现:
	### Intent.clone()
   public Object clone() {
        return new Intent(this); //可以看到是直接new了个intent对象
    }
	### Intent(this)
	  public Intent(Intent o) {
        this(o, COPY_MODE_ALL);
    }
	
	### this(o, COPY_MODE_ALL)
    private Intent(Intent o, @CopyMode int copyMode) {
        this.mAction = o.mAction;
        this.mData = o.mData;
        this.mType = o.mType;
        this.mIdentifier = o.mIdentifier;
        this.mPackage = o.mPackage;
        this.mComponent = o.mComponent;
        if (o.mCategories != null) {
            this.mCategories = new ArraySet<>(o.mCategories);
        }
		...
		 if (o.mExtras != null) {
                    this.mExtras = new Bundle(o.mExtras);
                }
          ...
        }
    }
可以看出Intent.clone的实现是通过原型模式进行深拷贝,而且并不是通过cloneable 接口来实现的,是通过new出一个新的对象来实现的,具体采用哪种方式需要根据构造对象的成本来决定。
总结
原型模式本质上就是对象拷贝,需要注意的就是深浅拷贝的问题。可以解决复杂对象构建的资源消耗,提升创建对象的效率,另外一个重要的用途就是保护性拷贝,可以通过返回一个对象拷贝的形式实现只读的限制;
优点:
 原型模式是对内存中二进制流的拷贝,比直接new性能好很多,特别是要在一个循环体内产生大量的对象时。
缺点:
 直接在内存中拷贝,构造函数是不会执行的,这点需要特别注意!
结语
如果以上文章对您有一点点帮助,希望您不要吝啬的点个赞加个关注,您每一次小小的举动都是我坚持写作的不懈动力!ღ( ´・ᴗ・` )



















