设计模式:原型

作者: 要拿出真本事了 | 来源:发表于2019-08-19 13:27 被阅读5次

原型模式基本介绍
原型模式的实现
源码中的原型模式记录

原型模式基本介绍

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

适用于:

  • 类初始化需要消耗很多的资源(包括数据、硬件资源等),通过复制避免这些消耗
  • 通过new产生一个对象需要非常繁杂的数据准备或访问权限
  • 一个对象需要提供给其他对象访问,且各个调用者可能都需要修改其值(保护性拷贝)

原型模式的实现

要想记住和使用原型模式,就要搞清楚使用原型模式有什么用

日常中使用WPS编辑文档时,你会发现你可以肆意的编辑,但如果你不保存,它便不会被修改,细心的话可以发现,每次打开一个文档的时候在相同的目录下会生成一个新的临时文件,这大概就是原型模式的应用,每次使用时拷贝一份备份文件用于编辑,如果保存编辑的话再做相应的操作,不保存时源文件不会受到影响

原型模式大概就是这么一个东西,编辑时基于拷贝文件而不影响源文件,实现了可撤销和对源文件的保护,Android客户端不会有声明高并发的情况,但还是不能忽略并发的问题

原型模式的实现核心为:

  • 定义一个包含拷贝方法clone的公共接口
  • 以及该接口的实现类

获取新对象时非通过new而是该接口的clone方法
但是实际的开发中,已经有了现成的实现,即Cloneable标识接口和Object类中的clone方法,可以自己定义,更多情况下还是使用Java中现成的实现

    //实现Cloneable接口和clone方法
    class CloneTest implements Cloneable {
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    }

使用Cloneable接口和clone方法有几点需要注意的地方:

  • 只有声明了Cloneable接口才能使用clone方法
  • 在定义中,clone方法实现拷贝是基于内存中的二进制流,而非构造函数
  • clone的实现并不一定比new操作速度快,只有new构造函数比较耗时,clone才能获得效率上的提升

关于浅拷贝和深拷贝的问题

浅拷贝

clone方法的实现包含两部分的数据的拷贝,分别是Java基本数据类型的拷贝和引用型数据的拷贝

    //实现Cloneable接口和clone方法
    class CloneTest implements Cloneable {
        private String mText;
        private ArrayList<String> mList = new ArrayList<>();

        public CloneTest(){
            //clone不会调用构造方法
        }

        @Override
        protected CloneTest clone() throws CloneNotSupportedException {
            try{
                CloneTest newInstance = (CloneTest) super.clone();
                newInstance.mText = this.mText;
                newInstance.mList = this.mList;
                return newInstance;
            }catch (Exception e){
                
            }
            return null;
        }
    }

Java基本数据类型的拷贝是基于数值的,即把值拷贝到新的实例中,修改新实例中的值数据并不会影响到原来对象的数据,如mText

引用型数据的拷贝并不会创建新的实例,如mList 。
在JVM中引用型数据实际上是使用指针,所以对于引用型数据的拷贝实际上就是拷贝指针,这会导致对其中一个对象的修改会影响另外一个对象,因为指向的是同一个地址(对象)

这就是浅拷贝,只对值进行拷贝,不管是值数据(值)还是引用型数据(指针)
这也是拷贝的本质,其实本没有什么深浅之分,只是这样的拷贝会产生问题,我们需要解决它,所以深拷贝就诞生了

深拷贝

刚刚有提到,JAVA中默认的对于值的拷贝会引发问题,要解决这个问题就要解决引用类型的数据的拷贝时不能只是拷贝其指针的指向,而是应该创建一个新实例并把原数据的值拷贝给它

可以通过new创建新实例并在原来的类中定义基于自己的构造函数
或者还是通过clone实现二进制流的复制

用clone的方式实现存在的问题是不会执行构造函数,对于一些有构造函数需求的类会出现问题,具体实现如下:

    ...
        @Override
        protected CloneTest clone() throws CloneNotSupportedException {
            try{
                CloneTest newInstance = (CloneTest) super.clone();
                newInstance.mText = this.mText;
                newInstance.mList = (ArrayList<String>) this.mList.clone();
                return newInstance;
            }catch (Exception e){
                
            }
            return null;
        }

可以看到,mList 再进行clone也就是clone嵌套clone,这样mList 就不再只是拷贝了其指针,而是又进行了值拷贝,可以想象,如果类结构层次很深的话,需要一直不断的clone下去,且都要实现Cloneable接口

对于浅拷贝和深拷贝的总结

浅拷贝就是clone,基于内存中二进制流的拷贝,对于引用类型的数据,就拷贝其指针指向的地址,造成的问题就是引用类型的数据对象在两个对象间只有同一个

深拷贝就是把对象中的所有引用类型的数据再clone,使其生成新的对象

源码中的原型模式记录

暂缓!

相关文章

网友评论

    本文标题:设计模式:原型

    本文链接:https://www.haomeiwen.com/subject/phkjsctx.html