创建式:原型模式

1 基本介绍

​ 原型模式大概是:创建一个原型对象,再通过复制这个原型对象来创建更多同类型的对象

​ 工作原理很简单:将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝自己来实现创建过程。

​ 通过克隆方法所创建的对象是全新的对象,它们在内存中拥有新的地址,通常对克隆所产生的对象进行修改对原型对象不会造成任何影响,每一个克隆对象都是相互独立

2 设计分析

2.1 栗子

​ 使用原型模式来实现工作周报的快速创建

image-20220314150001843

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
package DesignPatterns.JavaDesign.Prototype;

public class Test {
public static void main(String args[]) {
WeeklyLog log_previous = new WeeklyLog();
//创建原型对象
log_previous.setName("张无忌");
log_previous.setDate("第12周");
log_previous.setContent("这周工作很忙,每天加班!");
System.out.println("****周报****");
System.out.println("周次:" + log_previous.getDate());
System.out.println("姓名:" + log_previous.getName());
System.out.println("内容:" + log_previous.getContent());
System.out.println("--------------------------------");
WeeklyLog log_new;
log_new = log_previous.clone(); //调用克隆方法创建克隆对象
log_new.setDate("第13周");
System.out.println("****周报****");
System.out.println("周次:" + log_new.getDate());
System.out.println("姓名:" + log_new.getName());
System.out.println("内容:" + log_new.getContent());
System.out.println(log_previous == log_new);
System.out.println(log_previous.getDate() == log_new.getDate());
System.out.println(log_previous.getName() == log_new.getName());
System.out.println(log_previous.getContent() == log_new.getContent());
}
}
class WeeklyLog implements Cloneable {
private String name;
private String date;
private String content;
public void setName(String name) { this.name = name; }
public void setDate(String date) { this.date = date; }
public void setContent(String content) { this.content = content; }
public String getName() { return (this.name); }
public String getDate() {return (this.date); }
public String getContent() { return (this.content); }
//克隆方法clone(),此处使用Java语言提供的克隆机制
public WeeklyLog clone() {
Object obj = null;
try { obj = super.clone();
return (WeeklyLog)obj;
}catch(CloneNotSupportedException e) {
System.out.println("不支持复制!");
return null;
}
}
}

image-20220314150105599

2.2 浅拷贝与深拷贝

浅拷贝

​ 在浅克隆中:

  • 如果原型对象的成员变量是值类型,将复制一份给克隆对象;
  • 如果原型对象的 成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址

深拷贝

​ 在深克隆中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象

image-20220314150720771

2.3 原型管理器

​ 原型管理器(Prototype Manager)是将多个原型对象存储在一个集合中供客户端使用,它是一个专门负责克隆对象的工厂,其中定义了一个集合用于存储原型对象,如果需要某个原型对象的一个克隆,可以通过复制集合中对应的原型对象来获得。

image-20220314152548719

​ 下面模拟一个简单的公文管理器来介绍原型管理器的设计与实现

image-20220314152651242

代码如下

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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
package DesignPatterns.JavaDesign.Prototype;

import java.util.Hashtable;

public class Test1 {
public static void main(String[] args) {
//获取原型管理器对象
PrototypeManager pm = PrototypeManager.getPrototypeManager();
OfficialDocument doc1,doc2,doc3,doc4;
doc1 = pm.getOfficialDocument("far"); doc1.display();
doc2 = pm.getOfficialDocument("far"); doc2.display();
System.out.println(doc1 == doc2);
doc3 = pm.getOfficialDocument("srs");
doc3.display();
doc4 = pm.getOfficialDocument("srs");
doc4.display();
System.out.println(doc3 == doc4);
}
}

//抽象公文接口,也可定义为抽象类,提供clone()方法的实现,将业务方法声明为抽象方法
interface OfficialDocument extends Cloneable {
public OfficialDocument clone();
public void display();
}
//可行性分析报告(Feasibility Analysis Report)类
class FAR implements OfficialDocument {
public OfficialDocument clone() {
OfficialDocument far = null;
try {
far = (OfficialDocument) super.clone();
} catch (CloneNotSupportedException e) {
System.out.println("不支持复制!");
}
return far;
}
public void display() {
System.out.println("《可行性分析报告》");
}
}

//软件需求规格说明书(Software Requirements Specification)类
class SRS implements OfficialDocument {
public OfficialDocument clone() {
OfficialDocument srs = null;
try {
srs = (OfficialDocument)super.clone();
}catch(CloneNotSupportedException e) {
System.out.println("不支持复制!");
}
return srs;
}
public void display() {
System.out.println("《软件需求规格说明书》");
}
}

//原型管理器(使用饿汉式单例实现)
class PrototypeManager {
//定义一个Hashtable,用于存储原型对象
private Hashtable ht=new Hashtable();
private static PrototypeManager pm = new PrototypeManager();
//为Hashtable增加公文对象
private PrototypeManager() {
ht.put("far",new FAR());
ht.put("srs",new SRS());
}
//增加新的公文对象
public void addOfficialDocument(String key,OfficialDocument doc) {
ht.put(key,doc);
}
//通过浅克隆获取新的公文对象
public OfficialDocument getOfficialDocument(String key) {
return ((OfficialDocument)ht.get(key)).clone();
}
public static PrototypeManager getPrototypeManager() {
return pm;
}
}

image-20220314153135181

​ 在PrototypeManager中定义了一个Hashtable类型的集合对象,使用“键值对”来存储原型对象,客户端可以通过Key(如“far”或“srs”)来获取对应原型对象的克隆对象。

3 总结

优缺点分析

优点:

  1. 通过复制一个已有实例可以提高新实例的创建效率
  2. 扩展性较好,由于在原型模式中提供了抽象原型类,在客户端可以针对抽象原型类进行编程,而将具体原型类写在配置文件中,增加或减少产品类对原有系统都没有任何影响
  3. 可以使用深克隆的方式保存对象的状态

缺点:

  1. 需要为每一个类配备一个克隆方法,而且该克隆方法位于一个类的内部,当对已有的类进行改造时,需要修改源代码,违背了“开闭原则
  2. 实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重的嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来可能会比较麻烦。

适用场景

  1. 创建新对象成本较大,新的对象可以通过原型模式对已有对象进行复制来获得,如果是相似对象,则可以对其成员变量稍作修改。
  2. 如果系统要保存对象的状态,而对象的状态变化很小,或者对象本身占用内存较少时,可以使用原型模式配合备忘录模式来实现。
  3. 需要避免使用分层次的工厂类来创建分层次的对象,并且类的实例对象只有一个或很少的几个组合状态

创建式:原型模式
https://2w1nd.github.io/2022/03/14/设计模式/创建式:原型模式/
作者
w1nd
发布于
2022年3月14日
许可协议