原型模式

原型模式


概念

在设计系统过程中,有时候可能需要一些相同的或相似的对象,这时可以采用原型模式。

  • 定义: 原型模式(Prototype Pattern):使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

实例

java中的原型模式很简单,也就是Object对象的clone()方法,不过只是浅拷贝,也就是采用clone方法获得的拷贝对象不会将引用类型也拷贝下来,只会拷贝基本类型。需要注意的是:正确拷贝的对象与原来的对象不在一个内存地址上,也就是它们是两个互相独立的对象,互不影响,只是内容看起来一样而已

下面以文件复制过程来演示下浅拷贝:

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
/**
* ShallowClone
*/
public class File implements Cloneable{
private String name;
private String content;
private Date date;
private List<String> list;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Date getDate() {
return date;
}
public void setDate(Date data) {
this.date = data;
}
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
@Override
public File clone () {
Object obj = null;
try {
obj = super.clone();
return (File) obj;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
}

客户端代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Client {
public static void main(String[] args) {
File file0 = new File();
file0.setName("记事本0");
file0.setContent("呜呜呜呜呜呜呜呜呜");
file0.setDate(new Date());
ArrayList<String> list = new ArrayList<String>(3);
list.add("1");
list.add("2");
list.add("3");
file0.setList(list);
File file1 = file0.clone();
file1.setName("记事本1");
System.out.println(file0 == file1);//false
System.out.println(file0.getClass() == file1.getClass()); //true
System.out.println(file0.getDate() == file1.getDate());//true
System.out.println(file0.getList() == file1.getList());//true
}
}

输出结果似乎没问题,不过我们可以看到file0.getList() == file1.getList()结果竟然是true。对于引用类型来说,用 ==比较符比较的应该是内存地址。结果为true说明file0与file1都指向同一个对象,file1只是把“指针”给复制过来了。将来如果对file0中的List进行修改,file1中的List也会被改变。这就是所谓的浅拷贝了。

如果要实现深拷贝需要我们自己实现编码,被拷贝的对象必须实现Serializable接口。下面随便建立一个带List成员的类演示下实现深拷贝:

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
/**
* DeepClone
*/
public class DeepClone implements Serializable{
private List<String> list;
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
public DeepClone deepClone () throws IOException, ClassNotFoundException {
//将对象写入流
ByteArrayOutputStream bao = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bao);
oos.writeObject(this);
//将对象读出流
ByteArrayInputStream bai = new ByteArrayInputStream(bao.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bai);
return (DeepClone)ois.readObject();
}
}

客户端代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Client {
public static void main(String[] args) {
DeepClone dp0 = new DeepClone();
ArrayList<String> list = new ArrayList<String>(3);
list.add("1");
list.add("2");
list.add("3");
dp0.setList(list);
try {
DeepClone dp1 = dp0.deepClone();
System.out.println(dp0 == dp1); //false
System.out.println(dp0.getList() == dp1.getList()); //false
} catch (Exception e) {
e.printStackTrace();
}
}
}

可以看到dp0.getList() == dp1.getList()结果为false,说明List拷贝成功。

总结

缺点
  • 需要为每个类提供Clone方法,比较麻烦。
  • 实现深克隆比较麻烦,尤其是当对象存在多重嵌套时。
适用场景
  • 创建对象成本较大时,可考虑用克隆模式来复制,减少系统资源占用。