装饰器模式(Decorator Pattern):动态扩展对象功能的灵活方式
装饰器模式是结构型设计模式的一种,核心思想是动态地给对象添加额外职责或功能,而无需修改原对象的结构。它通过 “包装”(组合)而非 “继承” 实现扩展,比生成子类更灵活,本质是 “动态组合”。
装饰器模式的核心结构
装饰器模式通过四个角色实现功能的动态扩展,层次清晰且职责明确:
组件接口(Component)
- 定义对象的核心功能接口,是装饰器和具体组件的共同父类。
- 示例:
InputStream(Java IO 中的输入流接口,定义读取数据的核心方法)。
具体组件(ConcreteComponent)
- 实现组件接口,是被装饰的原始对象,提供基础功能。
- 示例:
FileInputStream(具体的文件输入流,实现基础的文件读取)。
装饰器抽象类(Decorator)
- 实现组件接口,并持有一个组件对象的引用(通过构造方法注入),定义所有装饰器的统一接口。
- 示例:
FilterInputStream(Java IO 中的装饰器抽象类,持有InputStream引用)。
具体装饰器(ConcreteDecorator)
- 继承装饰器抽象类,添加额外功能,在调用被装饰对象的方法前后插入新逻辑。
- 示例:
BufferedInputStream(为输入流添加缓冲功能)、DataInputStream(添加数据类型转换功能)。
代码实现示例
以 “咖啡订单系统” 为例:基础咖啡(如美式咖啡)可通过装饰器动态添加牛奶、糖等配料,实现功能扩展。
1. 组件接口与具体组件
1 | // 1. 组件接口(咖啡) |
2. 装饰器抽象类与具体装饰器
1 | // 3. 装饰器抽象类(咖啡配料装饰器) |
3. 客户端使用
1 | public class DecoratorDemo { |
经典应用:Java IO 中的装饰器模式
Java IO 框架广泛使用装饰器模式,通过不同的装饰器组合实现多样的输入输出功能:
1 | // 具体组件:文件输入流(基础功能) |
- 组件接口:
InputStream(定义read()等核心方法)。 - 具体组件:
FileInputStream、ByteArrayInputStream(基础输入流)。 - 装饰器抽象类:
FilterInputStream(持有InputStream引用)。 - 具体装饰器:
BufferedInputStream(缓冲)、DataInputStream(数据转换)、CheckedInputStream(校验和)等。
装饰器模式的核心优势
- 动态扩展功能
可在运行时为对象添加或移除功能(如咖啡可随时加 / 不加牛奶),无需修改原始类,符合开闭原则。 - 灵活组合功能
通过多个装饰器的嵌套组合,实现复杂功能(如 “缓冲 + 数据转换 + 校验” 的输入流),组合方式远超继承的灵活性。 - 避免类爆炸
若用继承实现功能扩展(如MilkAmericano、SugarAmericano、MilkSugarAmericano),会导致类数量急剧增加,而装饰器模式通过组合解决这一问题。 - 单一职责原则
每个装饰器仅负责添加一种功能(如MilkDecorator只处理牛奶),职责清晰,便于维护和复用。
优缺点分析
优点
- 扩展性强:无需修改原有代码,通过新增装饰器即可扩展功能。
- 灵活性高:可任意组合装饰器,实现不同功能组合。
- 低耦合:具体组件与装饰器独立变化,互不影响。
缺点
- 对象层次复杂:多层装饰会导致对象嵌套层次深(如
A(B(C(original)))),调试和理解难度增加。 - 过度设计风险:简单扩展场景使用装饰器可能显得繁琐(如仅需添加一个固定功能,继承可能更简单)。
适用场景
- 需要动态扩展对象功能
如 IO 流的缓冲、加密、压缩等功能,需根据场景灵活组合。 - 避免使用继承扩展
当功能组合过多(如 10 种配料的咖啡),继承会导致类爆炸,装饰器是更好选择。 - 扩展不可继承的类
若被扩展的类是final(不可继承),只能通过装饰器实现扩展。 - 功能需随时开启 / 关闭
如日志系统可动态开启 / 关闭 “时间戳”“线程名” 等附加信息。
总结
装饰器模式通过 “动态组合” 实现对象功能的灵活扩展,核心是 “包装原有对象,添加新逻辑”。它弥补了继承的静态性缺陷,尤其适合需要多种功能组合的场景(如 Java IO)。使用时需注意控制装饰层次,避免过度复杂

v1.3.10