当前位置: 首页 > ds >正文

装饰模式:动态扩展对象功能的优雅设计

装饰模式:动态扩展对象功能的优雅设计

一、模式核心:不修改原类,动态添加功能

在软件开发中,我们经常需要为现有对象添加新功能(如给 “文本框” 添加 “验证功能”“日志功能”),但直接修改原类会违反开闭原则。装饰模式(Decorator Pattern) 通过创建一个包装对象(装饰器),在不改变原对象的前提下动态扩展其功能,核心解决:

  • 功能动态组合:可灵活选择不同装饰器组合,实现功能的 “即插即用”
  • 避免继承膨胀:替代多层继承,减少子类数量(如 “带验证的文本框” 无需继承原文本框类)

核心思想与 UML 类图

装饰模式通过让装饰器与被装饰对象实现相同接口,形成递归包装结构,使客户端透明地使用装饰后的对象:

PlantUML Diagram

二、核心实现:为文件流添加加密与压缩功能

1. 定义组件接口(抽象功能)

public interface DataStream {void write(String data); // 写入数据
}

2. 实现具体组件(基础功能:普通文件流)

public class FileStream implements DataStream {private String filePath;public FileStream(String filePath) {this.filePath = filePath;}@Overridepublic void write(String data) {System.out.println("写入文件 [" + filePath + "]: " + data);}
}

3. 定义装饰器抽象类(维护被装饰对象引用)

public abstract class StreamDecorator implements DataStream {protected DataStream dataStream;public StreamDecorator(DataStream dataStream) {this.dataStream = dataStream;}@Overridepublic void write(String data) {dataStream.write(data); // 委托给被装饰对象}
}

4. 实现具体装饰器(新增加密功能)

public class EncryptionDecorator extends StreamDecorator {public EncryptionDecorator(DataStream dataStream) {super(dataStream);}// 加密算法(示例:简单反转字符串)private String encrypt(String data) {return new StringBuilder(data).reverse().toString();}@Overridepublic void write(String data) {String encryptedData = encrypt(data); // 前置加密处理super.write(encryptedData); // 调用被装饰对象的写入逻辑}
}

5. 实现另一个装饰器(新增压缩功能)

public class CompressionDecorator extends StreamDecorator {public CompressionDecorator(DataStream dataStream) {super(dataStream);}// 压缩算法(示例:简单字符串截断)private String compress(String data) {return data.length() > 10 ? data.substring(0, 10) + "..." : data;}@Overridepublic void write(String data) {String compressedData = compress(data); // 前置压缩处理super.write(compressedData);}
}

6. 客户端组合装饰器(动态扩展功能)

public class ClientDemo {public static void main(String[] args) {// 基础功能:普通文件流DataStream simpleStream = new FileStream("data.txt");simpleStream.write("Hello, World!"); // 直接写入原始数据// 组合加密装饰器DataStream encryptedStream = new EncryptionDecorator(simpleStream);encryptedStream.write("Secret Message"); // 写入前自动加密// 组合加密+压缩装饰器(顺序影响结果)DataStream complexStream = new CompressionDecorator(new EncryptionDecorator(simpleStream));complexStream.write("LongTextThatNeedsCompressionAndEncryption");}
}

三、进阶:实现 JDK IO 式的多层装饰器链

1. 支持链式调用的装饰器设计

public abstract class ChainableDecorator implements DataStream {protected DataStream dataStream;public ChainableDecorator(DataStream dataStream) {this.dataStream = dataStream;}// 支持连续装饰(返回当前装饰器实例)public DataStream decorate(DataStream decorator) {return new ChainableDecorator(decorator) {@Overridepublic void write(String data) {// 可自定义装饰顺序(前置/后置)data = "[" + data + "]"; // 新增包装逻辑super.write(data);}};}
}// 使用示例:链式装饰
DataStream stream = new FileStream("log.txt");
stream = new EncryptionDecorator(stream).decorate(new CompressionDecorator(stream));

2. 装饰器与 AOP 的结合(方法级增强)

// 使用Spring AOP实现装饰器逻辑
@Aspect
public class StreamAspect {@Around("execution(* com.example.DataStream.write(..))")public Object addDecorator(ProceedingJoinPoint joinPoint) throws Throwable {String data = (String) joinPoint.getArgs()[0];// 前置增强:加密String encryptedData = encrypt(data);// 调用原始方法joinPoint.proceed(new Object[]{encryptedData});// 后置增强:记录日志logWrite(encryptedData);return null;}
}

3. 可视化装饰流程(Mermaid 流程图)

客户端请求
装饰器A处理
被装饰对象处理
装饰器B处理
返回结果

四、框架与源码中的装饰模式实践

1. Java IO 流(典型装饰模式应用)

  • 核心类:

    • 抽象组件:InputStream/OutputStream
    • 具体组件:FileInputStream/FileOutputStream
    • 装饰器:BufferedInputStream(添加缓冲)、DataInputStream(添加数据类型支持)
  • 使用示例:

    InputStream input = new BufferedInputStream(new DataInputStream(new FileInputStream("file.txt"))
    );
    

2. Servlet Filter(请求响应装饰)

  • Filter通过FilterChain形成装饰链,对请求和响应进行增强
public class EncodingFilter implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {request = new EncodedServletRequest((HttpServletRequest) request); // 装饰请求chain.doFilter(request, response); // 传递给下一个Filter}
}

3. MyBatis 插件(Interceptor)

  • 通过拦截器装饰ExecutorStatementHandler等对象,添加分页、性能监控等功能
@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class PerformanceInterceptor implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {long start = System.currentTimeMillis();Object result = invocation.proceed(); // 调用原始方法long end = System.currentTimeMillis();logPerformance(end - start); // 后置装饰:记录执行时间return result;}
}

五、避坑指南:正确使用装饰模式的 3 个要点

1. 确保装饰器与组件接口一致

  • ❌ 反模式:装饰器未实现与组件相同的接口,导致客户端无法透明调用
  • ✅ 最佳实践:装饰器必须继承 / 实现组件接口,并重写所有方法

2. 控制装饰链长度(避免性能损耗)

  • 过长的装饰链会增加方法调用层级,建议:
    • 对装饰器进行分组(如 “输入处理组”“输出处理组”)
    • 使用CompositeDecorator合并同类装饰器

3. 避免装饰顺序影响逻辑

  • 装饰器的执行顺序可能影响结果(如先加密后压缩 vs 先压缩后加密),需:
    • 在文档中明确装饰器顺序规范
    • 提供工厂类统一管理装饰链组装

4. 反模式:滥用装饰器替代继承

  • 当功能扩展是 “is-a” 关系时(如 “鸟类” 是 “动物类”),应使用继承而非装饰器

六、总结:何时该用装饰模式?

适用场景核心特征典型案例
动态添加功能功能可灵活组合,且运行时动态决定GUI 组件皮肤切换、游戏角色技能升级
避免类爆炸功能组合可能导致子类数量激增(如 n 个功能→2ⁿ个子类)日志系统(DEBUG/INFO/ERROR 级别组合)
遵循开闭原则不修改原代码,通过新增装饰器扩展功能框架插件机制、遗留系统功能增强

装饰模式通过 “包装对象 + 功能委托” 的设计,使系统在不破坏原有结构的前提下实现功能的动态扩展,是应对 “需求变化” 的核心设计模式之一。下一篇我们将深入探讨适配器模式,解析如何让不兼容的接口协同工作,敬请期待!

扩展思考:装饰模式 vs 代理模式

两者都通过包装对象实现功能增强,但核心差异在于:

模式目标持有对象关系典型场景
装饰模式增强功能(附加职责)装饰器与组件是 “is-a” 关系添加日志、加密、压缩
代理模式控制访问(替代职责)代理与目标是 “proxy-for” 关系远程调用、权限控制、懒加载

理解这种差异,能帮助我们在设计时选择更合适的模式来解决实际问题。

http://www.xdnf.cn/news/1027.html

相关文章:

  • AI Agent开发第35课-揭秘RAG系统的致命漏洞与防御策略
  • 极刻AI搜v1.0 问一次问题 AI工具一起答
  • 城市客运安全员证适用岗位及要求
  • QtCreator的设计器、预览功能能看到程序图标,编译运行后图标消失
  • 关于金碟云星空批号问题
  • 自动化测试
  • Psychology 101 期末测验(附答案)
  • Ubuntu 系统下安装和使用性能分析工具 perf
  • HarmonyOS-ArkUI: animateTo 显式动画
  • Git SSH 密钥多个 Git 来源
  • 承兑汇票文字录入解决方案-承兑汇票识别接口-C++集成方式
  • SQL优化
  • 安卓逆向工程:从APK到内核的层级技术解析
  • 聚客AI万字解密AI-Agent大模型智能体:从架构设计到工业落地的全栈指南
  • 算法题(130):激光炸弹
  • 力扣刷题Day 23:最长连续序列(128)
  • Azkaban集群搭建
  • 基于Python的图片/签名转CAD小工具开发方案
  • 13.电阻在EMC设计中的妙用
  • 黑苹果win10和macOS双系统
  • C++ 的史诗级进化:从C++98到C++20
  • MySQL 触发器
  • 三轴云台之激光测距技术篇
  • 软件工程师中级考试-上午知识点总结(上)
  • 小公司面经,当练手了
  • WPS科大讯飞定制版 11.4.1.5| 无广告,省电和降低占用,可与普通版本共存
  • [SpringBoot]配置文件
  • C++ STL:从零开始模拟实现 list 容器
  • 当前中国超融合市场的竞争格局以及针对不同需求场景的超融合产品推荐
  • OpenFeign 使用教程:从入门到实践