跳转到内容

状态模式

首发于:2022-04-02

基本概念

状态模式 (State Pattern)允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类,类的行为随着它的状态改变而改变。

当程序需要根据不同的外部情况来做出不同操作时,最直接的方法就是使用 switch-case 或 if-else 语句将这些可能发生的情况全部兼顾到,但是这种做法应付复杂一点的状态判断时就有点力不从心,开发者得找到合适的位置添加或修改代码,这个过程很容易出错,这时引入状态模式可以某种程度上缓解这个问题。

现实生活中的例子

交通灯,红黄绿三种状态,每种状态下车辆、行人的行为都不一样。

工作流,你提交了一个报销流程,这个流程可能处于,直属领导审批、部门领导审批、人力资源审批、完成等各种状态,每种状态的动作都不一样。

电灯的开关状态,如果是多功能的电灯可能还会有更多的状态。

女生作为你的朋友、好朋友、女朋友、老婆等不同状态,行为也不同。

在这些场景中,有以下特点:

  1. 对象有有限多个状态,且状态间可以相互切换;
  2. 各个状态和对象的行为逻辑有比较强的对应关系,即在不同状态时,对应的处理逻辑不一样。

应用场景

  1. 操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态,那么可以使用状态模式来将分支的处理分散到单独的状态类中;
  2. 对象的行为随着状态的改变而改变,那么可以考虑状态模式,来把状态和行为分离,虽然分离了,但是状态和行为是对应的,再通过改变状态调用状态对应的行为;

优缺点

优点:

  1. 结构相比之下清晰,避免了过多的 switch-case 或 if-else 语句的使用,避免了程序的复杂性提高系统的可维护性;
  2. 符合开闭原则,每个状态都是一个子类,增加状态只需增加新的状态类即可,修改状态也只需修改对应状态类就可以了;
  3. 封装性良好,状态的切换在类的内部实现,外部的调用无需知道类内部如何实现状态和行为的变换。

缺点:

  1. 引入了多余的类,每个状态都有对应的类,导致系统中类的个数增加。

实现

先来看一个 if-else 的例子,学了状态模式就不推荐这么做了🤪:

js
class TrafficLight {
  constructor() {
    this.state = '绿灯';
  }

  /* 设置交通灯状态 */
  setState(target) {
    if (target === '红灯') {
      this.state = '红灯'
      console.log('交通灯颜色变为 红色,行人通行 & 车辆等待')
    } else if (target === '黄灯') {
      this.state = '黄灯'
      console.log('交通灯颜色变为 黄色,行人等待 & 车辆等待')
    } else if (target === '绿灯') {
      this.state = '绿灯'
      console.log('交通灯颜色变为 绿色,行人等待 & 车辆通行')
    } else {
      console.error('交通灯还有这颜色?')
    }
  }
}

const trafficLight = new TrafficLight();
trafficLight.setState('红灯') // 输出: 交通灯颜色变为 红色,行人通行 & 车辆等待
trafficLight.setState('黄灯') // 输出: 交通灯颜色变为 黄色,行人等待 & 车辆等待
trafficLight.setState('绿灯') // 输出: 交通灯颜色变为 绿色,行人等待 & 车辆通行
trafficLight.setState('紫灯') // 输出: 交通灯还有这颜色?

状态模式的例子:

js
/* 抽象状态类 */
class AbstractState {
  constructor() {
    if (new.target === AbstractState) { // 如果是 ts 就不用搞这么麻烦了
      throw new Error('抽象类不能直接实例化!')
    }
  }

  /* 抽象方法 */
  employ() {
    throw new Error('抽象方法不能调用!')
  }
}

/* 交通灯类 */
class State extends AbstractState {
  constructor(name, desc) {
    super()
    this.color = { name, desc }
  }

  /* 覆盖抽象方法 */
  employ(trafficLight) {
    console.log('交通灯颜色变为 ' + this.color.name + ',' + this.color.desc)
    trafficLight.setState(this)
  }
}

/* 交通灯类 */
class TrafficLight {
  constructor() {
    this.state = null
  }

  /* 获取交通灯状态 */
  getState() {
    return this.state
  }

  /* 设置交通灯状态 */
  setState(state) {
    this.state = state
  }
}

const trafficLight = new TrafficLight()

const redState = new State('红色', '行人等待 & 车辆等待')
const greenState = new State('绿色', '行人等待 & 车辆通行')
const yellowState = new State('黄色', '行人等待 & 车辆等待')

redState.employ(trafficLight) // 输出: 交通灯颜色变为 红色,行人通行 & 车辆等待
yellowState.employ(trafficLight) // 输出: 交通灯颜色变为 黄色,行人等待 & 车辆等待
greenState.employ(trafficLight) // 输出: 交通灯颜色变为 绿色,行人等待 & 车辆通行

京ICP备18043750号