原型模式介绍

245 阅读3分钟

简介

本书第四章介绍了原型模式。

原型模式介绍

用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。被复制的实例就是“原型”,这个原型是可定制的。

使用场景

1.类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等,通过原型拷贝避免这些消耗。

2.通过 new 产生的一个对象需要非常繁琐的数据准备或者权限,这时可以使用原型模式。

3.一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用,即保护性拷贝。

简单实现

public class WordDocument implements Cloneable {
    // 文本
    private String mText;

    // 图片名列表
    private ArrayList mImages = new ArrayList();

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


    public String getmText() {
        return mText;
    }

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

    public ArrayList getmImages() {
        return mImages;
    }

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

    /**
     * 打印文档内容
     */
    public void showDocument() {
        System.out.println("------------word Content Start--------------");
        System.out.println("Text:" + mText);
        System.out.println("Images List:");
        for (String imgName : mImages) {
            System.out.println("image name:" + imgName);
        }
        System.out.println("------------word Content End--------------");
    }

}

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

        //2.编辑文档,添加图片内容
        originDoc.setmText("这是一篇文档");
        originDoc.addImage("图片 1");
        originDoc.addImage("图片 2");
        originDoc.addImage("图片 3");

        originDoc.showDocument();

        //以原始文档为原型,拷贝一份副本
        WordDocument doc2=originDoc.clone();
        doc2.showDocument();

        //修改文档副本
        doc2.setmText("修改");
        doc2.addImage("哈哈.jpg");
        doc2.showDocument();

        originDoc.showDocument();
    }
}

运行结果如下:


可以看到,originDoc 和 doc2 在 Text 修改之后输出是不一样的,但是在 doc2 中 doc2.addImage("哈哈.jpg") 之后,originDoc 也输出了 哈哈.jpg,说明这只是简单的浅拷贝,引用类型的的新对象 doc2 的 mImages 只是单纯地指向了 this.mImages 引用,并没有重新构造一个 mImages 对象。所以在原型模式里最需要注意的就是深拷贝和浅拷贝,使用深拷贝可以避免操作副本时影响原始对象的问题。我们在这里可以这么做:

@Override
protected WordDocument clone() {
    try {
        WordDocument doc = (WordDocument) super.clone();
        doc.mText = this.mText;
        ;
        doc.mImages = (ArrayList) this.mImages.clone();
        return doc;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

我们将 doc.mImages 指向 this.mImages 的一份拷贝,而不是 this.mImages 本身,所以在 doc2 添加图片不会影响 originDoc。结果如下:


Android源码中的原型模式

    Uri uri = Uri.parse("smsto:110");
    Intent intent = new Intent(Intent.ACTION_SEND,uri);
    intent.putExtra("sms_body", "The SMS text");
    //克隆
    Intent intent2 = (Intent)intent.clone();
    startActivity(intent2);

总结

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

2.还有一个重要的用途就是保护性拷贝,也就是对某个对象对外可能是只读的,为了防止外部对这个只读对象的修改,通常可以通过返回一个对象拷贝的形式实现只读的限制。

缺点:

1.这既是它的优点也是缺点,直接在内存中拷贝,构造函数是不会执行的,在实际开发中应该注意这个潜在问题。优点是减少了约束,缺点也是减少了约束,需要大家在实际应用时考虑。

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

参考资料

《Android 源码设计模式解析与实战 》