什么是装饰器模式?
装饰器模式允许你向一个对象动态地添加新的职责或行为,同时又不改变其结构。它属于结构型模式。
想象一下,你有一杯基础咖啡(组件),你想给它加点奶(装饰器A),再加点糖(装饰器B)。装饰器模式就像是这些调料,你可以根据需要自由组合,给基础咖啡添加不同的风味,而咖啡本身的核心(煮好的咖啡液)并没有改变。
装饰器提供了一种比继承更有弹性的替代方案来扩展功能。
核心思想
装饰器模式的核心在于创建一个“包装器”(Decorator)类,这个包装器类实现了与被包装对象(Component)相同的接口。
- 装饰器内部持有一个指向组件对象的引用(它可以是具体组件,也可以是另一个装饰器)。
- 当客户端调用装饰器的方法时,装饰器可以在将请求委托给被包装的组件对象之前或之后,执行一些额外的操作(这就是添加的职责)。
- 因为装饰器和组件实现了相同的接口,所以装饰器可以透明地替换组件,并且装饰器本身也可以被其他装饰器包装,形成嵌套结构,实现功能的层层叠加。
优点
- 灵活性 (Flexibility): 相比静态的继承,装饰器模式可以在运行时动态地给对象添加或移除职责,更加灵活。避免了继承可能导致的子类爆炸问题(例如,需要一个带日志的缓存数据源、只带日志的数据源、只带缓存的数据源等,用继承会很复杂)。
- 遵循开闭原则 (Open/Closed Principle): 你可以引入新的装饰器类来添加新功能,而无需修改现有的组件类或其他装饰器类。系统对扩展开放,对修改关闭。
- 可组合性 (Composability): 可以通过嵌套组合不同的装饰器,实现复杂功能的叠加。
- 单一职责原则 (Single Responsibility Principle): 每个装饰器类只负责添加一项特定的职责,使得类的功能更加纯粹和内聚。
可能的缺点
- 引入许多小对象: 使用装饰器模式可能会产生许多功能单一的小类,增加系统的复杂性。
- 配置复杂性: 理解和配置多层嵌套的装饰器有时可能比较困难。
使用场景
- 当你希望在不影响其他对象的情况下,以动态、透明的方式向单个对象添加职责时。
- 当你希望扩展一个类的功能,但使用继承不适用或不灵活时(例如,需要多种功能组合,或者类已经被
final修饰)。
- 经典例子: Java 的 I/O 类(如
FileInputStream,BufferedInputStream,DataInputStream),BufferedInputStream就是一个装饰器,它为InputStream添加了缓冲功能。
- Android 中的场景:
- 数据处理流: 包装一个数据源(如 Repository、API Client 或数据库 DAO),为其添加缓存 (
CachingDecorator)、日志记录 (LoggingDecorator)、数据加密/解密 (EncryptionDecorator)、请求重试 (RetryDecorator) 等功能。 - UI 组件增强 (概念上): 虽然 Android 的 View 系统主要依赖继承,但理论上可以构想用装饰器包装一个标准 View(如
TextView),为其添加边框、特殊背景、点击效果等,而无需创建新的子类。(实践中较少见,Android 框架有自己的方式)。 RecyclerView.Adapter包装: 包装现有的Adapter来添加通用功能,如添加 Header/Footer View、实现拖放排序、管理选择状态、空状态视图展示等,而无需修改原始 Adapter 的核心数据绑定逻辑。
UML 图
下面是一个装饰器模式的 UML 结构图,以我们稍后将要实现的“数据源”为例:
- DataSource (Component): 定义了对象的接口,装饰器和具体组件都需要实现它。
- ConcreteDataSource (ConcreteComponent): 实现了
DataSource接口的基础对象,是我们要装饰的目标。
- DataSourceDecorator (Decorator): 抽象装饰器类(或 Kotlin 中的
open类),实现了DataSource接口,并持有一个DataSource类型的引用 (wrappee),指向被包装的对象。它可以提供一个默认的委托实现。
- LoggingDataSourceDecorator, CachingDataSourceDecorator (ConcreteDecorator): 具体的装饰器类,继承自
DataSourceDecorator。它们实现了getData方法,在调用wrappee.getData()的前后添加了额外的功能(日志、缓存)。
代码示例
在这个例子中:
- 我们首先创建了一个基础的
ConcreteDataSource。
- 然后,我们用
LoggingDataSourceDecorator包装了它,得到了一个带日志功能的数据源。
- 接着,我们用
CachingDataSourceDecorator包装了已经带日志功能的数据源,实现了一个既有缓存又有日志(缓存优先)的数据源。注意看嵌套的顺序和输出日志的前缀,可以清晰地看到调用的流程。
- 我们还演示了不同的装饰顺序 (
Logging -> Caching -> Concrete) 会产生不同的行为(日志记录了缓存的操作)。
- 客户端代码始终通过
DataSource接口与对象交互,无需关心它被多少层装饰器包装了。
总结
- 装饰器模式是一种强大的结构型模式,它允许你在不修改原始类或使用继承的情况下,为对象动态地、透明地增加功能。
- 通过组合不同的装饰器,可以灵活地构建出具有多种附加行为的对象。
- 在 Android 开发中,尤其是在处理数据流、网络请求或需要给现有组件添加横切关注点(如日志、缓存、权限检查)时,装饰器模式是非常有用的工具。
记住它的核心:实现相同接口,持有引用,添加行为,委托调用。
- Author:CoderWdd
- URL:https://www.wuinsights.top//article/2a39625d-d02f-4d88-a196-980a1e15342c
- Copyright:All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!
Relate Posts