(《设计模式解析与实战》读书笔记)
一、定义
用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象;
二、使用场景
(1)类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等,通过原型拷贝避免这些消耗;
(2)通过new产生一个对象需要非常繁琐的数据准备或访问权限,这时可使用原型模式;
(3)一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用,即保护性拷贝。
三、简单示例
修改一份文档,我们一般不会直接修改“原型”,而是拷贝一份副本进行修改。
/**
* 文档是具体的原型类;Cloneable是接口,声明具备clone能力
*/
public class WordDocument implements Cloneable {
// 文本
private String mText;
// 图片列表
private ArrayList<String> mImages = new ArrayList<>();
public WordDocument() {
System.out.println("------我是WordDocument构造函数-------");
}
@Override
protected WordDocument clone() {
try {
WordDocument doc = (WordDocument) super.clone();
doc.mText = this.mText;
doc.mImages = this.mImages;
return doc;
} catch (Exception e) {
}
return null;
}
public String getmText() {
return mText;
}
public void setmText(String mText) {
this.mText = mText;
}
public ArrayList<String> getmImages() {
return mImages;
}
public void addImages(String img) {
this.mImages.add(img);
}
/**
* 打印文档内容
*/
public void showDocument() {
System.out.println("-----开始打印-----");
System.out.println("Text :" + mText);
System.out.println("Images List : ");
for (String imgName : mImages) {
System.out.println("image name : " + imgName);
}
System.out.println("-----打印结束-----");
}
}
public class Client {
public static void main(String[] args) {
// 1、构建文档对象
WordDocument document = new WordDocument();
// 2、修改文档
document.setmText("这是一篇文档");
document.addImages("图片1");
document.addImages("图片2");
document.showDocument();
// 以原始文档为原型,拷贝一份副本
WordDocument document2 = document.clone();
System.out.println("这是未修改的副本");
document2.showDocument();
// 修改文档副本,不会影响原始文档
document2.setmText("这是修改后的副本");
document2.showDocument();
// 验证原始文档是否被修改
document.showDocument();
}
}
输出后的结果:
可以看出在副本上修改不会影响到原型。并且通过clone拷贝对象时并不会执行构造函数。
修改Client代码:
// 修改文档副本
document2.setmText("这是修改后的副本");
document2.addImages("副本图片.jpg");
document2.showDocument();
运行结果:
会发现原型被修改了。原因是上述方法只是浅拷贝,document2的mImages只是单纯的指向了this.mImages引用,并没有重新构造一个mImages对象。
运用深拷贝可解决这个问题,在拷贝时,对于引用型的字段也要采用拷贝的形式,而不是引用的形式。
修改clone()方法:
@Override
protected WordDocument clone() {
try {
WordDocument doc = (WordDocument) super.clone();
doc.mText = this.mText;
// 对mImages对象调用clone()函数,进行深拷贝
doc.mImages = (ArrayList<String>) this.mImages.clone();
return doc;
} catch (Exception e) {
}
return null;
}
运行后发现副本添加图片并不会影响到原型。
所以在使用该模式时尽量使用深拷贝,避免操作副本时影响原始对象的问题。
四、优缺点
优点:
运行模式是在内存中二进制流的拷贝,要比直接new一个对象性能好很多,特别是要在一个循环体内产生大量的对象时,原型模式可以更好的体现其优点。
缺点:
直接在内存中拷贝,构造函数是不会执行的,在实际开发中应该注意这个潜在的问题。优点是减少了约束,缺点也是减少了约束,需要我们在实际应用中考虑。