装饰者模式(Decorator Pattern)学习笔记
1. 模式定义
结构型设计模式,动态地为对象添加额外的职责。通过组合而非继承的方式扩展功能,提供比继承更灵活的替代方案。
2. 适用场景
✅ 需要动态/透明地给对象添加功能
✅ 需要撤销附加功能时
✅ 无法通过继承扩展功能(final类)
✅ 需要组合多个可选功能
✅ 避免"子类爆炸"问题
3. 模式结构
4. 核心角色
角色 | 说明 |
---|---|
Component | 抽象组件,定义被装饰对象的接口 |
ConcreteComponent | 具体组件,实现基本功能 |
Decorator | 装饰者抽象类,持有组件引用并实现组件接口 |
ConcreteDecorator | 具体装饰者,添加额外功能 |
5. 代码示例
5.1 咖啡加料示例
// 抽象组件
public interface Coffee {
double getCost();
String getDescription();
}
// 具体组件
public class SimpleCoffee implements Coffee {
public double getCost() {
return 2.0;
}
public String getDescription() {
return "咖啡";
}
}
// 抽象装饰者
public abstract class CoffeeDecorator implements Coffee {
protected Coffee decoratedCoffee;
public CoffeeDecorator(Coffee coffee) {
this.decoratedCoffee = coffee;
}
public double getCost() {
return decoratedCoffee.getCost();
}
public String getDescription() {
return decoratedCoffee.getDescription();
}
}
// 具体装饰者
public class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) {
super(coffee);
}
public double getCost() {
return super.getCost() + 0.5;
}
public String getDescription() {
return super.getDescription() + " + 牛奶";
}
}
public class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee coffee) {
super(coffee);
}
public double getCost() {
return super.getCost() + 0.2;
}
public String getDescription() {
return super.getDescription() + " + 糖";
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Coffee coffee = new SimpleCoffee();
coffee = new MilkDecorator(coffee);
coffee = new SugarDecorator(coffee);
System.out.println("总价: $" + coffee.getCost());
System.out.println("描述: " + coffee.getDescription());
/* 输出:
总价: $2.7
描述: 咖啡 + 牛奶 + 糖 */
}
}
6. 模式变种
- 透明装饰模式:保持接口完全一致,客户端无需知道装饰存在
- 半透明装饰模式:新增装饰者特有方法,客户端需要知道具体装饰类型
- 组合装饰器:通过链式调用实现多层装饰
Coffee coffee = new SugarDecorator(new MilkDecorator(new SimpleCoffee()));
7. 优缺点分析
✔️ 优点:
- 扩展性比继承更好
- 动态添加/撤销功能
- 避免继承导致的类爆炸
- 符合开闭原则
❌ 缺点:
- 产生大量小对象
- 多层装饰增加调试难度
- 装饰顺序可能影响结果
- 需要管理装饰器组合关系
8. 相关模式对比
模式 | 目的 | 关键区别 |
---|---|---|
适配器模式 | 接口转换 | 改变对象接口 |
代理模式 | 控制访问 | 保持接口相同 |
组合模式 | 树形结构处理 | 装饰者是被组合对象的包装 |
责任链模式 | 请求传递 | 装饰者可以终止处理过程 |
9. 实际应用案例
- Java IO流体系:
new BufferedInputStream(new FileInputStream("file.txt"));
- Spring Security的过滤器链
- Java GUI的组件装饰(Border/Scrollbar)
- Servlet API的HttpServletRequestWrapper
- Spring Web的HandlerInterceptor
- MyBatis的Cache装饰器(FifoCache/LruCache)
10. 最佳实践建议
- 保持装饰接口透明:尽量不添加新方法,保持与组件接口一致
- 控制装饰层级:避免超过3层嵌套装饰
- 优先使用组合:通过组合不同装饰器实现功能叠加
- 注意装饰顺序:明确装饰器的应用顺序规则
- 使用工厂方法:封装复杂装饰组合的创建过程
- 实现撤销机制:提供removeDecorator()方法支持功能撤销
11. 扩展应用(增强HttpServletRequest)
public class LoggingHttpRequest extends HttpServletRequestWrapper {
public LoggingHttpRequest(HttpServletRequest request) {
super(request);
}
@Override
public String getParameter(String name) {
String value = super.getParameter(name);
System.out.println("获取参数: " + name + "=" + value);
return value;
}
@Override
public Part getPart(String name) throws IOException, ServletException {
Part part = super.getPart(name);
System.out.println("上传文件: " + name + " size=" + part.getSize());
return part;
}
}
// 在Filter中使用
public class LoggingFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest wrappedRequest = new LoggingHttpRequest((HttpServletRequest) request);
chain.doFilter(wrappedRequest, response);
}
}
🎁 设计原则体现:
- 开闭原则(OCP):不修改原有类即可扩展新功能
- 组合优于继承(Composite Reuse Principle):使用对象组合代替类继承
- 单一职责原则(SRP):每个装饰者只关注单一功能增强
通过装饰者模式,可以实现灵活的功能扩展,特别适合需要动态组合多种可选功能的场景。该模式在框架设计和基础库开发中应用广泛,是处理功能叠加问题的经典解决方案。