mcpField
G
gof-decorator

gof-decorator

github.com/yonatankarp开发143 次安装23 周前更新
代码仓库

关于 gof-decorator

gof-decorator 是面向开发者的设计模式技能,介绍如何在不修改对象的情况下扩展行为,并说明装饰器的组合、顺序与注意事项。

主要功能

  1. 01运行时扩展对象行为
  2. 02保持接口不变的对象包装
  3. 03可组合的单一职责装饰器
  4. 04包装顺序指导与注意事项

适用场景

  1. 01为对象添加日志或缓存
  2. 02组合可选功能而不创建子类
  3. 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