设计模式(三)— 原型模式

使程序运行更高效——原型模式

原型模式的定义

用原型实例指定创建的种类,并通过复制这些原型创建新的对象。原型模式的本质上就是对象拷贝。·

原型模式的使用场景

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

原型模式的示例

具体的原型类WordDocument.class

/**
 * description: Cloneable也是一个标识接口,表明这个类的对象是可拷贝的
 */
public class WordDocument implements Cloneable {
    //文本
    private String text;
    //图片名列表
    private ArrayList<String> images = new ArrayList<>();

    public WordDocument() {
        Log.i("WordDocument", "WordDocument的构造函数");
    }

    /**
     * description: 这个方法并不是Cloneable接口中的,而是Object中的方法
     * 如果没有实现Cloneable接口却调用了clone()函数会报异常
     */
    @Override
    protected WordDocument clone() {
        try {
            WordDocument doc = (WordDocument) super.clone();
            doc.text = this.text;
            doc.images = this.images;
            return doc;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }

    public String getText() {
        return text;
    }

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

    public ArrayList<String> getImages() {
        return images;
    }

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

    public void showDocument() {
        Log.i("WordDocument", "Text: " + text);
        Log.i("WordDocument", "Images List: ");
        for (String image : images) {
            Log.i("WordDocument", "image name: " + image);
        }
    }

Client.class

public class Client {
    public void Test() {
        //构建文档对象
        WordDocument originDoc = new WordDocument();
        //编辑文档,添加图片等
        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();
    }
}

调用new Client().Test(),在log中查看日志,如图原型模式log1.jpg

原型模式log1.jpg

由log可以看到,doc2是通过originDoc.clone()创建的,并且doc2第一次输出的时候和originDoc输出是一样的,而doc2修改了文本内容以后并不会影响originDoc的文本内容,保证了originDoc的安全性。

需要注意一个问题:通过clone拷贝对象时不会执行构造函数,在log中也能看得出来,所以不能在构造函数中执行代码。

浅拷贝和深拷贝

其实上面的代码实际上是一个浅拷贝,不是将原始文档的所有字段都重新构造了一份,而是副本字段引用原始文本字段。那这么说的话,我们都知道A引用B,那么A和B这两个对象指向同一个地址,A变,B也会跟着变。接下来做一个小小的变化:

Client.class

public class Client {
    public void Test() {
        WordDocument originDoc = new WordDocument();

        originDoc.setText("这是一篇文档");
        originDoc.addImages("图片1");
        originDoc.addImages("图片2");
        originDoc.addImages("图片3");
        originDoc.showDocument();

        WordDocument doc2 = originDoc.clone();
        doc2.showDocument();

        doc2.setText("这是修改过的Doc2文本");
        //在doc2中新增一个图片 “ 哈哈.jpg ”
        doc2.addImages("哈哈.jpg");
        doc2.showDocument();

        originDoc.showDocument();
    }
}

调用之后再来看下打印的日志,请看原型模式log2.jpg

原型模式log2.jpg

哦吼,originDoc中也新增了一个图片哈哈.jpg,这是为啥呢?这是因为WordDocumentclone()方法只是简单的进行浅拷贝,引用类型的新对象doc2的images只是单纯的指向了this.images引用,并没有重新构造一个images对象,这就导致doc2中的images与原始文档中的是同一个对象,修改其中一个文档,另一个文档也会改变,这不是我们想看到的,那么这时就需要采用深拷贝,即在拷贝对象时,对于引用型的字段也要采用拷贝的形式,修改WordDocument中的clone()方法:

    @Override
    protected WordDocument clone() {
        try {
            WordDocument doc = (WordDocument) super.clone();
            doc.text = this.text;
            //对image对象也调用clone()函数,进行深拷贝
            doc.images = (ArrayList<String>) this.images.clone();
            return doc;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }

再来看看效果图原型模式log3.jpg

原型模式log2.jpg

原型模式是一个非常简单的模式,核心就是对原始对象的拷贝,为了减少错误,建议尽量使用深拷贝。

小结

原型模式本质上就是对象拷贝,使用原型模式可以解决构建复杂对象的资源消耗问题,还有个用途是保护性拷贝,也就是某个对象对外可能是只读的,为了防止外部对这个对象修改,可以返回一个对象拷贝实现只读的限制。

优点

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

缺点

直接内存中拷贝,所以构造函数不会执行。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值