【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性能。以上只是本人猜测,还不确定作者要这么做的真实考量。