允许一个对象在其内部状态改变时改变它的行为。 就是将“一个大 class + 一堆 if else”替换为“一堆小 class”。

实现

  1. 上下文 (Context) 保存了对于一个具体状态对象的引用, 并会将所有与该状态相关的工作委派给它。 上下文通过状态接口与状态对象交互, 且会提供一个设置器用于传递新的状态对象。

    1. /**
    2. * The Context defines the interface of interest to clients. It also maintains a
    3. * reference to an instance of a State subclass, which represents the current
    4. * state of the Context.
    5. */
    6. class Context {
    7. /**
    8. * @type {State} A reference to the current state of the Context.
    9. */
    10. private state: State;
    11. constructor(state: State) {
    12. this.transitionTo(state);
    13. }
    14. /**
    15. * The Context allows changing the State object at runtime.
    16. */
    17. public transitionTo(state: State): void {
    18. console.log(`Context: Transition to ${(<any>state).constructor.name}.`);
    19. this.state = state;
    20. this.state.setContext(this);
    21. }
    22. /**
    23. * The Context delegates part of its behavior to the current State object.
    24. */
    25. public request1(): void {
    26. this.state.handle1();
    27. }
    28. public request2(): void {
    29. this.state.handle2();
    30. }
    31. }
  2. 状态 (State) 接口会声明特定于状态的方法。 这些方法应能被其他所有具体状态所理解, 因为你不希望某些状态所拥有的方法永远不会被调用。

    1. /**
    2. * The base State class declares methods that all Concrete State should
    3. * implement and also provides a backreference to the Context object, associated
    4. * with the State. This backreference can be used by States to transition the
    5. * Context to another State.
    6. */
    7. abstract class State {
    8. protected context: Context;
    9. public setContext(context: Context) {
    10. this.context = context;
    11. }
    12. public abstract handle1(): void;
    13. public abstract handle2(): void;
    14. }
  3. 具体状态 (Concrete States) 会自行实现特定于状态的方法。 为了避免多个状态中包含相似代码, 你可以提供一个封装有部分通用行为的中间抽象类。状态对象可存储对于上下文对象的反向引用。 状态可以通过该引用从上下文处获取所需信息, 并且能触发状态转移。 ```typescript /**

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

      public handle2(): void { console.log(‘ConcreteStateA handles request2.’); } }

class ConcreteStateB extends State { public handle1(): void { console.log(‘ConcreteStateB handles request1.’); }

  1. public handle2(): void {
  2. console.log('ConcreteStateB handles request2.');
  3. console.log('ConcreteStateB wants to change the state of the context.');
  4. this.context.transitionTo(new ConcreteStateA());
  5. }

}

  1. 4. 上下文和具体状态都可以设置上下文的下个状态, 并可通过替换连接到上下文的状态对象来完成实际的状态转换。
  2. ```typescript
  3. /**
  4. * The client code.
  5. */
  6. const context = new Context(new ConcreteStateA());
  7. context.request1();
  8. context.request2();

有限状态机(finite state machines, FSM)

image.png

  • 你拥有一组状态,并且可以在这组状态之间进行切换
  • 状态机同一时刻只能处于一种状态
  • 状态机会接收一组输入或事件
  • 每一个状态有一组转换,每一个转换都关联着一个输入并指向另一个状态

    整个状态机可以分为:状态、输入和转换

实例

  1. namespace Appliances
  2. {
  3. //
  4. // EXPORTED TYPES
  5. //
  6. export class Toaster implements ToasterOperations {
  7. private _state: ToasterState = new IdleState()
  8. constructor() {
  9. this.logCurrentState()
  10. }
  11. public insertBread(): void {
  12. this._state = this._state.insertBread()
  13. this.logCurrentState()
  14. }
  15. public pullLever(): void {
  16. this._state = this._state.pullLever()
  17. this.logCurrentState()
  18. }
  19. public ejectBread(): void {
  20. this._state = this._state.ejectBread()
  21. this.logCurrentState()
  22. }
  23. public removeBread(): void {
  24. this._state = this._state.removeBread()
  25. this.logCurrentState()
  26. }
  27. private logCurrentState(): void {
  28. console.log(this._state)
  29. }
  30. }
  31. export interface ToasterOperations {
  32. insertBread(): void
  33. pullLever(): void
  34. ejectBread(): void
  35. removeBread(): void
  36. }
  37. //
  38. // HIDDEN TYPES
  39. //
  40. abstract class ToasterState implements ToasterOperations {
  41. public insertBread(): ToasterState {
  42. throw new Error("Invalid operation")
  43. }
  44. public pullLever(): ToasterState {
  45. throw new Error("Invalid operation")
  46. }
  47. public ejectBread(): ToasterState {
  48. throw new Error("Invalid operation")
  49. }
  50. public removeBread(): ToasterState {
  51. throw new Error("Invalid operation")
  52. }
  53. }
  54. class IdleState extends ToasterState {
  55. public insertBread(): ToasterState {
  56. return new BreadInsertedState()
  57. }
  58. }
  59. class BreadInsertedState extends ToasterState {
  60. public pullLever(): ToasterState {
  61. return new ToastingState()
  62. }
  63. }
  64. class ToastingState extends ToasterState {
  65. public ejectBread(): ToasterState {
  66. return new BreadEjectedState()
  67. }
  68. }
  69. class BreadEjectedState extends ToasterState {
  70. public removeBread(): ToasterState {
  71. return new IdleState()
  72. }
  73. }
  74. }
  75. console.log("testing allowed transitions")
  76. var toaster = new Appliances.Toaster()
  77. toaster.insertBread()
  78. toaster.pullLever()
  79. toaster.ejectBread()
  80. toaster.removeBread()
  81. console.log("testing disallowed transitions")
  82. var toaster2 = new Appliances.Toaster()
  83. toaster2.pullLever()

参考资料

  1. 精读《设计模式 - State 状态模式》
  2. 状态模式
  3. 《游戏编程模式》—-第7章 状态模式
  4. 从零开始的状态机漫谈(1)——万物之始的语言
  5. The State Pattern Exemplified in TypeScript