设计模式 | 状态模式

50 阅读5分钟

Intent 意图

状态模式是一种行为设计模式。它可以在一个状态内部状态发生改变时改变其行为,且可以从一个状态变化到另外的一个状态。

Motivation 动机

当程序在任意时刻可处于几种有限的状态中,根据当前状态,程序可能会切换到另外一种状态,也可能会保持当前状态不变。在不同状态下,对于程序表现行为不同。状态模式主要解决特定状态下行为管理以及状态管理的问题。

Applicability 适用范围

  • 如果对象需要根据自身当前状态进行不同行为, 同时状态的数量非常多且与状态相关的代码会频繁变更的话, 可使用状态模式。
  • 模式建议你将所有特定于状态的代码抽取到一组独立的类(具体的状态类 concrete state class)中。 这样一来, 你可以在独立于其他状态的情况下添加新状态或修改已有状态, 从而减少维护成本。
  • 如果某个类需要根据成员变量的当前值改变自身行为, 从而需要使用大量的条件语句时, 可使用该模式。
  • 状态模式会将这些条件语句的分支抽取到相应状态类的方法中。 同时, 你还可以清除主要类中与特定状态相关的临时成员变量和帮手方法代码。
  • 当相似状态和基于条件的状态机转换中存在许多重复代码时, 可使用状态模式。
  • 状态模式让你能够生成状态类层次结构, 通过将公用代码抽取到抽象基类中来减少重复。

Structure 结构

image.png

Participate 结构成员

结构中各个类、对象所扮演的角色

  • Context: 原始对象被称为上下文 (con­text), 它并不会自行实现所有行为, 而是会保存一个指向表示当前状态的状态对象的引用, 且将所有与状态相关的工作委派给该对象。
  • state:接口中定义了可能修改 state 的动作方法
  • concreteState 类: 代表着不同的状态,在各个状态类里面定义了不同状态下触发各个可迁移状态操作的状态行为,即在某个状态下,触发该行为时,该行为方法的表现形式。

Cooperation 协作

状态模式建议为对象的所有可能状态新建一个类, 然后将所有状态的对应行为抽取到这些类中。原始对象被称为上下文 (con­text), 它并不会自行实现所有行为, 而是会保存一个指向表示当前状态的状态对象的引用, 且将所有与状态相关的工作委派给该对象。当动作被调用时,委托给当前状态的状态下的行为。同时,当状态发生变换时,这些数量有限且预先定义的状态切换规则被称为转移

Consequence 后果

  • Good
    • 单一职责原则。 将与特定状态相关的代码放在单独的类中。
    • 开闭原则。 无需修改已有状态类和上下文就能引入新状态。
    • 通过消除臃肿的状态机条件语句简化上下文代码。
  • Bad
    • 如果状态机只有很少的几个状态, 或者很少发生改变, 那么应用该模式可能会显得小题大作。

Implementation 示例

以下的代码实例来自于 head first 中状态机的章节,这里只是简单实现,文后附上书里具体的状态机各个方法的表现,有兴趣可以自己实现。

head first 中糖果状态机的状态转移示例

// An example of head first
class MachineContext implements State {
// 持有不同状态下状态类的实例
  SoldState: State
  SoldOutState: State
  NoQuarterState: State
  HasQuarterState: State

  state: State
  stateType: StateType

  count = 0

  constructor(count: number = 0) {
    this.SoldOutState = new SoldOutState(this)
    this.SoldState = new SoldState(this)
    this.NoQuarterState = new NoQuarterState(this)
    this.HasQuarterState = new HasQuarterState(this)
    this.count = count
    this.state = this.count > 0 ? this.NoQuarterState : this.SoldOutState
    this.stateType = this.count > 0 ? StateType.NO_QUARTER : StateType.SOLD_OUT
  }

  setState(state: State) {
    this.state = state
  }

  insertQuarter() {
 // context 调用 state 的方法,每个状态类都实现了该状态类下的表现行为
    this.state.insertQuarter()
  }
  // do something()
}

// concrete state class 状态类
class NoQuarterState implements State {
  machine: MachineContext // 持有 context

  constructor(machine: MachineContext) {
    this.machine = machine
  }

  insertQuarter() {
    console.log('NoQuarterState:')
   // key function call
    this.machine.setState(this.machine.HasQuarterState)
  }
  // do something
}

Known uses 已知应用

有限状态机 Finite State Machine

FSM 简单实例

状态机可归纳为4个要素,即当前状态、条件、动作、次态。“现态”和“条件”是因,“动作”和“次态”是果。详解如下:

  • 当前状态:是指当前所处的状态。
  • 条件 or 事件:当一个条件被满足,将会进行一次动作的执行或者状态的迁移。
  • 动作:当条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,但也可以仍旧保持原状态。
  • 次态:“次态”是相对于“现态”而言的,如果动作结束之后将会迁移至下个状态,则将下个状态称为“次态”(e.g. 如下表,如果 A 满足条件后,执行动作并迁移到 B,则当下时刻,A 被称为现态,B 称为次态)
状态 A状态 B....
条件 1.........
条件 2进入状态 B......
............

Related Pattern 和其他模式直接的关系

  • 状态 & 策略 两者都基于组合机制: 它们都通过将部分工作委派给 “帮手” 对象来改变其在不同情景下的行为。
  • 策略使得这些对象相互之间完全独立, 它们不知道其他对象的存在。
  • 状态模式没有限制具体状态之间的依赖, 且允许它们自行去改变在不同情景下的状态。

往期文章 index

【设计模式索引 - 持续更新中】 juejin.cn/post/705899…

Reference:

图中截图来自于 guru 网站以及 head first

guru: refactoringguru.cn/design-patt…

附一

image.png