原型模式
定义: 用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象.
使用场景:
- 类初始化需要消耗非常多的资源,这个资源包括数据,硬件资源等,通过原型拷贝避免这些消耗.
- 通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,这时可以用原型模型.
- 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用,保护性拷贝.
需要注意的是,通过实现 Cloneable 接口的原型模式在调用clone 函数构造实例时并不一定比通过 new 操作速度快,只有当通过 new 构造对象较为耗时或者成本较高时,通过 clone 方法才能够获得效率上的提升.
优点: 原型模式在内存中二进制流的拷贝,要比 new 一个对象性能好很多,特别是要在一个循环体内产生大量的对象时,原型模式可以更好地体现其优点.
缺点: 这既是它的优点也是缺点.直接在内存中拷贝,构造函数是不会执行的,在实际开发中应该注意这个潜在问题,优点就是减少了约束,缺点也是减少了约束.
下面用的例子来说明问题:
public class WordDocument implements Cloneable {
//文本
private String mText;
//图片名称表
private ArrayList<String> mImages = new ArrayList<String>();
public WordDocument() {
System.out.println("=======WordDocument构造函数=======");
}
@Override
public WordDocument clone() {
try {
WordDocument document = (WordDocument) super.clone();
document.mText = this.mText;
document.mImages = this.mImages;
return document;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
public String getmText() {
return mText;
}
public void setText(String mText) {
this.mText = mText;
}
public ArrayList<String> getmImages() {
return mImages;
}
public void addImages(String mImages) {
this.mImages.add(mImages) ;
}
public void showDocument(){
System.out.println("--------- Word Content Start -----------");
System.out.println("Text :"+mText);
System.out.println("Images list :");
for (String imName : mImages){
System.out.println("image name :"+imName);
}
System.out.println("============= word content end =======");
}
}
Client 端使用
public static void main(String[] args) {
//1. 构建文档对象
WordDocument originDoc = new WordDocument();
//2. 编辑文档,添加图片等.
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();
}
打印出来的结果是:
=======WordDocument构造函数=======
--------- Word Content Start -----------
Text :这是一篇文档
Images list :
image name :图片1
image name :图片2
image name :图片3
============= word content end =======
--------- Word Content Start -----------
Text :这是一篇文档
Images list :
image name :图片1
image name :图片2
image name :图片3
============= word content end =======
--------- Word Content Start -----------
Text :这是修改过的Doc2文本
Images list :
image name :图片1
image name :图片2
image name :图片3
============= word content end =======
--------- Word Content Start -----------
Text :这是一篇文档
Images list :
image name :图片1
image name :图片2
image name :图片3
============= word content end =======
以上是属于浅拷贝,这份拷贝实际上并不是将原始文档的所有字段都重新构造了一份,而是副本字段引用原始文档的字段,
当 A 引用 B 就是说两个对象指向同一个地址,当修改 A 时 B 也会改变,B 修改时 A 同样也会改变
比如,将 main 函数修改为 现在这样样子,会发生什么呢?
public static void main(String[] args) {
//1. 构建文档对象
WordDocument originDoc = new WordDocument();
//2. 编辑文档,添加图片等.
originDoc.setText("这是一篇文档");
originDoc.addImages("图片1");
originDoc.addImages("图片2");
originDoc.addImages("图片3");
originDoc.showDocument();
WordDocument doc2 = originDoc.clone();
doc2.showDocument();
doc2.setText("这是修改过的Doc2文本");
//区别 就在增加一个图片4
doc2.addImages("图片4");
doc2.showDocument();
originDoc.showDocument();
}
现在 打印出的结果是:
=======WordDocument构造函数=======
--------- Word Content Start -----------
Text :这是一篇文档
Images list :
image name :图片1
image name :图片2
image name :图片3
============= word content end =======
--------- Word Content Start -----------
Text :这是一篇文档
Images list :
image name :图片1
image name :图片2
image name :图片3
============= word content end =======
--------- Word Content Start -----------
Text :这是修改过的Doc2文本
Images list :
image name :图片1
image name :图片2
image name :图片3
image name :图片4
============= word content end =======
--------- Word Content Start -----------
Text :这是一篇文档
Images list :
image name :图片1
image name :图片2
image name :图片3
image name :图片4
============= word content end =======
可以看到的是 最后面, 拷贝的,原始的都有了 图片4 , 这就是因为以上就浅拷贝,引用类型的 doc2 的 images 只是单纯地指向了 this.images 引用,并没有重新构造一个 image 对象, 然后将原始文档的图片添加到新的 images 对象中, 这就导致了 doc2 中的 images 与原始文档中的是同一个对象,因此修改其中一个文档中的图片,另一个文档也会受影响.
要想解决这个问题呢,就需要采用深拷贝,而不是单纯引用的形式.clone 方法的修改如下:
@Override
public WordDocument clone() {
try {
WordDocument document = (WordDocument) super.clone();
document.mText = this.mText;
//对 mImages 对象也调用 clone() 函数,进行深拷贝
document.mImages = (ArrayList<String>) this.mImages.clone();
return document;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
现在打印出的结果是:
=======WordDocument构造函数=======
--------- Word Content Start -----------
Text :这是一篇文档
Images list :
image name :图片1
image name :图片2
image name :图片3
============= word content end =======
--------- Word Content Start -----------
Text :这是一篇文档
Images list :
image name :图片1
image name :图片2
image name :图片3
============= word content end =======
--------- Word Content Start -----------
Text :这是修改过的Doc2文本
Images list :
image name :图片1
image name :图片2
image name :图片3
image name :图片4
============= word content end =======
--------- Word Content Start -----------
Text :这是一篇文档
Images list :
image name :图片1
image name :图片2
image name :图片3
============= word content end =======