能在一个对象的内部状态变化时改变其行为, 使其看上去就像改变了自身所属的类一样。
程序在任意时刻仅可处于几种有限的状态中。
在任何一个特定状态中, 程序的行为都不相同, 且可瞬间从一个状态切换到另一个状态。
不过, 根据当前状态, 程序可能会切换到另外一种状态, 也可能会保持当前状态不变。
这些数量有限且预先定义的状态切换规则被称为转移。
实现
假如你有一个 文档
Document类。 文档可能会处于 草稿
Draft 、 审阅中
Moderation和 已发布
Published三种状态中的一种。 文档的 publish
发布方法在不同状态下的行为略有不同:
- 处于
草稿
状态时, 它会将文档转移到审阅中状态。 - 处于
审阅中
状态时, 如果当前用户是管理员, 它会公开发布文档。 - 处于
已发布
状态时, 它不会进行任何操作。
旧实现的缺点
状态机通常由众多条件运算符 ( if
或 switch
) 实现, 可根据对象的当前状态选择相应的行为。 “状态” 通常只是对象中的一组成员变量值。
为了能根据当前状态选择完成相应行为的方法, 绝大部分方法中会包含复杂的条件语句。 修改其转换逻辑可能会涉及到修改所有方法中的状态条件语句, 导致代码的维护工作非常艰难。
状态模式建议为对象的所有可能状态新建一个类, 然后将所有状态的对应行为抽取到这些类中。
原始对象被称为上下文 (context), 它并不会自行实现所有行为, 而是会**保存一个指向表示当前状态的状态对象的引用**, 且将所有与状态相关的工作委派给该对象。
所有状态类都必须遵循同样的接口, 而且上下文必须仅通过接口与这些对象进行交互。
- 上下文保存了一个具体状态对象的引用,将所有的状态相关的工作委派给他。
- 上下文通过状态接口与状态对象交互
- 提供一个设置器传递新的状态对象
- 状态接口声明了特定于状态的方法,这些状态能被其他具体状态锁理解
- 具体状态对象自行实现特定于状态的方法
- 状态对象可存储对于上下文对象的引用。
- 状态可以通过该引用从上下文处获取所需信息, 并且能触发状态转移。
- 上下文和具体对象都可以设置下一个状态
与策略模式对比
这个结构可能看上去与策略模式相似, 但有一个关键性的不同:
在状态模式中, 特定状态知道其他所有状态的存在, 且能触发从一个状态到另一个状态的转换; 策略则几乎完全不知道其他策略的存在。
**
状态可被视为策略的扩展。 两者都基于组合机制: 它们都通过将部分工作委派给 “帮手” 对象来改变其在不同情景下的行为。 策略使得这些对象相互之间完全独立, 它们不知道其他对象的存在。 但状态模式没有限制具体状态之间的依赖, 且允许它们自行改变在不同情景下的状态。