关于 gof-state
讲解 GoF State 模式的技能,适用于状态会改变对象行为的场景。它介绍上下文、状态接口与具体状态,并帮助识别状态分支过多及非法转换等问题。
主要功能
- 01将状态行为封装在独立类中
- 02由上下文委托当前状态的操作
- 03描述状态转换与状态图
- 04比较 State 与 Strategy
- 05处理非法状态转换
适用场景
- 01状态枚举对应大型条件分支
- 02订单或连接具有明确生命周期状态
- 03状态转换受规则限制且集合固定
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ yonatankarp/ software-design-skills --skill gof-state安装后在 Claude Code 中输入/gof-state确认已加载。
详细介绍
一句话概述
当对象的内部状态发生变化时,允许其改变行为——使对象看起来像是改变了自己的类。每种状态都封装在自己的类中;上下文将行为委托给它当前持有的状态。
何时使用此技能
- 对象有多个定义明确的状态,且其行为会随所处状态而显著变化(糖果机、订单生命周期、网络连接、解析器)。
- 当前实现中有一个很大的
switch或针对状态枚举的if-else,其中每个分支执行的操作都大不相同。 - 状态集合有限且已知;状态之间的转换有限,并受规则约束。
何时不使用此技能
- 只有两种状态,行为差异也很简单——单个布尔标志加一个小型
if更简单。 - 所谓的“状态”实际上是数据,而非行为——这种情况应使用值对象,而不是状态机。
- 状态并非互斥(多个标志可以同时为真)——这不是状态机,而是功能标志矩阵。
核心内容
该模式包含三个部分:
- 上下文——行为会发生变化的对象(糖果机)。它持有对当前
State对象的引用,并公开委托给该对象的操作。 - 状态接口——声明会随状态而变化的操作(
insertQuarter()、ejectQuarter()、turnCrank()、dispense())。 - 具体状态——实现 State 接口;每个具体状态代表一种特定状态,并具有自己的行为。状态转换通常由状态本身驱动——当前状态根据输入设置上下文的下一个状态。
此模式可以通过将状态实现为单例(无状态),或让每个状态持有对应上下文的状态来实现。优先选择更简单的形式。
State 与 Strategy。 结构完全相同:上下文将操作委托给可替换的行为对象。二者的意图不同:
- Strategy:由客户端在构造时选择算法。
- State:作为操作的结果,上下文在内部改变自身状态。
决策启发
- 识别“按状态进行大型分支判断”的异味——这是采用 State 模式的典型理由。
- 先定义状态图(有哪些状态、哪些转换合法)。状态类应与状态图一一对应。
- 可以在上下文中集中处理状态转换(更简单,但上下文需要了解所有状态),也可以在状态类中处理(更分散,但每个状态都必须知道接下来会是什么状态)。
反模式
- 状态爆炸——每个细微的行为变化都变成一个新的状态类。重新考虑这些变化是否真的是不同状态,还是仅仅是参数。
- 状态不通过上下文的方法,而是直接修改上下文状态——这会让状态类与上下文的内部实现耦合。
- 在“状态”实际上没有不同表现的用例中实现 State——这是在按模式给事物命名,而不是在使用模式。
- 忘记处理非法转换——
Dispensed状态收到insertQuarter()调用时,应明确响应,而不是静默地不执行任何操作。
另请参阅
gof-strategy——结构相同,但意图不同(以及由谁来选择)。gof-command——当某个操作触发状态转换时,该操作本身可以是 Command。gof-kotlin-idioms——Kotlin 密封类与穷尽式when结合,可在编译时检查状态机,无需为每种状态分别创建子类。
参考资料
- Head First Design Patterns(第 2 版),第 10 章(“事物的状态:State 模式”)——以糖果机为背景的经典阐述,其中包含 No-Quarter / Has-Quarter / Sold / Sold-Out 状态。
评论
支持 Markdown