Android设计模式之原型模式

原型模式

1.定义:

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

2.使用场景:

  • 类初始化需要消耗非常多的资源,这个资源包括数据、硬件资源等,通过原型copy避免了这些消耗;
  • 通过new产生一个对象需要非常繁琐的数据准备或访问权限,这时可以使用原型模式;
  • 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式copy多个对象供调用者使用,即保护性copy。
    注意:
    1.通过实现Cloneable接口的原型模式再调用clone函数构造实例时,并不一定比通过new操作效速度快,只有当通过new构造对象较为耗时或成本较高时,通过clone方法才能获得效率上的提升。因此,在使用Cloneable时需要考虑构建对象的成本以及一些效率上的测试。
    2.实现原型模式不一定非要实现Cloneable接口,也有其他的实现方式。

3.UML图

Android设计模式之原型模式_第1张图片

4.详解:

原型模式是一个创建型模式,‘原型’二字表明该模式应该有一个样板实例,提供给用户访问copy。用户从这个样板对象中复制出一个内部属性一致的对象的过程,称为克隆。被复制的样板对象就是‘原型’。原型模式多用于创建复杂的或构造耗时的实例,因为这些情况下,复制一个已经存在的实例可使程序运行效率更高。
下面用代码举例阐述:

public static class WordDocument implements Cloneable {
        private String text;
        private ArrayList images = new ArrayList<>();

        public WordDocument() {
            System.out.println("constructor start");
        }

        @Override
        protected WordDocument clone() {
            try {
                WordDocument doc = (WordDocument) super.clone();
                doc.text = text;
                doc.images = images;//浅拷贝
                return doc;
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
                return null;
            }
        }

        public String getText() {
            return text;
        }

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

        public List getImages() {
            return images;
        }

        public void addImage(String img) {
            images.add(img);
        }

        public void showDocument() {
            System.out.println("text:" + text);
            System.out.print("images:");
            for (int i = 0; i < images.size(); i++) {
                if (i == images.size() - 1) {
                    System.out.print(images.get(i) + "\n");
                } else {
                    System.out.print(images.get(i) + ",");
                }
            }
        }
    }

测试代码:

public static void main(String[] args) {
        WordDocument originDoc = new WordDocument();
        originDoc.setText("原文档text");
        originDoc.addImage("img1");
        originDoc.addImage("img2");
        originDoc.addImage("img3");
        originDoc.showDocument();
        System.out.println("=========================");

        WordDocument newDoc = originDoc.clone();
        newDoc.showDocument();
        System.out.println("=========================");

        newDoc.setText("修改后的text");
        newDoc.addImage("img4");
        newDoc.showDocument();

        System.out.println("=========================");
        originDoc.showDocument();
        /**
         * 输出结果:
         constructor start
         text:原文档text
         images:img1,img2,img3
         =========================
         text:原文档text
         images:img1,img2,img3
         =========================
         text:修改后的text
         images:img1,img2,img3,img4
         =========================
         text:原文档text
         images:img1,img2,img3,img4
         */
    }

这里的WordDocument 就是所谓的‘原型’,它里面有文字描述与图片url。现在用户需要重新编辑该文档,又想不破坏原文档,于是用户需要复制一份原文档,在拷贝文档上编辑,详细过程见测试代码。
从上面的测试结果可以得出如下结论:

  • 通过clone 拷贝对象时,并不会执行构造函数,因此,如果在构造函数中需要初始化操作的类型,在使用Cloneable实现拷贝时,需注意构造函数不会执行的问题。
  • 副文档修改了文本内容,原文档内容不受影响,这样保证了原文档的安全性;
  • 但是,细心的你也许发现了,在副文档中添加img4,原文档也会有img4。说好的安全呢?瞬间被打脸,有木有?其实这个例子是浅拷贝(也称为‘影子拷贝’),这份拷贝并不是将原始文档的所有字段都重构了一份,而是副文档的字段引用了原始文档的字段,即原文档与副文档的字段images其实指向同一个地址,于是才会出现上述情况。那么这种情况该如何规避?答案是深拷贝!即在拷贝对象时,对于引用型的字段也采用拷贝的形式,而不是上面的单纯引用的方式。
    下面修改clone方法:doc.images = (ArrayList) images.clone(),即深拷贝。
    @Override
        protected WordDocument clone() {
            try {
                WordDocument doc = (WordDocument) super.clone();
                doc.text = text;
                doc.images = (ArrayList) images.clone();//深拷贝
                return doc;
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
                return null;
            }
        }

测试代码不变,但是输出的结果却变了,如下:

        /**
         * 输出结果:
         constructor start
         text:原文档text
         images:img1,img2,img3
         =========================
         text:原文档text
         images:img1,img2,img3
         =========================
         text:修改后的text
         images:img1,img2,img3,img4
         =========================
         text:原文档text
         images:img1,img2,img3
         */

修改了副文档的images,原文档也没有改变,很安全了。
原型模式是非常简单的模式,它的核心问题就是对原始对象进行拷贝,在这个模式的使用过程中需要注意的一点就是深、浅拷贝的问题。在实际开发过程中,为了减少错误,尽量使用深拷贝原型模式,避免操作副本对象时,影响了原始对象的属性

5.代码托管地址

原型模式

你可能感兴趣的:(Android设计模式之原型模式)