原型模式的原理与应用

如果对象的创建成本比较大,而同一个类的不同对象之间差别不大(大部分字段都相同), 在这种情况下,我们可以利用对已有对象(原型)进行复制(或者叫拷贝)的方式来创建新 对象,以达到节省创建时间的目的。这种基于原型来创建对象的方式就叫作原型设计模式 (Prototype Design Pattern),简称原型模式。

什么是对象成本创建比较大

如果对象中的数据需要经过复杂的计算才能得到(比如排序、计算哈希值),或者需 要从 RPC、网络、数据库、文件系统等非常慢速的 IO 中读取,这种情况下,我们就可以利 用原型模式,从其他已有对象中直接拷贝得到,而不用每次在创建新对象的时候,都重复执 行这些耗时的操作。

原型模式的实现方式:深拷贝和浅拷贝

散列表索引中,每个结点存储的 key 是搜索关 键词,value 是 SearchWord 对象的内存地址。SearchWord 对象本身存储在散列表之外 的内存空间中。

image-20230612154147518

浅拷贝和深拷贝的区别在于,浅拷贝只会复制图中的索引(散列表),不会复制数据 (SearchWord 对象)本身。相反,深拷贝不仅仅会复制索引,还会复制数据本身。浅拷 贝得到的对象(newKeywords)跟原始对象(currentKeywords)共享数据 (SearchWord 对象),而深拷贝得到的是一份完完全全独立的对象。

image-20230612154246807

image-20230612154256726

在 Java 语言中,Object 类的 clone() 方法执行的就是我们刚刚说的浅拷贝。它只会拷贝对 象中的基本数据类型的数据(比如,int、long),以及引用对象(SearchWord)的内存 地址,不会递归地拷贝引用对象本身。

两个深拷贝demo

递归拷贝对象、对象的引用对象以及引用对象的引用对象……直到要拷贝的对 象只包含基本数据类型数据,没有引用对象为止。

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
public class Demo {
private HashMap<String, SearchWord> currentKeywords=new HashMap<>();
private long lastUpdateTime = -1;
public void refresh() {
// Deep copy
HashMap<String, SearchWord> newKeywords = new HashMap<>();
for (HashMap.Entry<String, SearchWord> e : currentKeywords.entrySet()) {
SearchWord searchWord = e.getValue();
SearchWord newSearchWord = new SearchWord(
searchWord.getKeyword(), searchWord.getCount(), searchWord.getLast());
newKeywords.put(e.getKey(), newSearchWord);
}
// 从数据库中取出更新时间>lastUpdateTime的数据,放入到newKeywords中
List<SearchWord> toBeUpdatedSearchWords = getSearchWords(lastUpdateTime);
long maxNewUpdatedTime = lastUpdateTime;
for (SearchWord searchWord : toBeUpdatedSearchWords) {
if (searchWord.getLastUpdateTime() > maxNewUpdatedTime) {
maxNewUpdatedTime = searchWord.getLastUpdateTime();
}
if (newKeywords.containsKey(searchWord.getKeyword())) {
SearchWord oldSearchWord = newKeywords.get(searchWord.getKeyword());
oldSearchWord.setCount(searchWord.getCount());
oldSearchWord.setLastUpdateTime(searchWord.getLastUpdateTime());
} else {
newKeywords.put(searchWord.getKeyword(), searchWord);
}
}
lastUpdateTime = maxNewUpdatedTime;
currentKeywords = newKeywords;
}
private List<SearchWord> getSearchWords(long lastUpdateTime) {
// TODO: 从数据库中取出更新时间>lastUpdateTime的数据
return null;
}
}

demo2先将对象序列化,然后再反序列化成新的对象。

1
2
3
4
5
6
7
8
public Object deepCopy(Object object) {
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream oo = new ObjectOutputStream(bo);
oo.writeObject(object);
ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
ObjectInputStream oi = new ObjectInputStream(bi);
return oi.readObject();
}

参考

《设计模式之美》