能在一个对象的内部状态变化时改变其行为, 使其看上去就像改变了自身所属的类一样。

image.png
程序在任意时刻仅可处于几种有限的状态中。

在任何一个特定状态中, 程序的行为都不相同, 且可瞬间从一个状态切换到另一个状态。

不过, 根据当前状态, 程序可能会切换到另外一种状态, 也可能会保持当前状态不变。

这些数量有限且预先定义的状态切换规则被称为转移。

实现

假如你有一个 文档Document类。 文档可能会处于 草稿Draft 、 审阅中Moderation和 已发布Published三种状态中的一种。 文档的 publish发布方法在不同状态下的行为略有不同:

  • 处于 草稿状态时, 它会将文档转移到审阅中状态。
  • 处于 审阅中状态时, 如果当前用户是管理员, 它会公开发布文档。
  • 处于 已发布状态时, 它不会进行任何操作。

状态模式 - 图2

旧实现的缺点

状态机通常由众多条件运算符 ( ifswitch ) 实现, 可根据对象的当前状态选择相应的行为。 “状态” 通常只是对象中的一组成员变量值。
image.png
为了能根据当前状态选择完成相应行为的方法, 绝大部分方法中会包含复杂的条件语句。 修改其转换逻辑可能会涉及到修改所有方法中的状态条件语句, 导致代码的维护工作非常艰难。

状态模式建议为对象的所有可能状态新建一个类, 然后将所有状态的对应行为抽取到这些类中。

原始对象被称为上下文 (context), 它并不会自行实现所有行为, 而是会**保存一个指向表示当前状态的状态对象的引用**, 且将所有与状态相关的工作委派给该对象。


状态模式 - 图4

所有状态类都必须遵循同样的接口, 而且上下文必须仅通过接口与这些对象进行交互。

  1. 上下文保存了一个具体状态对象的引用,将所有的状态相关的工作委派给他。
    1. 上下文通过状态接口与状态对象交互
    2. 提供一个设置器传递新的状态对象
  2. 状态接口声明了特定于状态的方法,这些状态能被其他具体状态锁理解
  3. 具体状态对象自行实现特定于状态的方法
    1. 状态对象可存储对于上下文对象的引用。
    2. 状态可以通过该引用从上下文处获取所需信息, 并且能触发状态转移。
  4. 上下文和具体对象都可以设置下一个状态

与策略模式对比

这个结构可能看上去与策略模式相似, 但有一个关键性的不同:
在状态模式中, 特定状态知道其他所有状态的存在, 且能触发从一个状态到另一个状态的转换策略则几乎完全不知道其他策略的存在。
**
状态可被视为策略的扩展。 两者都基于组合机制: 它们都通过将部分工作委派给 “帮手” 对象来改变其在不同情景下的行为。 策略使得这些对象相互之间完全独立, 它们不知道其他对象的存在。 但状态模式没有限制具体状态之间的依赖, 且允许它们自行改变在不同情景下的状态。