原型模式的原理与应用
如果对象的创建成本比较大, 而同一个类的不同对象之间差别不大(大部分字段都相同), 在这种情况下, 我们可以利用对已有对象(原型)进行复制(或者叫拷贝)的方式来创建新对象, 以达到节省创建时间的目的, 这种基于原型来创建对象的方式就叫做原型设计模式.
何为"对象的创建成本比较大"
创建对象包含的申请内存, 变量赋值这一过程, 本身并不会花费太多时间, 对于大部分业务系统来说, 这点时间是可以忽略的.
如果对象中的数据需要复杂的计算才能得到(比如排序, 计算哈希值), 或者需要从RPC, 网络, 数据库, 文件系统等非常慢速的IO中读取, 这种情况下, 我们就可以利用原型模式, 从其他已有对象中直接拷贝得到, 而不用每次在创建新对象时, 都重复执行这些耗时的操作.
原型模式的实现方式: 深拷贝与浅拷贝
深拷贝与浅拷贝的区别在于: 浅拷贝只会复制对象的索引(内存地址), 而不会复制对象本身. 深拷贝不仅会复制索引还好复制对象本身.
浅拷贝得到的对象跟原始对象共享数据, 当值进行修改后, 两边的值都会被修改, 因为他们是一个值.
而深拷贝得到的是一份完完全全独立的对象.
Java中的clone方法执行的就是浅拷贝.
进行深拷贝有两种方法:
-
递归拷贝对象, 对象的引用对象以及饮用对象的引用对象, 直到要拷贝的对象只包含基本数据类型, 没有饮用对象为止.
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(); }
优化
这两种方法, 不管采用哪种, 深拷贝都要比浅拷贝耗时, 耗内存空间. 我们可以先采用浅拷贝的方法来进行复制, 对于需要更新的对象, 再使用深拷贝的方式来创建一份新的对象, 替换老对象. 这种方法即利用了浅拷贝节省时间, 空间的优点. 又能保证数据符合要求.