状态模式
状态模式(State Pattern)是一种行为设计模式,它允许对象在其内部状态改变时改变它的行为。这种类型的设计模式属于行为型模式,它可以使状态的变化更加清晰和易于管理。
状态模式的主要角色
- Context(上下文):维护一个对当前状态对象的引用,并将所有与状态相关的行为委托给当前状态对象。
- State(状态接口):定义了由具体状态类实现的所有可能的行为。
- ConcreteState(具体状态类):实现了状态接口中定义的行为。每个具体状态类代表一种可能的状态,并负责处理来自
Context的请求。 - 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(上下文):包含了当前状态,并提供了方法让用户与机器交互。它还负责管理糖果的数量以及状态之间的转换。
- 使用示例:展示了如何创建一个糖果机实例并模拟用户与机器的交互,包括插入硬币、退回硬币、转动曲柄等操作。
应用场景
状态模式适用于以下几种情况:
- 当一个对象的行为取决于其状态,并且必须在运行时刻根据状态改变其行为时。
- 当操作包含大量重复的条件语句,其中每个分支处理特定状态时。
- 当状态转换的逻辑复杂且频繁变动时,使用状态模式可以简化状态管理逻辑,使代码更易读、更易维护。
通过使用状态模式,可以使状态管理和状态转换更加清晰和结构化,从而提高代码的可维护性和灵活性。这对于构建具有多种状态和复杂状态转换的应用程序特别有用。