一般在开发生产中,对于新需求的实现,我们一般会有两种方式来处理,一种是直接修改已有组件的代码,另一种是使用继承方式。第一种显然会破坏已有组件的稳定性。第二种,会导致大量子类的出现。装饰器模式可以动态的为对象添加功能,它是基于组合的方式来实现该功能的。组合优于继承。
装饰器模式也是需要一个原始需求抽象类或者接口,由它的子类或者实现类来完成它的实际功能,这是正常需求。当我们需要做扩展需求的时候,需要一个装饰抽象类(注意这里只有抽象类,没有接口)来继承该原始需求抽象类或者接口,目的是为了定义委托对象。再由该装饰抽象类的子类来完成扩展的需求。具体实例可以参考 设计模式整理
在mybatis的缓存模块中,它使用了装饰器模式的变体,将装饰抽象类直接放到了装饰实现类的内部,为了做一个比较,我们来看一下它的原始需求接口,基本实现类和它的装饰实现类
package org.apache.ibatis.cache;
import java.util.concurrent.locks.ReadWriteLock;
//原始需求接口
public interface Cache {
//该缓存对象的id
String getId();
//向缓存中添加数据,一般情况下,key是CacheKey,value是查询结果
void putObject(Object var1, Object var2);
//根据指定的key,在缓存中查找对应的结果对象
Object getObject(Object var1);
//删除key对应的缓存项
Object removeObject(Object var1);
//清空缓存
void clear();
//缓存项的个数
int getSize();
//获取读写锁
ReadWriteLock getReadWriteLock();
}
基本实现类PerpetualCache,我们可以看到它就是对一个HashMap的操作,实现了缓存的基本功能。
package org.apache.ibatis.cache.impl;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.cache.CacheException;
public class PerpetualCache implements Cache {
//Cache对象的唯一标识
private final String id;
//用以记录缓存项的Map对象
private Map<Object, Object> cache = new HashMap();
public PerpetualCache(String id) {
this.id = id;
}
public String getId() {
return this.id;
}
public int getSize() {
return this.cache.size();
}
public void putObject(Object key, Object value) {
this.cache.put(key, value);
}
public Object getObject(Object key) {
return this.cache.get(key);
}
public Object removeObject(Object key) {
return this.cache.remove(key);
}
public void clear() {
this.cache.clear();
}
public ReadWriteLock getReadWriteLock() {
return null;
}
public boolean equals(Object o) {
if(this.getId() == null) {
throw new CacheException("Cache instances require an ID.");
} else if(this == o) {
return true;
} else if(!(o instanceof Cache)) {
return false;
} else {
Cache otherCache = (Cache)o;
return this.getId().equals(otherCache.getId());
}
}
public int hashCode() {
if(this.getId() == null) {
throw new CacheException("Cache instances require an ID.");
} else {
return this.getId().hashCode();
}
}
}
它的装饰器实现类(以BlockingCache为例,实际上它有很多的装饰器实现类)
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.apache.ibatis.cache.decorators;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.cache.CacheException;
//阻塞版本的缓存装饰器
public class BlockingCache implements Cache {
//阻塞超时时长
private long timeout;
//所有的装饰器实现类所共有的底层缓存,所代表着装饰抽象类,虽然这里不是一个抽象类,而是一个接口
//相当于在装饰抽象类中使用委托机制是一个道理,这里委托的也是基本缓存实现类PerpetualCache
private final Cache delegate;
//每个key都有所对应的重入锁ReetrantLock对象
private final ConcurrentHashMap<Object, ReentrantLock> locks;
public BlockingCache(Cache delegate) {
this.delegate = delegate;
this.locks = new ConcurrentHashMap();
}
public String getId() {
return this.delegate.getId();
}
public int getSize() {
return this.delegate.getSize();
}
//此处进行了重入锁的释放,对委托类进行调用外,进行了增强
public void putObject(Object key, Object value) {
try {
this.delegate.putObject(key, value);
} finally {
this.releaseLock(key);
}
}
//此处进行了锁操作和释放,具体可以看到后面的实现
public Object getObject(Object key) {
this.acquireLock(key);
Object value = this.delegate.getObject(key);
if(value != null) {
this.releaseLock(key);
}
return value;
}
public Object removeObject(Object key) {
this.releaseLock(key);
return null;
}
public void clear() {
this.delegate.clear();
}
public ReadWriteLock getReadWriteLock() {
return null;
}
//由key来得到锁
private ReentrantLock getLockForKey(Object key) {
//重入锁对象
ReentrantLock lock = new ReentrantLock();
//如果locks(ConcurrentHashMap)中存在key,则赶回value,如果不存在则将key,value写入locks中,并返回null
ReentrantLock previous = (ReentrantLock)this.locks.putIfAbsent(key, lock);
//如果key拿不到锁,则使用新的lock,如果能拿到则使用拿到的value
return previous == null?lock:previous;
}
//获得锁
private void acquireLock(Object key) {
//拿到重入锁
Lock lock = this.getLockForKey(key);
//如果该锁是带超时时间的
if(this.timeout > 0L) {
try {
//在timeout时长后去拿取锁(注意这里不是锁多长时间),拿到返回true,拿不到返回false
boolean acquired = lock.tryLock(this.timeout, TimeUnit.MILLISECONDS);
//拿不到锁,抛出异常
if(!acquired) {
throw new CacheException("Couldn't get a lock in " + this.timeout + " for the key " + key + " at the cache " + this.delegate.getId());
}
} catch (InterruptedException var4) {
throw new CacheException("Got interrupted while trying to acquire lock for key " + key, var4);
}
//如果该锁不带超时时间
} else {
//直接锁定
lock.lock();
}
}
//释放锁
private void releaseLock(Object key) {
//拿取锁
ReentrantLock lock = (ReentrantLock)this.locks.get(key);
//判断拿到的锁是否是当前线程持有的
if(lock.isHeldByCurrentThread()) {
//释放锁
lock.unlock();
}
}
public long getTimeout() {
return this.timeout;
}
public void setTimeout(long timeout) {
this.timeout = timeout;
}
}