装饰器模式
问题
什么是装饰器模式?与继承有什么区别?Java 中有哪些典型应用?
答案
核心思想
在不修改原对象的前提下,动态地给对象添加新功能。通过组合(持有原对象引用)而非继承来扩展功能,支持多层装饰叠加。
装饰器 vs 继承
| 维度 | 装饰器模式 | 继承 |
|---|---|---|
| 扩展方式 | 运行时动态组合 | 编译时静态确定 |
| 灵活性 | 可任意组合装饰 | 每种组合需要一个子类 |
| 类数量 | 少(N 个装饰器) | 多(组合爆炸 2^N) |
实现示例
装饰器模式 - 数据流处理
// 组件接口
public interface DataSource {
void writeData(String data);
String readData();
}
// 原始实现
public class FileDataSource implements DataSource {
private final String filename;
public FileDataSource(String filename) { this.filename = filename; }
@Override
public void writeData(String data) { /* 写文件 */ }
@Override
public String readData() { /* 读文件 */ return ""; }
}
// 装饰器基类:持有被装饰对象的引用
public abstract class DataSourceDecorator implements DataSource {
protected final DataSource wrappee;
public DataSourceDecorator(DataSource source) {
this.wrappee = source;
}
@Override
public void writeData(String data) { wrappee.writeData(data); }
@Override
public String readData() { return wrappee.readData(); }
}
// 加密装饰器
public class EncryptionDecorator extends DataSourceDecorator {
public EncryptionDecorator(DataSource source) { super(source); }
@Override
public void writeData(String data) {
super.writeData(encrypt(data)); // 先加密再写
}
@Override
public String readData() {
return decrypt(super.readData()); // 先读再解密
}
}
// 压缩装饰器
public class CompressionDecorator extends DataSourceDecorator {
public CompressionDecorator(DataSource source) { super(source); }
@Override
public void writeData(String data) {
super.writeData(compress(data)); // 先压缩再写
}
}
// 使用:自由组合装饰器
DataSource source = new FileDataSource("data.txt");
source = new EncryptionDecorator(source); // 加密
source = new CompressionDecorator(source); // 压缩
source.writeData("hello"); // 压缩 → 加密 → 写文件
Java IO 中的装饰器
Java IO 是装饰器模式最经典的应用。
Java IO 装饰器链
// FileInputStream → BufferedInputStream → DataInputStream
InputStream in = new DataInputStream(
new BufferedInputStream(
new FileInputStream("data.bin")
)
);
// 每一层都是装饰器,增加一项能力:
// FileInputStream:读文件
// BufferedInputStream:增加缓冲(减少磁盘 IO)
// DataInputStream:增加读取基本类型(readInt、readDouble)
常见面试问题
Q1: 装饰器模式和代理模式的区别?
答案:
| 维度 | 装饰器模式 | 代理模式 |
|---|---|---|
| 目的 | 增强功能(加缓冲、加加密) | 控制访问(权限、缓存、延迟加载) |
| 叠加 | 多层装饰器可以叠加 | 通常只有一层代理 |
| 关注点 | 增加新行为 | 管理对目标对象的访问 |
Q2: Java IO 为什么用装饰器而不是继承?
答案:
Java IO 有 4 种基础流(File、ByteArray、Piped、Socket)和多种增强功能(缓冲、数据类型、压缩、加密)。如果用继承,需要 4 × N 个子类(BufferedFileInputStream、BufferedSocketInputStream...),组合爆炸。
装饰器模式下,基础流和增强功能独立开发,通过组合自由搭配。
Q3: Spring 中的装饰器模式?
答案:
HttpServletRequestWrapper:装饰 HttpServletRequest,自定义请求处理TransactionAwareCacheDecorator:给缓存添加事务感知能力BeanDefinitionDecorator:装饰 BeanDefinition