状态模式

# 状态模式

在一个对象的内部状态变化时改变其行为, 使对象看起来好像改变了自身所属的类.

现实世界中:

手机的按键和开关会根据设备当前状态完成不同的行为:

  • 当处于解锁状态时, 按下按钮将执行各种功能.

  • 当处于锁定状态时, 按下任何按键都将解锁屏幕.

  • 当处于电量不足时, 按下任何按钮都将显示充电界面.

# 优点

  • 单一职责原则 将与特定状态相关的代码放在独特的类中.

  • 开闭原则 无需修改已有状态和上下文就能引入新状态.

  • 通过消除臃肿的状态机条件语句简化上下文代码.

# 缺点

  • 如果状态机只有很少的几个状态, 或者很少发生改变, 那么该模式不适用

# 适用场景

  • 如果对象需要根据自身状态进行不同行为, 同时状态的数量非常多与状态相关的代码会频繁变更时.

  • 如果某个类需要根据成员变量的当前值改变自身行为, 从而需要使用大量的条件语句时.

  • 当相似状态和基于条件的状态机转换中存在许多重复代码时

# 实现

点击查看代码
/**
 * The Context defines the interface of interest to clients. It also maintains a
 * reference to an instance of a State subclass, which represents the current
 * state of the Context.
 */
class Context {
  /**
   * @type {State} A reference to the current state of the context
   */
  private state!: State;

  constructor(state: State) {
    this.transitionTo(state);
  }

  /**
   * The Context allows changing the State object at runtime.
   */
  public transitionTo(state: State): void {
    console.log(`Context: Transition to ${(<any>state).constructor.name}`);
    this.state = state;
    this.state.setContext(this);
  }

  public request1(): void {
    this.state.handle1();
  }

  public request2(): void {
    this.state.handle2();
  }
}

/**
 * The base State class declares methods that all Concrete State should
 * implement and also provides a backreference to the Context object, associated
 * with the State. This backreference can be used by States to transition the
 * Context to another State.
 */
abstract class State {
  protected context!: Context;

  public setContext(context: Context) {
    this.context = context;
  }

  public abstract handle1(): void;
  
  public abstract handle2(): void;
}

/**
 * Concrete States implement various behaviors, associated with a state of the
 * Context
 */
class ConcreteStateA extends State {
  public handle1() {
    console.log(`ConcreteStateA handles request1.`);
    console.log(`ConcreteStateA wants to change the state of the context.`);
    this.context.transitionTo(new ConcreteStateB());
  }

  public handle2() {
    console.log(`ConcreteStateA handles request2.`);
  }
}

class ConcreteStateB extends State {
  public handle1() {
    console.log(`ConcreteStateB handles request1.`);
  }

  public handle2() {
    console.log(`ConcreteStateB handles request2.`);
    console.log(`ConcreteStateB wants to change the state of the context.`);
    this.context.transitionTo(new ConcreteStateA());
  }
}

const context = new Context(new ConcreteStateA());
context.request1();
context.request2();

// output:
// Context: Transition to ConcreteStateA.
// ConcreteStateA handles request1.
// ConcreteStateA wants to change the state of the context.
// Context: Transition to ConcreteStateB.
// ConcreteStateB handles request2.
// ConcreteStateB wants to change the state of the context.
// Context: Transition to ConcreteStateA.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91