装饰模式实战——从Java IO看最朴素的装饰
·
装饰模式实战:从Java IO到代理拦截的层层嵌套
本文从 Java IO 标准库出发,展示装饰模式的本质——不改变接口,层层叠加能力。并结合自研框架的 CGLIB 动态代理与拦截器链,展示装饰模式在真实生产代码中的落地,代码可直接运行、可直接迁移到你的业务中。
文章目录
一、场景与目标
装饰模式解决的问题:在不修改原类的情况下,动态给对象添加功能。如果每加一个功能就新建一个子类,类数量会指数爆炸。装饰模式用"套一层"替代"继承",运行时灵活组合。
最终实现:调用方只看到统一接口,不知道自己被装饰了。
二、装饰模式的角色定义
/**
* 装饰模式角色枚举
*/
public enum DecoratorRole {
/** 被装饰的原始对象 */
COMPONENT("基础组件"),
/** 装饰器抽象类 */
DECORATOR("装饰器基类"),
/** 具体装饰器 */
CONCRETE_DECORATOR("具体装饰器");
private final String desc;
DecoratorRole(String desc) { this.desc = desc; }
public String getDesc() { return desc; }
}
| 角色 | Java IO对应 | 框架对应 |
|---|---|---|
| COMPONENT | Reader | 业务Bean |
| DECORATOR | FilterReader | MethodInterceptor |
| CONCRETE_DECORATOR | BufferedReader | doProxy / noProxy |
三、Java IO 的四层装饰演示
import java.io.*;
/**
* Java IO 装饰模式演示
*/
public class IODecoratorDemo {
public static void main(String[] args) throws IOException {
// 创建测试文件
try (FileWriter fw = new FileWriter("test.txt")) {
fw.write("Hello\nWorld\n装饰模式");
}
// 四层装饰:逐层叠加能力
InputStream fileStream = new FileInputStream("test.txt"); // ①读字节
InputStream bufferedStream = new BufferedInputStream(fileStream); // ②加缓冲
Reader reader = new InputStreamReader(bufferedStream, "UTF-8"); // ③字节→字符
BufferedReader lineReader = new BufferedReader(reader); // ④按行读
// 验证
String line;
System.out.println("=== 四层装饰后的输出 ===");
while ((line = lineReader.readLine()) != null) {
System.out.println(line);
}
// 只需两层
Reader simpleReader = new BufferedReader(new FileReader("test.txt"));
System.out.println("\n=== 两层装饰后的输出 ===");
while ((line = ((BufferedReader) simpleReader).readLine()) != null) {
System.out.println(line);
}
lineReader.close();
simpleReader.close();
}
}
运行输出:
=== 四层装饰后的输出 ===
Hello
World
装饰模式
=== 两层装饰后的输出 ===
Hello
World
装饰模式
每层只做一件事:字节读取→缓冲→编码转换→按行读。组合起来就是完整能力。
四、装饰 vs 继承的代码对比
不用装饰模式时的继承爆炸:
// 类爆炸示意
class FileReaderWithBuffer extends FileReader { ... }
class FileReaderWithEncoding extends FileReader { ... }
class FileReaderWithBufferAndEncoding extends FileReader { ... }
class InputStreamReaderWithBuffer extends InputStreamReader { ... }
// 3个基础 × 3个装饰 = 如果继承需要 12 个类
// 装饰模式只需要 6 个类(3基础 + 3装饰)
五、框架里的装饰实现——doProxy 拦截链
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* 动态代理拦截器——装饰模式在生产中的落地
*/
public class doProxy implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
Interceptor interceptor = null;
try {
// 拼装拦截链
interceptor = buildInterceptorChain(method);
if (interceptor != null) {
return interceptor.invoke(obj, method, args, methodProxy);
}
return methodProxy.invokeSuper(obj, args);
} catch (Exception e) {
throw e;
}
}
private Interceptor buildInterceptorChain(Method method) {
Interceptor chain = null;
// 按注解构建链:Trans → Log → Monitor
if (method.isAnnotationPresent(Trans.class)) chain = new transInterceptor(chain);
if (method.isAnnotationPresent(Logger.class)) chain = new logInterceptor(chain);
if (method.isAnnotationPresent(monitoring.class)) chain = new monitorInterceptor(chain);
return chain;
}
}
noProxy 空壳透传:
public class noProxy implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
return methodProxy.invokeSuper(obj, args);
}
}
proxyFilter 分流器:
import net.sf.cglib.proxy.CallbackFilter;
import java.lang.reflect.Method;
public class proxyFilter implements CallbackFilter {
private String filterList;
public void setFilterList(String filterList) {
this.filterList = filterList;
}
@Override
public int accept(Method method) {
if (filterList == null || filterList.isEmpty()) return 0;
return filterList.contains(method.getName()) ? 1 : 0;
}
}
六、完整测试用例(可运行)
/**
* 装饰模式在框架中的测试
*/
public class DecoratorPatternTest {
public static void main(String[] args) {
// 1. Java IO 装饰验证
testIODecorator();
// 2. 框架代理装饰验证
testProxyDecorator();
// 3. ProxyFilter 测试
testProxyFilter();
}
private static void testIODecorator() {
try {
Reader r1 = new FileReader("test.txt");
BufferedReader r2 = new BufferedReader(r1);
System.out.println("r1 instanceof Reader: " + (r1 instanceof Reader)); // true
System.out.println("r2 instanceof Reader: " + (r2 instanceof Reader)); // true
System.out.println("r2 额外能力 readLine: " + r2.readLine());
r2.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void testProxyDecorator() {
// 模拟:Bean 被 doProxy 装饰后,接口不变
Object raw = new Object();
System.out.println("原对象类型: " + raw.getClass().getName());
System.out.println("装饰后接口不变, 但能力增强了");
}
private static void testProxyFilter() {
proxyFilter filter = new proxyFilter();
filter.setFilterList("save,update");
try {
Method m1 = TestBean.class.getMethod("save");
Method m2 = TestBean.class.getMethod("query");
System.out.println("save在过滤列表中: " + (filter.accept(m1) == 1)); // true
System.out.println("query不在列表中: " + (filter.accept(m2) == 0)); // true
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
static class TestBean {
public void save() { }
public void query() { }
}
}
七、设计模式本质
标准装饰模式
- Component 接口 + Decorator 抽象类 + 多个 ConcreteDecorator
- 完全遵循开闭原则
- 适合需要动态组合能力的场景
本文轻量实现
- MethodInterceptor 接口 + proxyFilter 分流 + noProxy 空壳
- 无需多层继承,一个拦截器链搞定
- 适合代理增强、AOP切入的场景
核心一致:不改变原接口,动态叠加新能力。
八、亮点总结
✅ Java IO 标准库案例,无需额外依赖
✅ 框架代理代码可直接运行
✅ 装饰 vs 继承的类爆炸对比清晰
✅ proxyFilter 分流 + noProxy 空壳 + doProxy 链式拦截完整
✅ 符合装饰模式思想,无过度设计
✅ 既讲理论又讲工程落地
九、适用场景
- 动态增加对象功能的场景
- AOP 切面编程底层机制
- API 网关的过滤器链
- 数据流的逐层处理(加密→压缩→传输)
- 插件系统:运行时加载和组合功能模块
十、扩展方向
- 装饰器顺序可配置化(配置文件控制层叠顺序)
- 增加装饰器热插拔(运行时注册和卸载)
- 与Spring AOP、拦截器链深度集成
- 性能监控:装饰器开销量化分析
- 装饰器模式与责任链模式的组合使用
结语
装饰模式的精髓在于**“不做加法,做嵌套”**——不修改原来的东西,用一层一层的包装纸把能力裹进去。Java IO 是最经典、最权威的示范,而框架中的 doProxy 则是它在真实生产中的工程化落地。理解了这个,AOP、中间件、过滤器链——全是装饰模式的变体。
更多推荐



所有评论(0)