状态模式允许一个对象在其内部状态改变时改变它的行为。这种模式将状态行为封装在独立的类中,并将动作委托给当前状态对象,从而消除了许多条件语句。
// 状态接口
class VendingMachineState {
insertCoin(machine) {
throw new Error('Method not implemented');
}
selectProduct(machine) {
throw new Error('Method not implemented');
}
dispense(machine) {
throw new Error('Method not implemented');
}
}
// 具体状态类:无硬币状态
class NoCoinState extends VendingMachineState {
insertCoin(machine) {
console.log("Coin inserted");
machine.setState(machine.getHasCoinState());
}
selectProduct(machine) {
console.log("Please insert a coin first");
}
dispense(machine) {
console.log("Please insert a coin first");
}
}
// 具体状态类:有硬币状态
class HasCoinState extends VendingMachineState {
insertCoin(machine) {
console.log("You already inserted a coin");
}
selectProduct(machine) {
console.log("Product selected");
machine.setState(machine.getSoldState());
}
dispense(machine) {
console.log("No product selected");
}
}
// 具体状态类:售出状态
class SoldState extends VendingMachineState {
insertCoin(machine) {
console.log("Please wait, we're already giving you a product");
}
selectProduct(machine) {
console.log("Please wait, we're already giving you a product");
}
dispense(machine) {
console.log("Product dispensed");
if (machine.getCount() > 0) {
machine.setState(machine.getNoCoinState());
} else {
console.log("Out of products");
machine.setState(machine.getSoldOutState());
}
}
}
// 具体状态类:售罄状态
class SoldOutState extends VendingMachineState {
insertCoin(machine) {
console.log("Machine is sold out, cannot accept coins");
}
selectProduct(machine) {
console.log("Machine is sold out, cannot select product");
}
dispense(machine) {
console.log("No product to dispense");
}
}
// 自动售货机类
class VendingMachine {
#noCoinState;
#hasCoinState;
#soldState;
#soldOutState;
#currentState;
#count = 0;
constructor(productCount) {
this.#noCoinState = new NoCoinState();
this.#hasCoinState = new HasCoinState();
this.#soldState = new SoldState();
this.#soldOutState = new SoldOutState();
this.#count = productCount;
this.#currentState = productCount > 0 ? this.#noCoinState : this.#soldOutState;
}
insertCoin() {
this.#currentState.insertCoin(this);
}
selectProduct() {
this.#currentState.selectProduct(this);
}
dispense() {
this.#currentState.dispense(this);
if (this.#currentState !== this.#soldOutState) {
this.#count--;
}
}
setState(state) {
this.#currentState = state;
}
getCount() {
return this.#count;
}
getNoCoinState() { return this.#noCoinState; }
getHasCoinState() { return this.#hasCoinState; }
getSoldState() { return this.#soldState; }
getSoldOutState() { return this.#soldOutState; }
}
// 使用示例
function demonstrateState() {
const machine = new VendingMachine(2);
console.log("Attempting to buy without inserting coin:");
machine.selectProduct();
console.log("\nInserting coin and selecting product:");
machine.insertCoin();
machine.selectProduct();
machine.dispense();
console.log("\nAttempting to buy second product:");
machine.insertCoin();
machine.selectProduct();
machine.dispense();
console.log("\nAttempting to buy when machine is empty:");
machine.insertCoin();
machine.selectProduct();
machine.dispense();
}
demonstrateState();
实现思路
-
VendingMachineState接口:定义了所有具体状态类必须实现的方法,包括insertCoin,selectProduct, 和dispense。 -
具体状态类(
NoCoinState,HasCoinState,SoldState,SoldOutState):- 每个类代表自动售货机的一种特定状态。
- 每个类都实现了
VendingMachineState接口中定义的方法。 - 这些方法定义了在特定状态下,对各种操作的响应。
-
VendingMachine类:- 使用私有字段来存储所有可能的状态和当前状态。
- 提供了插入硬币、选择产品和发放产品的方法。
- 这些方法简单地委托给当前状态对象。
- 提供了
setState方法来改变当前状态。
优点
- 单一职责原则:每个状态类只负责处理该状态下的行为。
- 开闭原则:可以轻松添加新的状态而不修改现有代码。
- 消除了复杂的条件语句:避免了使用大量的 if-else 语句。
- 状态转换的显式化:状态转换逻辑清晰可见。