问题
在实际生产中,新需求在软件的整个生命过程中是不断出现的。当有新需求出现时,就需要为某个组件添加新的功能来满足这些需求。添加新功能的方式有很多。
1、我们可以直接修改已有的组件的代码添加相应的新功能,这显然破坏了已有组件的稳定性。修改完之后,整个组件需要重新进行测试,才能上线使用。违反了“开放-封闭”原则。
2、使用继承方式,子类实现新功能实现扩展。这种方式是静态的,用户不能控制增加行为的方式和时机。而且有些情况下继承是不可行的,例如final修饰的类。另外,若新功能存在多种组合,使用继承方式,会导致大量子类产生。
装饰器能帮助解决上述问题。
装饰器模式(Decorator Pattern)
概念
装饰器模式 允许向一个现有的对象添加新的功能,同时又不改变其结构。装饰者可以在所委托被装饰者的行为之前或之后加上自己的行为,以达到特定的目的。
装饰器可以动态地为对象添加功能,他是基于组合的方式实现功能的。在实践中,我们应该尽量使用组合的方式来扩展系统功能,而非使用继承方式。
组成
装饰器模式由组件和装饰者组成。
抽象组件(Component):需要装饰的抽象对象。
具体组件(ConcreteComponent):是我们需要装饰的对象
抽象装饰类(Decorator):内含指向抽象组件的引用及装饰者共有的方法。
具体装饰类(ConcreteDecorator):被装饰的对象。
适用场景:
- 扩展一个类的功能。
- 动态增加功能,动态撤销。
优缺点:
优点:
- 装饰类和被装饰类可以独立发展,不会相互耦合
- 动态的将责任附加到对象身上。
缺点:
- 需求越来越多时,可能会创建多层装饰,增加了系统的复杂性。
代码实战:
参看mybatis-cache源码包
package org.apache.ibatis.cache;
import java.util.concurrent.locks.ReadWriteLock;
public interface Cache {
String getId();
int getSize();
void putObject(Object key, Object value);
Object getObject(Object key);
Object removeObject(Object key);
void clear();
ReadWriteLock getReadWriteLock();
}
抽象组件(Component):需要装饰的抽象对象。
其下有多个实现,多数为其装饰器。而具体的实现是PerpetualCache,类似具体组件(ConcreteComponent):是我们需要装饰的对象。
装饰器只列举其中一个实现类做说明,例如FifoCache,存储了被装饰的目标对象。
public class FifoCache implements Cache {
private final Cache delegate; //被装饰的cache对象
private final LinkedList<Object> keyList; //用于记录key进入缓存的先后顺序
private int size; //缓存上限
public FifoCache(Cache delegate) {
this.delegate = delegate;
this.keyList = new LinkedList<Object>();
this.size = 1024;
}
public String getId() {
return delegate.getId();
}
public int getSize() {
return delegate.getSize();
}
public void setSize(int size) {
this.size = size;
}
public void putObject(Object key, Object value) {
cycleKeyList(key);
delegate.putObject(key, value);
}
public Object getObject(Object key) {
return delegate.getObject(key);
}
public Object removeObject(Object key) {
return delegate.removeObject(key);
}
public void clear() {
delegate.clear();
keyList.clear();
}
public ReadWriteLock getReadWriteLock() {
return delegate.getReadWriteLock();
}
private void cycleKeyList(Object key) {
keyList.addLast(key);
if (keyList.size() > size) {
Object oldestKey = keyList.removeFirst();
delegate.removeObject(oldestKey);
}
}
}
其他实现装饰类自行了解。LRU、软引用、弱引用等常用cache方式。可结合guava缓存原理、实现独立学习一下。
参考:
Head First 设计模式
mybatis源码