Android开发Android技术知识Android开发

原型模式

2018-04-02  本文已影响95人  YoungerDev

Android进阶之设计模式

原型模式

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

使用场景:

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

需要注意的是,通过实现 Cloneable 接口的原型模式在调用clone 函数构造实例时并不一定比通过 new 操作速度快,只有当通过 new 构造对象较为耗时或者成本较高时,通过 clone 方法才能够获得效率上的提升.

优点: 原型模式在内存中二进制流的拷贝,要比 new 一个对象性能好很多,特别是要在一个循环体内产生大量的对象时,原型模式可以更好地体现其优点.

缺点: 这既是它的优点也是缺点.直接在内存中拷贝,构造函数是不会执行的,在实际开发中应该注意这个潜在问题,优点就是减少了约束,缺点也是减少了约束.

下面用的例子来说明问题:



public class WordDocument implements Cloneable {

    //文本
    private String mText;

    //图片名称表
    private ArrayList<String> mImages = new ArrayList<String>();

    public WordDocument() {
        System.out.println("=======WordDocument构造函数=======");
    }

    @Override
    public WordDocument clone() {

        try {
            WordDocument document = (WordDocument) super.clone();
            document.mText = this.mText;
            document.mImages = this.mImages;
            return document;

        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        return null;
    }

    public String getmText() {
        return mText;
    }

    public void setText(String mText) {
        this.mText = mText;
    }

    public ArrayList<String> getmImages() {
        return mImages;
    }

    public void addImages(String mImages) {
        this.mImages.add(mImages) ;
    }

    public void showDocument(){

        System.out.println("--------- Word Content Start -----------");
        System.out.println("Text :"+mText);
        System.out.println("Images list :");
        for (String imName : mImages){
            System.out.println("image name :"+imName);
        }
        System.out.println("============= word content end =======");
    }
}

Client 端使用

 public static void main(String[] args) {
 

        //1. 构建文档对象
        WordDocument originDoc = new WordDocument();

        //2. 编辑文档,添加图片等.
        originDoc.setText("这是一篇文档");

        originDoc.addImages("图片1");
        originDoc.addImages("图片2");
        originDoc.addImages("图片3");
        originDoc.showDocument();

        WordDocument doc2 = originDoc.clone();
        doc2.showDocument();
        doc2.setText("这是修改过的Doc2文本");
        doc2.showDocument();

        originDoc.showDocument();


    }
    

打印出来的结果是:

=======WordDocument构造函数=======
--------- Word Content Start -----------
Text :这是一篇文档
Images list :
image name :图片1
image name :图片2
image name :图片3
============= word content end =======
--------- Word Content Start -----------
Text :这是一篇文档
Images list :
image name :图片1
image name :图片2
image name :图片3
============= word content end =======
--------- Word Content Start -----------
Text :这是修改过的Doc2文本
Images list :
image name :图片1
image name :图片2
image name :图片3
============= word content end =======
--------- Word Content Start -----------
Text :这是一篇文档
Images list :
image name :图片1
image name :图片2
image name :图片3
============= word content end =======

以上是属于浅拷贝,这份拷贝实际上并不是将原始文档的所有字段都重新构造了一份,而是副本字段引用原始文档的字段,
当 A 引用 B 就是说两个对象指向同一个地址,当修改 A 时 B 也会改变,B 修改时 A 同样也会改变

比如,将 main 函数修改为 现在这样样子,会发生什么呢?


 public static void main(String[] args) {
 
   //1. 构建文档对象
        WordDocument originDoc = new WordDocument();

        //2. 编辑文档,添加图片等.
        originDoc.setText("这是一篇文档");

        originDoc.addImages("图片1");
        originDoc.addImages("图片2");
        originDoc.addImages("图片3");
        originDoc.showDocument();

        WordDocument doc2 = originDoc.clone();
        doc2.showDocument();
        doc2.setText("这是修改过的Doc2文本");
        //区别 就在增加一个图片4
        doc2.addImages("图片4");
        doc2.showDocument();

        originDoc.showDocument();

    }
    

现在 打印出的结果是:


=======WordDocument构造函数=======
--------- Word Content Start -----------
Text :这是一篇文档
Images list :
image name :图片1
image name :图片2
image name :图片3
============= word content end =======
--------- Word Content Start -----------
Text :这是一篇文档
Images list :
image name :图片1
image name :图片2
image name :图片3
============= word content end =======
--------- Word Content Start -----------
Text :这是修改过的Doc2文本
Images list :
image name :图片1
image name :图片2
image name :图片3
image name :图片4
============= word content end =======
--------- Word Content Start -----------
Text :这是一篇文档
Images list :
image name :图片1
image name :图片2
image name :图片3
image name :图片4
============= word content end =======

可以看到的是 最后面, 拷贝的,原始的都有了 图片4 , 这就是因为以上就浅拷贝,引用类型的 doc2 的 images 只是单纯地指向了 this.images 引用,并没有重新构造一个 image 对象, 然后将原始文档的图片添加到新的 images 对象中, 这就导致了 doc2 中的 images 与原始文档中的是同一个对象,因此修改其中一个文档中的图片,另一个文档也会受影响.

要想解决这个问题呢,就需要采用深拷贝,而不是单纯引用的形式.clone 方法的修改如下:

 @Override
    public WordDocument clone() {

        try {
            WordDocument document = (WordDocument) super.clone();
            document.mText = this.mText;
            //对 mImages 对象也调用 clone() 函数,进行深拷贝
            document.mImages = (ArrayList<String>) this.mImages.clone();
            return document;

        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        return null;
    }

现在打印出的结果是:


=======WordDocument构造函数=======
--------- Word Content Start -----------
Text :这是一篇文档
Images list :
image name :图片1
image name :图片2
image name :图片3
============= word content end =======
--------- Word Content Start -----------
Text :这是一篇文档
Images list :
image name :图片1
image name :图片2
image name :图片3
============= word content end =======
--------- Word Content Start -----------
Text :这是修改过的Doc2文本
Images list :
image name :图片1
image name :图片2
image name :图片3
image name :图片4
============= word content end =======
--------- Word Content Start -----------
Text :这是一篇文档
Images list :
image name :图片1
image name :图片2
image name :图片3
============= word content end =======

结论:可以看出 构造方法的确就执行了一次,拷贝后的修改,并不会修改原来的内容. 在实际开发中尽量使用深拷贝.
上一篇下一篇

猜你喜欢

热点阅读