# State（状态模式）

State（状态模式）属于行为型模式。

**意图：允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。**

简单来说，就是将 “一个大 class + 一堆 if else” 替换为 “一堆小 class”。一堆小 class 就是一堆状态，用一堆状态代替 if else 会更好拓展与维护。

## 举例子

如果看不懂上面的意图介绍，没有关系，设计模式需要在日常工作里用起来，结合例子可以加深你的理解，下面我准备了三个例子，让你体会什么场景下会用到这种设计模式。

### 团队接口人

团队是由很多同学组成的，但有一位接口人 TL，这位 TL 可能一会儿和产品经理谈需求，一会儿和其他 TL 谈规划，一会儿和 HR 谈人事，总之要做很多事情，很显然一个人是忙不过来的。TL 通过将任务分发给团队中每个同学，而不让他们直接和产品经理、其他 TL、HR 接触，那么这位 TL 的办事效率就会相当高，因为每个同学只负责一块具体的业务，而 TL 在不同时刻叫上不同的同学，让他们出面解决他们负责的专业领域问题，那么在外面看，这位 TL 团队能力很广，在内看，每个人负责的事情也比较单一。

### 台灯按钮

我们经常会看到只有一个按钮的台灯，但是可以通过按钮调节亮度，大概是如下一个循环 “关 -> 弱光 -> 亮 -> 强光 -> 关”，那么每次按按钮后，要跳转到什么状态，其实和当前状态有关。我们可以用 if else 解决这个问题，也可以用状态模式解决。

用状态模式解决，就是将这四个状态封装为四个类，每个类都执行按下按钮后要跳转到的状态，这样未来新增一种模式，只要改变部分类即可。

### 数据库连接器

在数据库连接前后，这个连接器的状态显然非常不同，我们如果仅用一个类描述数据库连接器，则内部免不了写大量分支语句进行状态判断。那么此时有更好的方案吗？状态模式告诉我们，可以创建多个不同状态类，比如连接前、连接中、连接后三种状态类，在不同时刻内部会替换为不同的子类，它们都继承同样的父类，所以外面看上去不需要感知内部的状态变化，内部又可以进行状态拆分，进行更好的维护。

## 意图解释

**意图：允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。**

重点在 “内部状态” 的理解，也就是状态改变是由对象内部触发的，而不是外部，所以 **外部根本无需关心对象是否用了状态模式**，拿数据库连接器的例子来说，不管这个类是用 if else 堆砌的，还是用状态模式做的，都完全不妨碍它对外提供的稳定 API（接口问题），所以状态模式实质上是一种内聚的设计模式。

## 结构图

<img width=600 src="https://img.alicdn.com/imgextra/i1/O1CN01tbZ0bQ1w8xcUgbWTJ_!!6000000006264-2-tps-1350-486.png">

- State: 状态接口，类比为台灯状态。
- ConcreteState: 具体状态，都继承于 State，类比为台灯的强光、弱光状态。

## 代码例子

下面例子使用 typescript 编写。

```typescript
// 定义状态接口
interface State {
  // 模拟台灯点亮
  show: () => string
}

class Light1 implements State {
  constructor(context: Context) {
    this.context = context
  }

  show() {
    return '关灯'
  }

  // 按下按钮
  public click() {
    this.context.setState(new Light2(this.context))
  }
}

class Light2 implements State {
  constructor(context: Context) {
    this.context = context
  }

  show() {
    return '弱光'
  }

  // 按下按钮
  public click() {
    this.context.setState(new Light3(this.context))
  }
}

class Light3 implements State {
  constructor(context: Context) {
    this.context = context
  }

  show() {
    return '亮'
  }

  // 按下按钮
  public click() {
    this.context.setState(new Light4(this.context))
  }
}

class Light4 implements State {
  constructor(context: Context) {
    this.context = context
  }

  show() {
    return '强光'
  }

  // 按下按钮
  public click() {
    this.context.setState(new Light1(this.context))
  }
}

// 台灯
public class Lamp {
  // 当前状态
  private currentState = new Light1(this)

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

  // 按下按钮
  public click() {
    this.getState().click()
  }
}

const lamp = new Lamp() // 关闭
lamp.click() // 弱光
lamp.click() // 亮
lamp.click() // 强光
lamp.click() // 关闭
```

其实有很多种方式来实现，不必拘泥于形式，大体上只要保证由多个类实现不同状态，每个类实现到下一个状态切换就好了。

## 弊端

该用 if else 的时候还是要用，不要但凡遇到 if else 就使用状态模式，那样就是书读傻了。一定要判断，是否各状态间差异很大，且使用状态模式后维护性比 if else 更好，才应该用状态模式。

## 总结

在合适场景下，状态模式可以使代码更符合开闭原则，每个类独立维护时，逻辑也更精简、聚焦，更易维护。

> 讨论地址是：[精读《设计模式 - State 状态模式》· Issue #303 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/303)

**如果你想参与讨论，请 [点击这里](https://github.com/dt-fe/weekly)，每周都有新的主题，周末或周一发布。前端精读 - 帮你筛选靠谱的内容。**

> 关注 **前端精读微信公众号**

<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg">

> 版权声明：自由转载-非商用-非衍生-保持署名（[创意共享 3.0 许可证](https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh)）
