设计模式GOF23--原型模式

what is the prototype model(原型模式是什么)?

  • 用原型实例制定创建的对象的种类,并且通过拷贝这些原型创建新的对象
  • 就是通过JAVA的克隆技术,以某个对象为原型,复制出新的对象。显然,新的对象具备原型对象的特点
  • 克隆类似于new,但是不同于new。new创建的对象属性采用的是默认值。克隆出的对象属性值于原型对象相同。并且克隆出的新对象改变不会影响原型对象。然后,再修改克隆对象的值。

Why use prototype mode(为什么要用原型模式)?

  • 通过new产生的一个对象需要非常繁琐的数据准备或访问权限,则可以用原型模式。
  • 优势有:效率高(直接克隆,避免了重新构造过程的步骤)

浅复制和深复制

浅复制和深复制是个什么鬼?憋说话,我们先上代码。

浅复制代码解析

我们先来建一个羊的实体类,属性包括名称和出生日期。如果想实现克隆(复制),我们需要实现Cloneable接口。重写他的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
56
57
58
59
60
61
public class Sheep implements Cloneable{
private String sname;
private Date birthday;

public String getSname() {
return sname;
}

public void setSname(String sname) {
this.sname = sname;
}

public Date getBirthday() {
return birthday;
}

public void setBirthday(Date birthday) {
this.birthday = birthday;
}

@Override
protected Object clone() throws CloneNotSupportedException {
Object obj = super.clone();
return obj;
}

public Sheep(String sname, Date birthday) {
this.sname = sname;
this.birthday = birthday;
}

public Sheep() {
}

}

````
我们来测试一下,实例化两个羊,我们来看看不同。(如果没有原型模式,那我们多半会直接new 两个Sheep,这样确实可以实现需求,可是,就考虑代码性能方面来看,每次调用构造方法,都会造成大量的资源耗损,那么,我们来试试传说中的原型模式)使用clone()方法,我们来构建s2(第二只羊)。然后将他们的属性都打印出来。

````java
/**
* 测试原型模式(浅克隆)
* 克隆分(深克隆和浅克隆)
*/
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Date date = new Date(234234234234L);
Sheep s1 = new Sheep("小羊",date);
Sheep s2 = (Sheep) s1.clone();
System.out.println(s1);
System.out.println(s1.getSname());
System.out.println(s1.getBirthday());
date.setTime(123123132L);
System.out.println(s1.getBirthday());

s2.setSname("大羊");
System.out.println(s2);
System.out.println(s2.getSname());
System.out.println(s2.getBirthday());
}
}

结果:
com.kid.prototype.Sheep@4554617c
小羊
Sat Jun 04 09:03:54 CST 1977
Fri Jan 02 18:12:03 CST 1970
com.kid.prototype.Sheep@7f31245a
大羊
Fri Jan 02 18:12:03 CST 1970

分析结果,可以发现 s1和s2是两个不同的实体类。而且我们在克隆s2之后,改变其sname参数的值,也没有影响到原模型的参数。但是!我们发现我们在改变s1的date参数的时候,直接影响到了s2的结果。那么就涉及到之前所提的深复制浅复制了。

深复制浅复制解析

  • 浅复制:当原型对象被复制时,只复制它本身和其中包含的值类型的成员变量,而引用类型的成员变量并没有复制。
  • 深复制:在计算机中开辟了一块新的内存地址用于存放复制的对象。
  • 深复制和浅复制最根本的区别在于是否是真正获取了一个对象的复制实体,而不是引用。

深复制代码解析

我们将实体类中的clone方法重写

1
2
3
4
5
6
7
8
@Override
protected Object clone() throws CloneNotSupportedException {
Object obj = super.clone();
//添加如下代码实现深复制
Sheep2 s = (Sheep2) obj;
s.birthday = (Date) this.birthday.clone(); //把属性也形象克隆
return obj;
}

然后再调用测试方法测试一下

结果:
com.kid.prototype.Sheep2@4554617c
小羊
Sat Jun 04 09:03:54 CST 1977
Fri Jan 02 18:12:03 CST 1970
com.kid.prototype.Sheep2@7f31245a
大羊
Sat Jun 04 09:03:54 CST 1977

我们会发现,这个时候,s2的出生日期和s1未修改之前的日期一样,这样我们就实现了深复制。

原型模式图结构图

原型模式实现

  • Cloneable接口和clone方法
  • prototype模式中实现起来最困难的地方就是内存复制操作,所幸java中提供了clone方法替我们做了绝大部分事情。
注:该博文为学习总结,视频来源为高淇java300集