原型模式

原型模式(Prototype Pattern)属于创建型模式,主要用于创建重复的对象,我们通常创建重复对象的时候都是通过set方法一个一个copy属性值的,这样如果类中属性特别多的话非常不好用,而且效率也不高在日常开发中,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void submitRepair(RepairForm repairForm) {
Repair repair = new Repair();
repair.setAppraisal(repairForm.getAppraisal());
repair.setContent(repairForm.getContent());
repair.setReply(repairForm.getReply());
repair.setReplyTime(repairForm.getReplyTime());
repair.setStarNum(repairForm.getStarNum());
repair.setState(repairForm.getState());
repair.setVisitTime(repairForm.getVisitTime());
repair.setTime(repairForm.getCreateTime());
repair.setTitle(RepairTypeEnums.getTextByType(repairForm.getType()));
// ...
xxxService.submit(repair);
}

这样的代码,一旦类属性过多,就会显得很臃肿,而原型模式,能帮助我们解决这样的问题。

简介

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

原型模式主要适用于以下场景:

  • 类初始化消耗资源较多。

  • new 产生的一个对象需要非常繁琐的过程(数据准备、访问权限等)

  • 构造函数比较复杂。

  • 循环体中生产大量对象时。

在 Spring 中,原型模式应用得非常广泛。例如 scope=“prototype”,在我们经常用 的 JSON.parseObject()也是一种原型模式。下面,我们来看看原型模式类结构图:

image-20200918101451519

简单克隆

简单原型模式的设计代码如下:

  • 定义一个Prototype接口
1
2
3
public interface Prototype {
Prototype clone();
}
  • 创建需要被克隆的类

有多个可以使用类似的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class PrototypeA implements Prototype {

private int age;
private String name;
private List hobbies;

// getter...
// setter...

@Override
public Prototype clone() {
PrototypeA concretePrototype = new PrototypeA();
concretePrototype.setAge(this.age);
concretePrototype.setName(this.name);
concretePrototype.setHobbies(this.hobbies);
return concretePrototype;
}
}
  • 创建一个client
1
2
3
4
5
6
7
8
9
10
11
public class Client {
private Prototype prototype;

public Client(Prototype prototype) {
this.prototype = prototype;
}

public Prototype startClone(Prototype concretePrototype) {
return (Prototype) concretePrototype.clone();
}
}
  • 测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class PrototypeTest {
public static void main(String[] args) {
// 创建一个具体的需要克隆的对象
PrototypeA concretePrototype = new PrototypeA();
// 填充属性,方便测试
concretePrototype.setAge(18);
concretePrototype.setName("prototype");
List hobbies = new ArrayList<String>();
concretePrototype.setHobbies(hobbies);
System.out.println(concretePrototype);

// 创建 Client 对象,准备开始克隆
Client client = new Client(concretePrototype);
PrototypeA concretePrototypeClone = (PrototypeA)
client.startClone(concretePrototype);
System.out.println(concretePrototypeClone);
System.out.println("克隆对象中的引用类型地址值:" + concretePrototypeClone.getHobbies());
System.out.println("原对象中的引用类型地址值:" + concretePrototype.getHobbies());
System.out.println("对象地址比较:" + (concretePrototypeClone.getHobbies() ==
concretePrototype.getHobbies()));
}
}
  • 运行结果
1
2
3
4
5
com.design.pattern.prototype.simple.PrototypeA@24d46ca6
com.design.pattern.prototype.simple.PrototypeA@4517d9a3
克隆对象中的引用类型地址值:[]
原对象中的引用类型地址值:[]
对象地址比较:true

从测试结果看出 hobbies 的引用地址是相同的,意味着复制的不是值,而是引用的地址。 这样的话,如果我们修改任意一个对象中的属性值,concretePrototype 和 concretePrototypeCone 的 hobbies 值都会改变,这个就是我们说的浅克隆。

深克隆

我们知道公司的员工除了自己的信息以外,都会有一个工位,但是每个人的工位都是不相同的,我们用深克隆来实现这个场景。

  • 定义员工信息类
1
2
3
4
public class StaffInfo {
public String name;
public int age;
}
  • 工位类
1
2
3
4
5
6
public class Station implements Serializable {
// 区域
public String area;
// 号码
public String number;
}
  • 员工类
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
public class Staff extends StaffInfo implements Serializable, Cloneable {
public Station station;

public Staff() {
station = new Station();
}

@Override
protected Object clone() throws CloneNotSupportedException {
return this.deepClone();
}

/**
* 深克隆
*
* @return
*/
public Object deepClone() {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
Staff copy = (Staff) ois.readObject();
return copy;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

/**
* 浅克隆
*
* @param target
* @return
*/
public Staff shallowClone(Staff target) {
Staff staff = new Staff();
staff.age = target.age;
staff.name = target.name;
staff.station = target.station;
return staff;
}
}
  • 测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class DeepCloneTest {

public static void main(String[] args) {
Staff staff = new Staff();
try {
Staff clone = (Staff) staff.clone();
System.out.println("深克隆:" + (staff.station == clone.station));
} catch (Exception e) {
e.printStackTrace();
}
Staff q = new Staff();
Staff n = q.shallowClone(q);
System.out.println("浅克隆:" + (q.station == n.station));
}
}
  • 结果
1
2
深克隆:false
浅克隆:true