设计模式-创建型-原型模式

  1. 1. 什么是原型模式
  2. 2.如何使用原型模式
    1. 2.1 实际场景案例
    2. 2.2 根据需求的迭代

1. 什么是原型模式

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

  • 如何理解对象的创建成本比较大
    • 比如对象当中的数据需要经过复杂的计算才能得到(排序,哈希)
    • 需要IO读取

在这种情况下,我们就可以利用原型模式,从其他已有对象当中直接拷贝,而不是在每次创建对象的时候,重复执行这个非常耗时的操作。

2.如何使用原型模式

2.1 实际场景案例

  • 数据库 存储10万条搜索关键词信息

    • 包含关键词,关键词被搜索次数,信息最近被更新的时间

    • 系统A启动的时候会加载这份数据到内存当中,用于处理某些其他的业务需求

    • 构建散列表索引 – hashmap

      • key为搜索关键词
      • value为关键词的详细信息
    • 系统B分析搜索日志,每隔10分钟就批量更新数据库中的数据,并且标记为新的数据版本

    • 系统A需要定期根据数据库的数据更新内存中的索引和数据

2.2 根据需求的迭代

  • 在系统A中记录更新时间,在数据库中拿出更新时间大于系统A当中的搜索关键词,然后针对差集中的每个关键词进行处理
  • 如果在散列表中了,更新相应的搜索次数,更新时间等
  • 如果不在,插入散列表当中
public class Demo {
  private ConcurrentHashMap<String, SearchWord> currentKeywords = new ConcurrentHashMap<>();
  private long lastUpdateTime = -1;

  public void refresh() {
    // 从数据库中取出更新时间>lastUpdateTime的数据,放入到currentKeywords中
    List<SearchWord> toBeUpdatedSearchWords = getSearchWords(lastUpdateTime);
    long maxNewUpdatedTime = lastUpdateTime;
    for (SearchWord searchWord : toBeUpdatedSearchWords) {
      if (searchWord.getLastUpdateTime() > maxNewUpdatedTime) {
        maxNewUpdatedTime = searchWord.getLastUpdateTime();
      }
      if (currentKeywords.containsKey(searchWord.getKeyword())) {
        currentKeywords.replace(searchWord.getKeyword(), searchWord);
      } else {
        currentKeywords.put(searchWord.getKeyword(), searchWord);
      }
    }

    lastUpdateTime = maxNewUpdatedTime;
  }

  private List<SearchWord> getSearchWords(long lastUpdateTime) {
    // TODO: 从数据库中取出更新时间>lastUpdateTime的数据
    return null;
  }
}
  • 如果需要任何时刻系统A中的所有数据都必须是同一个版本的
  • 更新内存数据的时候,系统A不能处于不可用的状态,不能停机更新数据
    • 针对需求,我们需要出了正在使用的服务版本之外,创建另外一个版本的数据。当新的版本数据建好之后,再一次性地将服务版本进行切换
    • 可以保证数据一直可用,并且避免中间状态的存在
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;
  }
}
  • 新数据结构的构建成本非常高,需要IO读出数据库,计算哈希值,构建newKeywords
  • 我们可以拷贝当前的版本到新的待处理的散列表当中,然后从数据库当中拿出新增或者有更新的关键词,来做更新
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;
  }
}
  • 上述代码做的是浅拷贝,因为在散列表当中,key存的是搜索关键词,而value实际上存储的是对象的内存地址
  • 当我们做浅拷贝的时候,我们实际上是把内存地址给拷贝了过来;这样的话当我们做修改的话,实际上两个版本的数据都做了变动,并没有将其彻底的分割开
  • 我们实际需要的是深拷贝,即不仅仅复制索引,并且复制数据本身
    • 递归拷贝对象,对象的引用对象以及引用对象的引用对象
    • 先将对象序列化,再反序列化成新对象
// 实现递归深拷贝
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;
  }
}

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 stone2paul@gmail.com

文章标题:设计模式-创建型-原型模式

文章字数:1.6k

本文作者:Leilei Chen

发布时间:2020-06-18, 02:46:18

最后更新:2020-06-18, 02:46:53

原始链接:https://www.llchen60.com/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E5%88%9B%E5%BB%BA%E5%9E%8B-%E5%8E%9F%E5%9E%8B%E6%A8%A1%E5%BC%8F/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录
×

喜欢就点赞,疼爱就打赏