美文网首页
原型模式

原型模式

作者: 凯玲之恋 | 来源:发表于2020-05-16 23:58 被阅读0次

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

    这种基于原型来创建对象的方式就叫作原型设计模式(Prototype Design Pattern),简称原型模式

    何为“对象的创建成本比较大”?

    创建对象包含的申请内存、给成员变量赋值这一过程,本身并不会花费太多时间,或者说对于大部分业务系统来说,这点时间完全是可以忽略的。
    应用一个复杂的模式,只得到一点点的性能提升,这就是所谓的过度设计,得不偿失。

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

    举例

    
    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 中的 clone() 语法来复制一个对象。

    如果你熟悉的语言没有这个语法,那把数据从 currentKeywords 中一个个取出来,然后再重新计算哈希值,放入到 newKeywords 中也是可以接受的。

    毕竟,最耗时的还是从数据库中取数据的操作。相对于数据库的 IO 操作来说,内存操作和 CPU 计算的耗时都是可以忽略的。

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

    用散列表组织的搜索关键词信息是如何存储的。我画了一张示意图,
    从图中我们可以发现,散列表索引中,每个结点存储的 key 是搜索关键词,value 是 SearchWord 对象的内存地址。

    SearchWord 对象本身存储在散列表之外的内存空间中。


    TIM截图20200516234936.png

    浅拷贝和深拷贝的区别在于,浅拷贝只会复制图中的索引(散列表),不会复制数据(SearchWord 对象)本身。
    相反,深拷贝不仅仅会复制索引,还会复制数据本身。

    浅拷贝得到的对象(newKeywords)跟原始对象(currentKeywords)共享数据(SearchWord 对象),而深拷贝得到的是一份完完全全独立的对象。

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

    那如何实现深拷贝呢?总结一下的话,有下面两种方法。

    递归拷贝对象

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

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

    参考

    47 | 原型模式:如何最快速地clone一个HashMap散列表?

    相关文章

      网友评论

          本文标题:原型模式

          本文链接:https://www.haomeiwen.com/subject/bvidohtx.html