关于 gof-decorator
gof-decorator 是面向开发者的设计模式技能,介绍如何在不修改对象的情况下扩展行为,并说明装饰器的组合、顺序与注意事项。
主要功能
- 01运行时扩展对象行为
- 02保持接口不变的对象包装
- 03可组合的单一职责装饰器
- 04包装顺序指导与注意事项
适用场景
- 01为对象添加日志或缓存
- 02组合可选功能而不创建子类
- 03扩展类似 Java I/O 的对象链
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ yonatankarp/ software-design-skills --skill gof-decorator安装后在 Claude Code 中输入/gof-decorator确认已加载。
详细介绍
一句话总结
将一个对象包装在另一个与其共享接口并增加职责的对象中——可在运行时以任意组合方式实现,且无需修改原对象。
何时使用此技能
- 某个类需要 N 种正交扩展(咖啡可以加牛奶、糖、奶泡、糖浆……任意组合)。使用子类会产生 N! 种变体;使用装饰器则只需 N 个小型包装器。
- Java I/O 流(
BufferedReader(InputStreamReader(FileInputStream(...))))是教科书级的现实案例。 - 在现有对象周围添加横切关注点(日志记录、缓存、验证、速率限制),而不触碰其代码。
何时不使用此技能
- 扩展之间并非正交(必须先加糖再加牛奶等)——装饰器支持无序组合;如果顺序很重要,就存在隐藏耦合。
- 只有一种扩展,而且近期看不到第二种——使用子类或标志更简单。
- 包装改变了接口,而不只是行为——此时应使用
gof-adapter。
核心内容
结构包含两个部分:
- 一个由“核心”对象和每个装饰器共享的通用接口(或抽象类)。
- 每个装饰器都持有一个相同接口的“被包装”实例的引用,并将调用转发给它,可选择在转发前后添加操作。
组合从外向内读取:Foam(Milk(Espresso)) 表示“先是浓缩咖啡,然后加牛奶,再加奶泡”。最外层的装饰器是客户端使用的对象。
“开闭原则”是采用装饰器的经典动机——类应对扩展开放、对修改关闭,而装饰器通过组合而非子类化来扩展功能。
决策启发
- 如果你原本打算在核心类中编写
if (hasMilk) … if (hasSugar) … if (hasFoam) …,装饰器可以将每项关注点拆分到各自的类型中。 - 每个装饰器应只添加一项关注点。如果某个装饰器做了两件事,就将其拆分。
- 包装顺序就是从外向内的执行顺序——应有意选择,并针对顺序进行测试。
反模式
- “装饰器大杂烩”——存在许多关注点重叠的微型装饰器,难以理清实际调用链。
- 改变约定的装饰器(例如在被包装对象不会返回
null的情况下返回null)——会悄无声息地破坏调用方。 - 因为“装饰器不过是一个带有额外功能的子类”而仍然使用继承来添加行为——这就背离了装饰器的初衷。
注意:装饰器不是被包装的类型
装饰器实现的是被包装对象的接口,而不是其具体类。因此,即使装饰器的行为与被包装对象相同,针对被包装具体类型的 is / instanceof 检查仍会返回 false:
val loggedRepo: Repository = LoggingRepository(DefaultRepository())
loggedRepo is Repository // true
loggedRepo is DefaultRepository // false — even though it wraps one
loggedRepo is LoggingRepository // true
在装饰器链中进行运行时类型检查时,这一点常常会让人措手不及。如果你发现自己想要询问“底层对象是否属于 X 类型?”,就说明抽象已经泄漏——要么通过接口处理这条链,要么重新调整结构。
另请参阅
gof-adapter— 当包装改变了接口而不只是行为时使用。gof-proxy— 结构形态相同,意图不同(代理控制访问;装饰器添加行为)。gof-composite— 装饰器与组合模式在结构上相近;两者都会包装对象,也都会递归转发。gof-kotlin-idioms— Kotlin 的by关键字(类委托)能让装饰器只需一行代码。
参考资料
- Head First Design Patterns(第 2 版),第 3 章(“装饰对象:装饰器模式”)——经典的 Starbuzz 咖啡示例和 Java I/O 演示。
- Kotlin Design Patterns and Best Practices(Alexey Soshin),第 3 章——装饰器,包括“装饰器设计模式的注意事项”一节;该节阐述了上文
is检查注意事项的由来。
评论
支持 Markdown