《Android源码设计模式解析与实战》读书笔记(四)——原型模式

xiaoxiao2021-02-28  84

以前就纠结过一个问题,什么是基本类型数据,什么是引用类型数据,它们有什么区别,看了原型模式后,也算是知道了它们的一点区别了。

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

原型模式是一个创建型的模式,该模式有一个样板实例,用户从这个样板对象中复制出一个内部属性一致的对象,这个过程就是俗称的“克隆”。被复制的实例就是所谓的“原型”,这个原型是可定制的。原型模式多用于创建复杂的或者构造耗时的实例,因为复制一个已经存在的实例可以使程序运行更高效。

1.定义

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

2.使用场景

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

3.简单实现

public class WordDocument implements Cloneable { private String text; private ArrayList<String> images = new ArrayList<>(); public WordDocument() { System.out.println("我是构造方法"); } public String getText() { return text; } public void setText(String text) { this.text = text; } public ArrayList<String> getImages() { return images; } public void addImage(String image) { this.images.add(image); } @Override protected WordDocument clone() { try { WordDocument wordDocument = (WordDocument) super.clone(); wordDocument.text = this.text; wordDocument.images = this.images; return wordDocument; } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; } public void showDocument() { System.out.println("文档开头"); System.out.println("text:" + text); System.out.println("images:" + images.size()); for (String image : images) { System.out.println("image:" + image); } System.out.println("文档结尾"); } }

调用端:

WordDocument originalWd = new WordDocument(); originalWd.setText("这是原始文档"); originalWd.addImage("原始图片1"); originalWd.addImage("原始图片2"); originalWd.addImage("原始图片3"); originalWd.showDocument(); WordDocument copyWd = originalWd.clone(); copyWd.showDocument(); copyWd.setText("这是原始文档克隆出来的克隆文档"); copyWd.showDocument(); originalWd.showDocument();

clone()方法并不是Cloneable接口中的,而是Object中的,Cloneable是一个标识接口,它表明这个对象是可拷贝的,如果没有实现Cloneable接口而调用clone()方法,则会抛出异常。克隆文档是通过original.clone()创建的,所以它们内容一样,而且克隆文档修改了text(注意,是text)后原始文档也没有改变,这就保证了原始文档的安全性。特别的是,通过clone()方法克隆的对象不会执行构造方法,所以如果在构造方法中有一些特殊操作的情况下,要注意构造方法不会执行的问题。

4.浅拷贝和深拷贝

简单实现的原型模式只是一个浅拷贝,也称为影子拷贝,它并不是将原始文档的所有字段都重新构造了一份,而是克隆文档的字段引用原始文档的字段,实际上是两个文档的对象都指向了同一个地址。这也就是说,修改其中一个文档的基本数据类型的变量时,另一个文档不会受影响,但是如果修改其中一个文档的引用数据类型的变量时,另一个文档就会受到影响了。用代码试试看,修改一下原来的代码(其实也就只是加入了一行代码),结果就不一样了。

WordDocument originalWd = new WordDocument(); originalWd.setText("这是原始文档"); originalWd.addImage("原始图片1"); originalWd.addImage("原始图片2"); originalWd.addImage("原始图片3"); originalWd.showDocument(); WordDocument copyWd = originalWd.clone(); copyWd.showDocument(); copyWd.setText("这是原始文档克隆出来的克隆文档"); copyWd.addImage("克隆图片1"); copyWd.showDocument(); originalWd.showDocument(); 可以看到原始文档中的文本没有变,但是图片却多了一张。因为String是基本数据类型,List是引用数据类型,在克隆文档中增加了一张图片,由于克隆文档并不是重新构造一个iamges对象,而是和原始文档指向的同一个引用,所以原始文档也被修改了。这很显然不是我们希望看到的,如何解决这个问题,方法就是采用深拷贝,即在拷贝对象(引用数据类型)时,对于引用型的字段也要采取拷贝而不是单纯的引用的形式。 修改一下clone()方法。

@Override protected WordDocument clone() { try { WordDocument wordDocument = (WordDocument) super.clone(); wordDocument.text = this.text; // wordDocument.images = this.images; wordDocument.images = (ArrayList<String>) this.images.clone(); return wordDocument; } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; } 可以看到修改克隆文档中的引用数据类型的变量并不会影响到原始文档了。 原型模式是非常简单的一个模式,需要注意的问题就是浅拷贝和深拷贝的问题,一般为了减少失误,都尽量采用深拷贝。

5.总结

原型模式本质就是对象拷贝,类似于C++中的拷贝构造函数,使用原型模式可以解决构建复杂对象的资源消耗问题,能够在某些场景下提升创建对象的效率,还有一个用途就是保护性拷贝,即某个对象可能对外是只读的,为了防止外部对这个只读对象进行修改,通常可以通过返回一个克隆对象的形式来实现只读的限制。 优点 1).原型模式是在内存中进行二进制流的拷贝,比直接new一个对象性能要好很多,特别是要在一个循环体内产生大量的对象时,原型模式能更好的体现其优点。 缺点 1).正因为它是直接从内存中拷贝,所以不会执行构造函数,实际开发中可能需要注意这个问题。

Demo下载

转载请注明原文地址: https://www.6miu.com/read-28531.html

最新回复(0)