【Mybatis之设计模式】装饰器模式下的FifoCache

什么是装饰器模式

装饰器模式(Decorator),动态地给一个对象添加一些额外的职责,就增加功能来说,装饰器模式比生成子类更加灵活。

装饰器模式组成部分

  • Component 定义一个对象的接口,定义了该对象的职责
  • Decorator 装饰器抽象类,继承Component接口,包含 Component接口实例
  • ConcreteComponent 是Component的实现类,实现了Component的接口
  • ConcreteDecoratorA,ConcreteDecoratorB 是ConcreteComponent的派生类,扩展了Decorator的职能

Mybatis缓存装饰器模式介绍

Cache接口定义了缓存的一系列操作,常见的
  • void putObject(Object key, Object value);
  • Object getObject(Object key);
  • Object removeObject(Object key);
  • void clear();
PerpetualCache内部实现
  • PerpetualCache内部使用了HashMap作为Cache的存储结构,源码如下:
public class PerpetualCache implements Cache {

  private final String id;

  private Map<Object, Object> cache = new HashMap<>();

  public PerpetualCache(String id) {
    this.id = id;
  }

  @Override
  public String getId() {
    return id;
  }

  @Override
  public int getSize() {
    return cache.size();
  }

  @Override
  public void putObject(Object key, Object value) {
    cache.put(key, value);
  }

  @Override
  public Object getObject(Object key) {
    return cache.get(key);
  }

  @Override
  public Object removeObject(Object key) {
    return cache.remove(key);
  }

  @Override
  public void clear() {
    cache.clear();
  }

  @Override
  public boolean equals(Object o) {
    if (getId() == null) {
      throw new CacheException("Cache instances require an ID.");
    }
    if (this == o) {
      return true;
    }
    if (!(o instanceof Cache)) {
      return false;
    }

    Cache otherCache = (Cache) o;
    return getId().equals(otherCache.getId());
  }

  @Override
  public int hashCode() {
    if (getId() == null) {
      throw new CacheException("Cache instances require an ID.");
    }
    return getId().hashCode();
  }

}

FifoCache源码详解

Fifo故名思意,先入先出,因为队列长度固定,意味着如果达到最大队列长度,每次putObject,就需要删除remove队列首部的key,FifoCache源码如下:

public class FifoCache implements Cache {

  private final Cache delegate;
  private final Deque<Object> keyList;
  private int size;

  public FifoCache(Cache delegate) {
    this.delegate = delegate;
    this.keyList = new LinkedList<>();
    this.size = 1024;
  }

  @Override
  public String getId() {
    return delegate.getId();
  }

  @Override
  public int getSize() {
    return delegate.getSize();
  }

  public void setSize(int size) {
    this.size = size;
  }

  @Override
  public void putObject(Object key, Object value) {
    cycleKeyList(key);
    delegate.putObject(key, value);
  }

  @Override
  public Object getObject(Object key) {
    return delegate.getObject(key);
  }

  @Override
  public Object removeObject(Object key) {
    return delegate.removeObject(key);
  }

  @Override
  public void clear() {
    delegate.clear();
    keyList.clear();
  }

  private void cycleKeyList(Object key) {
    keyList.addLast(key);
    if (keyList.size() > size) {
      Object oldestKey = keyList.removeFirst();
      delegate.removeObject(oldestKey);
    }
  }

}

FifoCache可能存在的问题

removeObject直接使用的delegate的remove方法,delegate直接将map中的Object删除,却没有删除keyList中的key Object,那么如果发生如下操作会怎么样?

  • 队列已满1024长度,
  • 随机删除某对象,调用removeObject,map中的元素个数为1023,而keyList长度任然是1024
  • 放入某对象,调用putObject

因为keySize已经达到1024,会删除List的第一个元素,再删除map中的对象,因没有删除keyList中的key元素,此时,map中的元素个数将一直保持在1023,直到之前删除的key成为keyList的第一个元素

FifoCache为什么删除Map中元素的同时不删除keyList
  • 思考:如果从实现功能的角度来看,这里应该是一个缺陷,但是从性能角度看,因为keyList采用的是双向链表的数据结构,算法复杂度为O(n),每次removeObject,需要查找keyList中的key,对性能有较大的影响,作者这里没有对keyList进行删除,从而提升remove性能。以上只是本人猜测,还不确定作者要这么做的真实考量
posted @ 2019-11-21 14:06  持续成长  阅读(215)  评论(0编辑  收藏  举报