原型模式
原型模式
模式动机
在软件系统中,有些对象的创建过程较为复杂,而且有时候需要频繁创建,原型模式通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象,这就是原型模式的意图所在
模式定义
原型模式(Prototype Pattern):原型模式是一种对象创建型模式,用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。原型模式允许一个对象再创建另外一个可定制的对象,无须知道任何创建的细节
Prototype Pattern: Specify the kind of objects to create using a prototypical instance, and create new objects by copying this prototype.
原型模式的基本工作原理是通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝原型自己来实现创建过程
模式结构
模式结构:
- Prototype: 抽象原型角色
- ConcretePrototype: 具体原型角色
- Client: 客户端角色
模式分析
- 在原型模式结构中定义了一个抽象原型类,所有的Java 类都继承自java.lang.Object,而Object类提供一个clone()方法,可以将一个Java对象复制一份。因此在Java中可以直接使用Object提供的clone()方法来实现对象的克隆,Java语言中的原型模式实现很简单。
- 能够实现克隆的Java类必须实现一个标识接口Cloneable,表示这个Java类支持复制。如果一个类没有实现这个接口但是调用了clone()方法,Java编译器将抛出一个CloneNotSupportedException异常。
- Java语言提供的clone()方法将对象复制了一份并返回给调用者。一般而言,clone()方法满足
- 对任何的对象x,都有x.clone() !=x,即克隆对象与原对象不是同一个对象
- 对任何的对象x,都有x.clone().getClass()==x.getClass(),即克隆对象与原对象的类型一样
- 如果对象x的equals()方法定义恰当,那么 x.clone().equals(x) 应该成立
浅克隆与深克隆
通常情况下,一个类包含一些成员对象,在使用原型模式克隆对象时,根据其成员对象是否也克隆,原型模式可以分为两种形式:深克隆和浅克隆。
- 浅克隆: 只复制对象本身的基本数据类型成员变量和引用类型成员变量的引用地址,而不复制引用类型成员变量所指向的对象。这样,克隆出的对象与原对象共享同一块内存空间。
- 深克隆: 复制对象本身的基本数据类型成员变量和引用类型成员变量所指向的对象。这样,克隆出的对象与原对象不共享内存空间。
样例代码
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import java.util.ArrayList;
import java.util.List;
// 原型接口
interface Prototype extends Cloneable {
Prototype clone() throws CloneNotSupportedException;
}
// 具体原型类
class Document implements Prototype {
private String title;
private List<String> tags; // 引用类型(演示深/浅拷贝区别)
private List<String> pages = new ArrayList<>(); // 引用类型(演示浅拷贝)
public Document(String title, List<String> tags, String initialPage) {
this.title = title;
this.tags = tags;
this.pages.add(initialPage);
}
// 浅拷贝实现(默认clone方法)
@Override
public Document clone() throws CloneNotSupportedException {
return (Document) super.clone();
}
// 深拷贝实现(自定义方法)
public Document deepCopy() {
// 创建基础数据的拷贝
Document copy = new Document(this.title, new ArrayList<>(this.tags), "");
// 深度拷贝引用类型
copy.pages = new ArrayList<>(this.pages);
return copy;
}
// 修改标题
public void setTitle(String title) {
this.title = title;
}
// 添加标签
public void addTag(String tag) {
tags.add(tag);
}
// 添加页面
public void addPage(String page) {
pages.add(page);
}
@Override
public String toString() {
return String.format("Document [%s] | Tags: %s | Pages: %s",
title, tags, pages);
}
}
// 演示使用原型模式
public class PrototypePatternDemo {
public static void main(String[] args) throws CloneNotSupportedException {
// 初始化原始文档
List<String> initialTags = new ArrayList<>();
initialTags.add("important");
Document original = new Document("年度报告", initialTags, "封面");
original.addPage("内容页");
System.out.println("===== 原始文档 =====");
System.out.println(original);
// 浅拷贝演示
Document shallowCopy = original.clone();
shallowCopy.setTitle("月度报告(浅拷贝)");
shallowCopy.addTag("draft");
shallowCopy.addPage("附录页");
System.out.println("\n===== 修改浅拷贝后 =====");
System.out.println("原始对象: " + original); // tags被修改,pages也被修改
System.out.println("浅拷贝对象: " + shallowCopy);
// 重新初始化
initialTags = new ArrayList<>();
initialTags.add("final");
Document deepOriginal = new Document("标准文档", initialTags, "封面");
// 深拷贝演示
Document deepCopy = deepOriginal.deepCopy();
deepCopy.setTitle("客户定制文档(深拷贝)");
deepCopy.addTag("confidential");
deepCopy.addPage("额外页");
System.out.println("\n===== 修改深拷贝后 =====");
System.out.println("原始对象: " + deepOriginal); // 所有属性保持不变
System.out.println("深拷贝对象: " + deepCopy);
}
}
模式优缺点
- 优点:
- 当创建新的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,通过一个已有实例可以提高新实例的创建效率
- 可以动态增加或减少产品类
- 原型模式提供了简化的创建结构
- 可以使用深克隆保存对象的状态
- 缺点:
- 需要为每一个类配备一个克隆方法,而且这个克隆方法需要对类的功能进行通盘考虑,这对全新的类来说不是很难,但对已有的类进行改造时,不一定是件容易的事,必须修改其源代码,违背了“开闭原则”
- 在实现深克隆时需要编写较为复杂的代码
适用环境
在以下情况下可以使用原型模式:
- 创建新对象成本较大,新的对象可以通过原型模式对已有对象进行复制来获得,如果是相似对象,则可以对其属性稍作修改
- 如果系统要保存对象的状态,而对象的状态变化很小,或者对象本身占内存不大的时候,也可以使用原型模式配合备忘录模式来应用。相反,如果对象的状态变化很大,或者对象占用的内存很大,那么采用状态模式会比原型模式更好
- 需要避免使用分层次的工厂类来创建分层次的对象,并且类的实例对象只有一个或很少的几个组合状态,通过复制原型对象得到新实例可能比使用构造函数创建一个新实例更加方便
This post is licensed under CC BY 4.0 by the author.
