Skip to content

状态模式

状态模式(State Pattern)是一种行为设计模式,它允许对象在其内部状态改变时改变它的行为。这种类型的设计模式属于行为型模式,它可以使状态的变化更加清晰和易于管理。

状态模式的主要角色

  1. Context(上下文):维护一个对当前状态对象的引用,并将所有与状态相关的行为委托给当前状态对象。
  2. State(状态接口):定义了由具体状态类实现的所有可能的行为。
  3. ConcreteState(具体状态类):实现了状态接口中定义的行为。每个具体状态类代表一种可能的状态,并负责处理来自Context的请求。
  4. Client(客户端):创建Context对象,并根据需要更改其状态。

在 JavaScript 中的实现

下面通过一个简单的例子来说明如何在 JavaScript 中使用状态模式:

示例:糖果机

假设我们需要模拟一个糖果售卖机的状态变化过程。糖果机有四种状态:已售罄、无硬币投入、已投入硬币和出货中。

javascript
// State(状态接口)
class State {
  insertCoin() {}
  ejectCoin() {}
  turnCrank() {}
  dispense() {}
}

// ConcreteState(具体状态类)
class SoldOutState extends State {
  constructor(context) {
    super();
    this.context = context;
  }

  insertCoin() {
    console.log("You can't insert a coin, the machine is sold out");
  }

  ejectCoin() {
    console.log("You can't eject, you haven't inserted a coin yet");
  }

  turnCrank() {
    console.log("You turned, but there are no candies left");
  }

  dispense() {
    console.log("No candy dispensed");
  }
}

class NoCoinInsertedState extends State {
  constructor(context) {
    super();
    this.context = context;
  }

  insertCoin() {
    console.log("You inserted a coin");
    this.context.setState(this.context.getHasCoinState());
  }

  ejectCoin() {
    console.log("You haven't inserted a coin");
  }

  turnCrank() {
    console.log("You turned, but there's no coin");
  }

  dispense() {
    console.log("You need to pay first");
  }
}

class HasCoinState extends State {
  constructor(context) {
    super();
    this.context = context;
  }

  insertCoin() {
    console.log("You can't insert another coin");
  }

  ejectCoin() {
    console.log("Coin returned");
    this.context.setState(this.context.getNoCoinInsertedState());
  }

  turnCrank() {
    console.log("You turned...");
    this.context.setState(this.context.getSoldState());
    setTimeout(() => this.context.releaseBall(), 100); // Simulate delay
  }

  dispense() {
    console.log("No candy dispensed");
  }
}

class SoldState extends State {
  constructor(context) {
    super();
    this.context = context;
  }

  insertCoin() {
    console.log("Please wait, we're already giving you a candy");
  }

  ejectCoin() {
    console.log("Sorry, you already turned the crank");
  }

  turnCrank() {
    console.log("Turning twice doesn't get you more candies!");
  }

  dispense() {
    this.context.releaseBall();
    if (this.context.getCount() > 0) {
      this.context.setState(this.context.getNoCoinInsertedState());
    } else {
      console.log("Oops, out of candies!");
      this.context.setState(this.context.getSoldOutState());
    }
  }
}

// Context(上下文)
class GumballMachine {
  constructor(count) {
    this.soldOutState = new SoldOutState(this);
    this.noCoinInsertedState = new NoCoinInsertedState(this);
    this.hasCoinState = new HasCoinState(this);
    this.soldState = new SoldState(this);

    this.count = count;
    this.state = this.count > 0 ? this.noCoinInsertedState : this.soldOutState;
  }

  insertCoin() {
    this.state.insertCoin();
  }

  ejectCoin() {
    this.state.ejectCoin();
  }

  turnCrank() {
    this.state.turnCrank();
    this.state.dispense();
  }

  releaseBall() {
    console.log("A gumball comes rolling out the slot...");
    if (this.count !== 0) {
      this.count -= 1;
    }
  }

  setState(state) {
    this.state = state;
  }

  getState() {
    return this.state;
  }

  getCount() {
    return this.count;
  }

  getSoldOutState() {
    return this.soldOutState;
  }

  getNoCoinInsertedState() {
    return this.noCoinInsertedState;
  }

  getHasCoinState() {
    return this.hasCoinState;
  }

  getSoldState() {
    return this.soldState;
  }
}

// 使用示例
const gumballMachine = new GumballMachine(5);

gumballMachine.insertCoin(); // You inserted a coin
gumballMachine.turnCrank(); // You turned...
// A gumball comes rolling out the slot...

gumballMachine.insertCoin(); // You inserted a coin
gumballMachine.ejectCoin(); // Coin returned

代码解释

  • State(状态接口):定义了所有状态下的通用操作,如插入硬币、退回硬币、转动曲柄和分发糖果。
  • SoldOutState、NoCoinInsertedState、HasCoinState 和 SoldState(具体状态类):分别实现了不同的状态逻辑。每个状态知道如何响应各种用户输入,并决定是否需要切换到另一个状态。
  • GumballMachine(上下文):包含了当前状态,并提供了方法让用户与机器交互。它还负责管理糖果的数量以及状态之间的转换。
  • 使用示例:展示了如何创建一个糖果机实例并模拟用户与机器的交互,包括插入硬币、退回硬币、转动曲柄等操作。

应用场景

状态模式适用于以下几种情况:

  • 当一个对象的行为取决于其状态,并且必须在运行时刻根据状态改变其行为时。
  • 当操作包含大量重复的条件语句,其中每个分支处理特定状态时。
  • 当状态转换的逻辑复杂且频繁变动时,使用状态模式可以简化状态管理逻辑,使代码更易读、更易维护。

通过使用状态模式,可以使状态管理和状态转换更加清晰和结构化,从而提高代码的可维护性和灵活性。这对于构建具有多种状态和复杂状态转换的应用程序特别有用。

Released under the MIT License.