使程序运行更高效-原型模式
原型模式多用于创建复杂的或者构造耗时的实例,因为这种情况下,复制一个已经存在的实例可使程序运行更高效。
原型模式的定义
用原型实例指定创建对象的种类,并通过复制这些原型创建新的对象。
使用场景
- 类初始化需要消耗非常多的资源,这个资源包括数据,硬件资源等,通过原型复制避免这些消耗。
- 通过new产生一个对象需要非常繁琐的数据准备或访问权限,这时可以使用原型模式。
- 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式复制多个对象供调用者,提供保护性拷贝
注意:实现Cloneable接口的原型模式在调用clone函数构造实例并不一定比new对象快。只有在new对象时候非常耗时或者繁琐的时候,通过clone函数才会获得效率上的提升。
简单实现
文档类 JavaBean
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
package Prototype;
import java.util.ArrayList;
public class WordDocument implements Cloneable{
//文本
private String mText;
//图片名列表
private ArrayList<String> mImages=new ArrayList<String>();
public WordDocument() {
System.out.println("-----------构造函数-----------");
}
@Override
protected WordDocument clone(){
try {
WordDocument document=(WordDocument) super.clone();
document.mImages=this.mImages;
document.mText=this.mText;
return document;
}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 setmImages(ArrayList<String> mImages) {
this.mImages = mImages;
}
public void addImg(String name){
this.mImages.add(name);
}
//打印文档内容
public void showDocument(){
System.out.println("-------word Content Start------");
System.out.println("text:"+mText);
System.out.println("imgList:");
for (String name:mImages) {
System.out.println("imgName:"+name);
}
System.out.println("---------word Content End-------");
}
}
|
演示端:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package Prototype;
public class Client {
public static void main(String[] args) {
WordDocument document=new WordDocument();
document.setmText("这个是文档");
document.addImg("图片1");
document.addImg("图片2");
document.addImg("图片3");
document.showDocument();
//以原始文档为原型,拷贝一份副本
WordDocument doc2= document.clone();
doc2.showDocument();
doc2.setmText("这个是修改过的副本文档");
doc2.showDocument();
document.showDocument();
}
}
|
执行结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
-----------构造函数-----------
-------word Content Start------
text:这个是文档
imgList:
imgName:图片1
imgName:图片2
imgName:图片3
---------word Content End-------
-------word Content Start------
text:这个是文档
imgList:
imgName:图片1
imgName:图片2
imgName:图片3
---------word Content End-------
-------word Content Start------
text:这个是修改过的副本文档
imgList:
imgName:图片1
imgName:图片2
imgName:图片3
---------word Content End-------
-------word Content Start------
text:这个是文档
imgList:
imgName:图片1
imgName:图片2
imgName:图片3
---------word Content End-------
Process finished with exit code 0
|
由上面可以看到,doc2是通过document.clone()创建的,并且第一次输出的时候2个对象的输出是一样的。而且doc2修改文本内容后不会改变影响document对象的文本内容,这就保证了document的安全性。还需要注意的是,通过clone拷贝对象的时候是不会执行构造函数的
深拷贝和浅拷贝
上面的拷贝被称为浅拷贝,也称作影子拷贝。并不是将所有字段都重新构造了一份,而是通过副本文档的字段引用原始文档的字段。
因此,如果A引用B,那么无论修改A还是B对象,另一个对象都会随之改变。但我们大多数时候都需要A/B对象改变的时候另一个对象不会改变,那么我们如何避免这种情况呢,这边就引出了新的概念:深拷贝
所谓深拷贝,即在拷贝对象时,对于引用型的字段也要采用拷贝的形式,而不是单纯的引用形式。这里就借用上面的demo代码做相关改动,使浅拷贝变成深拷贝。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
//深拷贝
@Override
protected WordDocument clone(){
try {
WordDocument document=(WordDocument) super.clone();
document.mImages=(ArrayList) this.mImages.clone();
document.mText=this.mText;
return document;
}catch (Exception e){
}
return null;
}
//浅拷贝
@Override
protected WordDocument clone(){
try {
WordDocument document=(WordDocument) super.clone();
document.mImages=this.mImages;
document.mText=this.mText;
return document;
}catch (Exception e){
}
return null;
}
|
我们可以看到上面的改动对比:深拷贝时调用的clone函数,将中间mImages指向this.mImages的拷贝,而不是它的本身。这样在修改doc2对象时调用addImg()函数就不会影响到document对象了。
so 我们在平时使用的时候尽量要使用深拷贝,以免造成操作副本时影响原始对象的问题