装饰器模式
装饰器模式允许我们通过将原始对象放入包含特殊行为的封装对象中来为原对象绑定新的行为,使得原始对象可以保留其本来的行为,也可以扩展新的行为。
当我们需要更改一个对象的行为时,第一个跳入脑海的想法就是使用继承扩展它所属的类。但是,我们不能忽视继承可能引发的几个问题:
- 继承是静态的,无法在运行时更改已有对象的行为,只能使用由不同子类创建的对象来替代当前的整个对象。
- 子类只能有一个父类,大部分编程语言不允许一个类同时继承多个类的行为。
另一种方式是使用组合代替继承,这也是平时开发过程中使用较多的方式。通过新建一个对象,实现原有对象的相同接口,并持有原有对象,从而扩展原有对象的行为。从代码层面看,这是一种 "套娃" 的实现方式。典型的例子就是 Java 中的 I/O 流、MyBatis 中的二级缓存。
Java 的 I/O 流如下:
// 通过继承的方式,扩展了 FileInputStream 的功能
// 真正干活的其实是 FileInputStream
InputStream input = new BufferedInputStream(new FileInputStream("test.txt"));
MyBatis 的二级缓存如下:

二级缓存都实现了 Cache 接口,接口定义如下:
public interface Cache {
// 省略其他方法
/**
* @param key Can be any object but usually it is a {@link CacheKey}
* @param value The result of a select.
*/
void putObject(Object key, Object value);
/**
* @param key The key
*
* @return The object stored in the cache.
*/
Object getObject(Object key);
}
public class BlockingCache implements Cache {
private long timeout;
private final Cache delegate;
private final ConcurrentHashMap<Object, CountDownLatch> locks;
// delegate 就是 SynchronizedCache
public BlockingCache(Cache delegate) {
this.delegate = delegate;
this.locks = new ConcurrentHashMap<>();
}
@Override
public void putObject(Object key, Object value) {
try {
// 使用原始对象存储数据
delegate.putObject(key, value);
} finally {
releaseLock(key);
}
}
@Override
public Object getObject(Object key) {
acquireLock(key);
// 使用原始对象获取数据
Object value = delegate.getObject(key);
if (value != null) {
releaseLock(key);
}
return value;
}
}
public class SynchronizedCache implements Cache {
private final ReentrantLock lock = new ReentrantLock();
private final Cache delegate;
// delegate 就是 LoggingCache
public SynchronizedCache(Cache delegate) {
this.delegate = delegate;
}
@Override
public void putObject(Object key, Object object) {
lock.lock();
try {
delegate.putObject(key, object);
} finally {
lock.unlock();
}
}
@Override
public Object getObject(Object key) {
lock.lock();
try {
return delegate.getObject(key);
} finally {
lock.unlock();
}
}
}
在很多框架的源码中,我们都能看到 "delegate" 的身影,这就是装饰器模式思想的经典应用。