什么是装饰器模式?

装饰器模式允许你向一个对象动态地添加新的职责或行为,同时又不改变其结构。它属于结构型模式。
想象一下,你有一杯基础咖啡(组件),你想给它加点奶(装饰器A),再加点糖(装饰器B)。装饰器模式就像是这些调料,你可以根据需要自由组合,给基础咖啡添加不同的风味,而咖啡本身的核心(煮好的咖啡液)并没有改变。
装饰器提供了一种比继承更有弹性的替代方案来扩展功能。

核心思想

装饰器模式的核心在于创建一个“包装器”(Decorator)类,这个包装器类实现了与被包装对象(Component)相同的接口。
  1. 装饰器内部持有一个指向组件对象的引用(它可以是具体组件,也可以是另一个装饰器)。
  1. 当客户端调用装饰器的方法时,装饰器可以在将请求委托给被包装的组件对象之前或之后,执行一些额外的操作(这就是添加的职责)。
  1. 因为装饰器和组件实现了相同的接口,所以装饰器可以透明地替换组件,并且装饰器本身也可以被其他装饰器包装,形成嵌套结构,实现功能的层层叠加。

优点

  1. 灵活性 (Flexibility): 相比静态的继承,装饰器模式可以在运行时动态地给对象添加或移除职责,更加灵活。避免了继承可能导致的子类爆炸问题(例如,需要一个带日志的缓存数据源、只带日志的数据源、只带缓存的数据源等,用继承会很复杂)。
  1. 遵循开闭原则 (Open/Closed Principle): 你可以引入新的装饰器类来添加新功能,而无需修改现有的组件类或其他装饰器类。系统对扩展开放,对修改关闭。
  1. 可组合性 (Composability): 可以通过嵌套组合不同的装饰器,实现复杂功能的叠加。
  1. 单一职责原则 (Single Responsibility Principle): 每个装饰器类只负责添加一项特定的职责,使得类的功能更加纯粹和内聚。

可能的缺点

  • 引入许多小对象: 使用装饰器模式可能会产生许多功能单一的小类,增加系统的复杂性。
  • 配置复杂性: 理解和配置多层嵌套的装饰器有时可能比较困难。

使用场景

  1. 当你希望在不影响其他对象的情况下,以动态、透明的方式向单个对象添加职责时。
  1. 当你希望扩展一个类的功能,但使用继承不适用或不灵活时(例如,需要多种功能组合,或者类已经被 final 修饰)。
  1. 经典例子: Java 的 I/O 类(如 FileInputStream, BufferedInputStream, DataInputStream),BufferedInputStream 就是一个装饰器,它为 InputStream 添加了缓冲功能。
  1. 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 开发中,尤其是在处理数据流、网络请求或需要给现有组件添加横切关注点(如日志、缓存、权限检查)时,装饰器模式是非常有用的工具。
💡
记住它的核心:实现相同接口,持有引用,添加行为,委托调用。
 
设计模式——行为型-观察者模式设计模式——创建型-抽象工厂模式