Android设计模式详解之原型模式

前言

原型模式是一个创建型设计模式

定义:用原型实例指定创建对象的种类,并通过复制这些原型创建新的对象。

使用场景:

  1. 类初始化需要消耗非常多的资源,这个资源包括数据、硬件资源,通过原型复制避免这些消耗;
  2. 通过new产生一个对象需要非常繁琐的数据准备或访问权限;
  3. 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式复制多个对象供调用者使用,即保护性拷贝。

注意:通过实现Cloneable接口的原型模式在调用clone函数构造实例时并不一定比通过new操作速度快,只有当通过new构造对象较为耗时时或者说成本较高时,通过clone方法才能获取效率上的提升。
另外,实现原型模式也不一定非要实现cloneable接口,也有其他实现方式。

UML类图:
原型模式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()
    }
}

最终打印结果如下:
测试结果
从上面实例我们可以看出:

  1. 通过clone拷贝对象时并不会执行构造函数;
  2. 修改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()

打印结果如下:
修改image数据
可以看到修改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性能好很多,特别是要在一个循环体内产生大量的对象时

缺点:
直接在内存中拷贝,构造函数是不会执行的,这点需要特别注意!

结语

如果以上文章对您有一点点帮助,希望您不要吝啬的点个赞加个关注,您每一次小小的举动都是我坚持写作的不懈动力!ღ( ´・ᴗ・` )

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值