原型模式简单来说就是基于原型来进行创建新的对象,这种创建的方法可以有复制 拷贝等,创建出来的方式就叫做原型设计模式,简称原型模式

那么什么时候会出现对象的创建成本比较大的时候,也就是很适合使用原型模式的时候呢?

实际上,一般来说与数据库交互,或者和一些外部系统进行交互以及进行很多精密的计算之后,得到的数据算是比较复杂的数据,除此外 在内存或者CPU当中直接进行计算获得的数据本身是不会花费太多时间的,对于上面说的比较难以获得的数据使用原型模式是比较值得的

我们举一个例子来说明这样的情况

假如有一个数据库存储了10万条的搜索关键词的数据,我们需要在项目启动的时候,将这份数据加载到内存当中,并且方便查找,我们还需要建立一个key-value的形式,在Java中我们可以选择使用map这种形式,当然我们还有另一个系统b,用于定期的往数据库更新日志,比如如下的更新.

图片

为了保证数据的实时性,我们需要定时的从数据库中拿取数据,并更新到我们维持这个map当中,这样 我们只需要在系统a当中,记录一个更新时间,然后从数据库中查找出大于这个更新时间的数据,然后依次更新到我们的map里面   那这样我们的原型模式就可以加上使用了.

我们创建两个版本,并利用copyonwrite的设计思路,在新的版本上面我们进行更新,然后在更新完成之后用新的版本替换我们旧的版本,这样既保证了数据的可用性又避免了有过渡状态的存在

public class Demo {

private HashMap<String, SearchWord> currentKeywords=new HashMap<>();

public void refresh() {

HashMap<String, SearchWord> newKeywords = new LinkedHashMap<>();

// 从数据库中取出所有的数据,放入到newKeywords中

List<SearchWord> toBeUpdatedSearchWords = getSearchWords();

for (SearchWord searchWord : toBeUpdatedSearchWords) {

newKeywords.put(searchWord.getKeyword(), searchWord);

}

currentKeywords = newKeywords;

}

private List<SearchWord> getSearchWords() {

// TODO: 从数据库中取出所有的数据

return null;

}

}

上面的实现非常糙,因为我们每次都要去创建一个新的map,而且要进行10万次的插入,所以说不太好.为此我们使用了Java的克隆方法来创建一个新的map,并且在这个新的map基础之上进行更新,更新完成之后替换过去

public class Demo {

private HashMap<String, SearchWord> currentKeywords=new HashMap<>();

private long lastUpdateTime = -1;

public void refresh() {

// 原型模式就这么简单,拷贝已有对象的数据,更新少量差值

HashMap<String, SearchWord> newKeywords = (HashMap<String, SearchWord>) currentKeywords.clone();

// 从数据库中取出更新时间>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;

}

}

那样看起来就已经很完美了,因为直接拷贝一个数据比从集合里,一个个取出来计算哈希值要快得多,但是在Java当中是行不通的,因为Java当中有深拷贝和前拷贝两种概念

何为深拷贝和浅拷贝,相比我就不用说了,Java中的浅拷贝,就是只拷贝数据的地址,并不会真正的去复制对象中的数据,所以我们必须要将原有代码上的浅拷贝替换为深拷贝

常见的深拷贝的方式有两种,

一是递归的拷贝这个对象直到这个对象里面没有引用数据类型,只有基本数据类型,这样就能做到深拷贝

二是将这个对象序列化以及反序列化

这两种的代码实现我们分别列在下面,而且不管采用哪一种,虽然增加了时间和内存消耗,但是能够有效的避免出现过度版本的问题

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.getLastUpdateTime());

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;

}

}

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();

}

当然,有一种更加取巧的方法,就是我们进行一次浅拷贝,然后对于需要更新的对象,在利于深拷贝的方式,替换一个新的对象,这样能减少了空间和时间,又能保证数据一致性和可用性

public class Demo {

private HashMap<String, SearchWord> currentKeywords=new HashMap<>();

private long lastUpdateTime = -1;

public void refresh() {

// Shallow copy

HashMap<String, SearchWord> newKeywords = (HashMap<String, SearchWord>) currentKeywords.clone();

// 从数据库中取出更新时间>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())) {

newKeywords.remove(searchWord.getKeyword());

}

newKeywords.put(searchWord.getKeyword(), searchWord);

}

lastUpdateTime = maxNewUpdatedTime;

currentKeywords = newKeywords;

}

private List<SearchWord> getSearchWords(long lastUpdateTime) {

// TODO: 从数据库中取出更新时间>lastUpdateTime的数据

return null;

}

}

发表评论

邮箱地址不会被公开。 必填项已用*标注