浅尝有限状态机

1,531 阅读2分钟

场景

实现一个 Connection 连接类,它有 openreadwirteclose 四个函数,下面是这四个函数的限制条件:

  • read 需要在 open 后才能使用,否则抛出 Not Open 异常
  • wirte 需要在 open 后才能使用,否则抛出 Not Open 异常
  • open 不能在 open 状态下使用,否则抛出 Already Open 异常
  • close 不能在 close 状态下使用,否则抛出 Already Closed 异常

如何实现这个类?

方案一:在函数前置判断语句用来判断

最通常的做法是在 openreadwirteclose 函数前部加一些状态判断条件,例如:

class Connection {
  state = "CLOSED";

  read = () => {
    if (this.state !== "OPEN") {
      throw new Error("Not Open");
    }
    console.log("reading");
  };

  write = (content) => {
    if (this.state !== "OPEN") {
      throw new Error("Not Open");
    }
    console.log("writing:", content);
  };

  open = () => {
    if (this.state === "OPEN") {
      throw new Error("Already Open");
    }
    this.state = "OPEN";
  };

  close = () => {
    if (this.state === "CLOSED") {
      throw new Error("Already Closed");
    }
    this.state = "CLOSED";
  };
}

上面的状态比较少,所以判断还不算很复杂;不过如果有更多的状态,前部的判断条件语句会变得非常复杂且难以维护。

方案二:使用有限状态机

另一个方案是使用有限状态机,它的核心思想是为每一个状态都抽象成一个状态类,这个状态类继承一个基础的状态类,并且在自身实现当前状态能够操作的函数,例如:

class ConnectionState {
  read = () => {
    throw new Error("Not Open");
  };

  write = () => {
    throw new Error("Not Open");
  };

  open = () => {
    throw new Error("Already Open");
  };

  close = () => {
    throw new Error("Already Closed");
  };
}

class ClosedConnectionState extends ConnectionState {
  open = (_this) => {
    _this.state = new OpenConnectionState();
  };
}

class OpenConnectionState extends ConnectionState {
  read = () => {
    console.log("reading");
  };

  write = (content) => {
    console.log("writing:", content);
  };

  close = (_this) => {
    _this.state = new ClosedConnectionState();
  };
}

class Connection {
  state;

  constructor() {
    this.init();
  }

  init = () => {
    this.makeNewState(new ClosedConnectionState());
  };

  makeNewState = (state) => {
    this.state = state;
  };

  read = () => {
    return this.state.read(this);
  };

  write = (content) => {
    return this.state.write(content);
  };

  open = () => {
    return this.state.open(this);
  };

  close = () => {
    return this.state.close(this);
  };
}

可以看到,上面的写法中没有用一行 if 条件判断,就实现了状态的转移和判断。

上面的写法比第一种写法代码量多了不少,不过可维护性却大大增加了,这个优势在状态更多时更能体现。

参考文档

原文链接