装饰器模式是一种极具弹性的结构型设计模式,它允许我们通过组合的方式动态扩展对象功能而无需修改原有结构。本文将通过JDK源码中的实际应用和通俗易懂的代码示例,带你深入了解这一强大模式的精髓。
装饰器模式核心原理
装饰器模式的核心思想:在原有对象外面"包装"一层新功能,同时保持与被装饰对象相同的接口。它能够:
- 在不改变对象的前提下增强功能
- 避免因过度继承导致类爆炸
- 支持运行时动态添加功能
- 组合替代继承提高灵活性
Java IO包中的装饰器模式实战
让我们深入JDK源码(Java 17),看看java.io
包如何完美应用装饰器模式:
java
import java.io.*;
public class DecoratorInJavaIO {
public static void main(String[] args) throws IOException {
// 基础数据类型装饰
DataInputStream dataInput = new DataInputStream(
new BufferedInputStream(
new FileInputStream("data.bin")
)
);
// 字符编码转换装饰
BufferedReader reader = new BufferedReader(
new InputStreamReader(
new FileInputStream("text.txt"), "UTF-8"
)
);
// 动态添加行号功能
LineNumberReader lineReader = new LineNumberReader(reader);
// 动态添加大小写转换装饰器
UpperCaseReader upperReader = new UpperCaseReader(lineReader);
String line;
while ((line = upperReader.readLine()) != null) {
int num = upperReader.getLineNumber();
System.out.println("Line " + num + ": " + line);
}
}
}
// 自定义装饰器:将内容转为大写
class UpperCaseReader extends FilterReader {
protected UpperCaseReader(Reader in) {
super(in);
}
@Override
public int read() throws IOException {
int c = super.read();
return (c == -1) ? c : Character.toUpperCase(c);
}
@Override
public int read(char[] cbuf, int off, int len) throws IOException {
int n = super.read(cbuf, off, len);
for (int i = off; i < off + n; i++) {
cbuf[i] = Character.toUpperCase(cbuf[i]);
}
return n;
}
// 增强功能:提供读取整行的方法
public String readLine() throws IOException {
char[] buffer = new char[1024];
int pos = 0;
int c;
while ((c = read()) != -1) {
if (c == '\n') break;
buffer[pos++] = (char)c;
}
if (pos == 0 && c == -1) return null;
return new String(buffer, 0, pos);
}
}
在上述代码中:
- 我们使用Java IO的核心装饰器(
BufferedInputStream
,InputStreamReader
) - 展示了装饰器链式组合的强大功能
- 创建了自定义的装饰器
UpperCaseReader
来扩展原有功能
装饰器模式结构解析
下面使用Mermaid工具展示装饰器模式的类图结构:
持有引用 Component +operation() : void ConcreteComponent +operation() : void Decorator -component: Component +Decorator(Component) +operation() : void ConcreteDecoratorA +operation() : void +addedBehavior() : void ConcreteDecoratorB +addedState: String +operation() : void
图中关键角色:
- Component: 被装饰对象的公共接口(如Java的InputStream)
- ConcreteComponent: 基础实现(如FileInputStream)
- Decorator: 装饰器抽象层(如FilterInputStream)
- ConcreteDecorator: 具体装饰器实现(如BufferedInputStream)
JDK中装饰器模式实现原理
分析java.io.FilterInputStream
源码:
java
public class FilterInputStream extends InputStream {
protected volatile InputStream in;
protected FilterInputStream(InputStream in) {
this.in = in;
}
public int read() throws IOException {
return in.read();
}
// 所有方法都委托给in对象
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
public int read(byte[] b, int off, int len) throws IOException {
return in.read(b, off, len);
}
// 其他方法...
}
在JDK实现中:
- 所有具体装饰器都继承自FilterInputStream
- 每个装饰器持有底层InputStream的引用
- 基础方法直接委托给底层流
- 需要增强的方法被重写(如BufferedInputStream缓冲功能)
装饰器模式 vs 继承
特点 | 装饰器模式 | 继承 |
---|---|---|
扩展方式 | 运行时 | 编译时 |
组合方式 | 对象组合 | 类继承 |
灵活性 | 高(动态组合) | 低(静态绑定) |
功能叠加 | 线性添加 | 只能单一路径 |
修改风险 | 无(不修改原类) | 需要修改类层次 |
装饰器模式的典型应用场景
- 输入/输出流处理:Java IO/NIO中的流装饰
- Servlet API:HttpServletRequestWrapper装饰请求
- Collections工具类:unmodifiableXXX创建不可变视图
- JavaFX应用:Node对象的多种样式装饰
java
// Java集合框架中的装饰器应用
List<String> origin = new ArrayList<>();
List<String> safeList = Collections.checkedList(origin, String.class);
List<String> unmodifiable = Collections.unmodifiableList(origin);
装饰器模式的优点与局限
核心优势:
- 符合开闭原则:扩展不修改
- 职责明确:小类单一职责
- 动态组合:运行时装配功能
- 避免类爆炸:取代多层继承结构
潜在缺点:
- 过度使用导致结构复杂
- 调试困难(调用链路深)
- 小对象数量可能增加
总结与最佳实践
装饰器模式在Java核心库特别是IO系统中发挥了至关重要的作用。它通过优雅的包装机制,实现了功能的动态组合,避免了传统继承的固有问题。
使用建议:
- 当需要动态、透明地添加职责时
- 当不适合使用子类扩展时
- 当目标可能有多种不同组合时
- 当需要保持被装饰对象的接口纯净时
掌握装饰器模式将使你的设计更具弹性,帮助创建更灵活、可扩展的系统架构。同时也要注意避免过度装饰导致的复杂性,在恰当的场景发挥其最大价值。
设计思想的精髓: 组合优于继承,封闭修改打开扩展,通过对象包装而非类继承来实现功能增强!