关于 gof-composite
gof-composite 是讲解 Composite 模式的技能,用于表示部分与整体的层次结构。它帮助开发者判断叶节点和分支是否应共享接口,并分析类型安全与递归方面的权衡。
主要功能
- 01叶节点与分支共用 Component 接口
- 02递归委托操作
- 03透明与安全 Composite 的权衡
- 04选择依据与反模式
适用场景
- 01建模文件与目录
- 02构建嵌套菜单或 GUI 组件
- 03判断层次结构是否需要 Composite
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ yonatankarp/ software-design-skills --skill gof-composite安装后在 Claude Code 中输入/gof-composite确认已加载。
详细介绍
一句话概述
将对象组合成树形结构来表示部分与整体的层次关系,让客户端能够统一处理单个对象(叶节点)和对象组合(分支节点)。
何时使用此技能
- 存在部分与整体层次结构:文件系统(文件和目录)、菜单和子菜单、GUI 组件(按钮与组件面板)、组织层级。
- 客户端代码需要对叶节点和分支节点调用相同的操作,无需关心当前处理的是哪一种。
- 存在不应在调用代码中显露的递归结构。
何时不使用此技能
- 只有扁平集合,没有层次结构,直接使用列表即可。
- 叶节点和分支节点的操作确实不同,强行通过同一个接口处理会模糊这种区别。
- 只有一层分组的两级结构——有时一个
List<Item>就足够,无需引入 Composite。
核心内容
该模式引入一个由叶节点和组合对象共享的通用接口(Component)。两者都实现客户端所需的操作;组合对象则通过委托给其子对象来实现这些操作。
Component { operation() }
Leaf { operation() /* does the work */ }
Composite { children: List<Component>
operation() { children.forEach { it.operation() } }
add(c); remove(c); }
关于透明性与安全性的权衡:
- 透明 Composite —
Component声明add(child)。叶节点和组合对象都有该方法。客户端可以统一处理它们,但Leaf.add(child)要么不执行任何操作,要么抛出异常。 - 安全 Composite —
add(child)仅存在于Composite中。类型安全,但客户端添加子对象前必须检查类型。
HFDP 在示例中倾向于使用透明 Composite;在实际应用中,除非确实需要统一处理,否则安全 Composite 通常更简洁。
判断启发
- 当所有操作对叶节点和分支节点都确实有意义时,Composite 才值得采用。
- 如果只有一个操作可以统一应用,而其余操作各不相同,那么这种抽象就是勉强的——考虑是否真的需要 Composite。
- 对于很深或很宽的树,要留意递归操作可能导致栈溢出——必要时使用 Iterator 展平。
反模式
- Composite 中的操作只对叶节点有意义,却在组合对象上不执行任何操作(反之亦然)——这种统一只是表面功夫,并非真正的统一。
- 名为 “Composite” 的结构实际上只是一个列表——没有递归,也没有包含子对象的分支。它只是一个集合。
- Composite 通过
getChildren()直接暴露子对象,并允许调用者在外部修改树结构——这会破坏不变量。
另请参阅
gof-iterator— 遍历 Composite 树的自然方式。gof-decorator— 结构形态相同(两者都会包装对象),但意图不同(Decorator 增加行为;Composite 表示部分与整体)。gof-kotlin-idioms— 密封类层次结构(sealed interface Component { class Leaf; class Branch(val children: List<Component>) })配合穷尽式when,让 Composite 的结构清晰明确且类型安全。
参考资料
- Head First Design Patterns(第 2 版),第 9 章(“良好管理的集合:Iterator 和 Composite 模式”)——该章通过多级菜单示例说明 Composite。
评论
支持 Markdown