装饰器模式
装饰器模式用于在不修改原对象的情况下,动态增强对象能力。它通过“包装原对象”的方式,在调用前后增加额外逻辑。
为什么需要装饰器模式
假设一个文件读取器本来只负责读取内容,现在希望增加日志、缓冲、加密、统计耗时。如果直接改原类,会让原类越来越复杂。
mermaid
flowchart TD
A["原始读取器"] --> B["读取文件"]
A --> C["新增日志"]
A --> D["新增缓冲"]
A --> E["新增加密"]
A --> F["统计耗时"]问题:
- 原类职责变多。
- 不同增强组合很难复用。
- 新增强能力要修改老代码。
装饰器把增强逻辑拆成一层层包装:
mermaid
flowchart TD
A["调用方"] --> B["日志装饰器"]
B --> C["缓冲装饰器"]
C --> D["文件读取器"]基本结构
| 角色 | 作用 |
|---|---|
| Component | 原对象和装饰器共同实现的接口 |
| ConcreteComponent | 原始对象 |
| Decorator | 持有 Component 引用 |
| ConcreteDecorator | 在调用前后增加功能 |
Java IO 示例
Java IO 大量使用装饰器模式。
java
InputStream input = new BufferedInputStream(
new FileInputStream("demo.txt")
);FileInputStream 负责读取文件,BufferedInputStream 增加缓冲能力。调用方看到的仍然是 InputStream。
执行流程:
mermaid
flowchart TD
A["调用 read"] --> B["BufferedInputStream"]
B --> C{"缓冲区是否有数据"}
C -- "有" --> D["直接返回"]
C -- "没有" --> E["调用 FileInputStream"]
E --> F["读取文件并填充缓冲区"]
F --> D代码 Demo
公共接口:
java
public interface DataSource {
String read();
}原始对象:
java
public class FileDataSource implements DataSource {
@Override
public String read() {
return "file data";
}
}抽象装饰器:
java
public abstract class DataSourceDecorator implements DataSource {
protected final DataSource target;
protected DataSourceDecorator(DataSource target) {
this.target = target;
}
}日志装饰器:
java
public class LogDataSourceDecorator extends DataSourceDecorator {
public LogDataSourceDecorator(DataSource target) {
super(target);
}
@Override
public String read() {
System.out.println("read start");
String value = target.read();
System.out.println("read end");
return value;
}
}使用:
java
DataSource dataSource = new LogDataSourceDecorator(new FileDataSource());
String value = dataSource.read();和代理模式区别
| 对比项 | 装饰器模式 | 代理模式 |
|---|---|---|
| 目的 | 动态叠加功能 | 控制访问或增强调用 |
| 关注点 | 功能组合 | 访问控制、远程代理、延迟加载、AOP |
| 是否强调多层叠加 | 是 | 不一定 |
| 典型例子 | Java IO | Spring AOP、动态代理 |
结构上二者都可能“包一层”,但意图不同。装饰器强调增强能力可组合,代理强调控制访问。
滥用风险
| 风险 | 后果 |
|---|---|
| 装饰层太多 | 调用链很深,调试困难 |
| 装饰器改变原语义 | 调用方预期被破坏 |
| 构造顺序不清楚 | 不同包装顺序结果不同 |
| 简单增强也手写装饰器 | 不如 AOP 或普通方法直观 |
小结
装饰器模式适合在不修改原类的情况下,为对象动态叠加能力。它的核心是“原对象和装饰器实现同一个接口”,调用方不需要知道自己拿到的是原始对象还是被增强后的对象。
