装饰器模式
装饰器模式用于在不修改原对象代码的情况下,动态给对象增加功能。
一句话理解:接口不变,外面包一层,能力增强。
解决什么问题
如果要给一个对象不断增加功能,直接继承会导致类爆炸。
例如输入流可能需要:
- 缓冲能力。
- 解压能力。
- 解密能力。
- 统计读取字节数。
这些能力可以任意组合,如果都用继承,会产生大量组合类。装饰器模式通过“包装对象”解决这个问题。
基本结构
text
Component
^
|
ConcreteComponent
^
|
Decorator -> Component
^
|
ConcreteDecorator角色:
Component:统一接口。ConcreteComponent:原始对象。Decorator:装饰器基类,持有一个Component。ConcreteDecorator:具体增强逻辑。
Java 示例
基础接口:
java
interface Notifier {
void send(String message);
}原始对象:
java
class EmailNotifier implements Notifier {
public void send(String message) {
System.out.println("email: " + message);
}
}装饰器基类:
java
abstract class NotifierDecorator implements Notifier {
protected final Notifier target;
protected NotifierDecorator(Notifier target) {
this.target = target;
}
}短信增强:
java
class SmsNotifierDecorator extends NotifierDecorator {
SmsNotifierDecorator(Notifier target) {
super(target);
}
public void send(String message) {
target.send(message);
System.out.println("sms: " + message);
}
}日志增强:
java
class LogNotifierDecorator extends NotifierDecorator {
LogNotifierDecorator(Notifier target) {
super(target);
}
public void send(String message) {
System.out.println("before send log");
target.send(message);
System.out.println("after send log");
}
}使用:
java
Notifier notifier = new LogNotifierDecorator(
new SmsNotifierDecorator(
new EmailNotifier()
)
);
notifier.send("server is down");JDK 中的例子
Java IO 是典型装饰器:
java
InputStream inputStream = new BufferedInputStream(
new FileInputStream("app.log")
);FileInputStream 负责读取文件,BufferedInputStream 增加缓冲能力,它们都实现 InputStream。
使用场景
- 给对象动态增加功能。
- 功能可以叠加组合。
- 不想修改原类。
- 继承会导致组合类太多。
- 输入输出流、过滤器、请求包装等场景。
优点
- 比继承更灵活。
- 可以运行时组合多个增强。
- 符合开闭原则。
- 原对象和增强逻辑分离。
缺点
- 包装层太多时调试不直观。
- 对象链过长会增加理解成本。
- 多个装饰器顺序不同,结果可能不同。
和代理模式的区别
| 模式 | 重点 |
|---|---|
| 装饰器 | 增强对象能力,多个增强可叠加 |
| 代理 | 控制对象访问,常用于权限、懒加载、远程调用 |
两者结构很像,但设计意图不同。
实战注意
装饰器最好保持和原对象相同的接口。调用方应该不知道自己拿到的是原对象还是装饰后的对象。