状态模式之状态机

状态机的背景

在软件开发过程中,尤其是涉及到复杂的系统行为控制时,我们常常会遇到这样的情况:一个对象或者系统会在多种状态之间进行转换,并且在不同状态下对相同事件的响应是不同的。

以自动售卖机为例,自动售卖机有多种状态,如空闲、已投币、正在选择商品、正在出货、缺货、故障等状态。它可以接收多种事件,如投币、选择商品、出货、补货、报告故障等。在不同的状态下,自动售卖机对这些事件的处理方式完全不同。

如果不采用一种有效的建模方式,这种复杂的状态转换和行为响应逻辑将会变得非常混乱。例如,我们可能会使用大量的if - else语句来判断自动售卖机的当前状态和要处理的事件,这会导致代码难以理解、维护和扩展。当需要添加新的状态或者事件时,代码的修改会变得非常复杂,容易引入新的错误。

为了解决这些问题,状态机的概念应运而生。状态机提供了一种清晰、结构化的方式来描述系统在不同状态下如何响应各种事件,以及如何进行状态转换。它将系统的状态、事件、状态转换和动作进行了抽象和整合,使得开发人员能够更好地理解和设计复杂的系统行为。

状态机的组成部分

状态(States)

  1. 定义
  • 状态是系统在某个特定时刻的一种模式或者情况。在自动售卖机的例子中,如 “空闲” 状态表示自动售卖机在等待用户投币或者进行其他操作,“正在出货” 状态表示自动售卖机正在将商品送到出货口的过程中。
  1. 代码体现
  • 在示例代码中,通过VendingMachineState枚举来定义自动售卖机的各种状态。枚举中的每个成员(如IDLECOIN_INSERTED等)代表了一种具体的状态,这种方式使得状态的定义清晰明了,并且在代码中易于引用和比较。

事件(Events)

  1. 定义
  • 事件是触发系统状态转换或者动作执行的因素。它可以是外部用户的操作(如用户投币),也可以是内部系统的信号(如定时器触发的补货提醒)。对于自动售卖机来说,“投币事件” 会使自动售卖机从 “空闲” 状态转换到 “已投币” 状态。
  1. 代码体现
  • 在代码中,使用VendingMachineEvent枚举来定义可能发生的事件。这些事件作为参数传递给状态机(VendingMachine类)的handleEvent方法,用于决定当前状态下如何响应事件并进行状态转换。

状态转换(Transitions)

  1. 定义
  • 状态转换描述了系统从一个状态转换到另一个状态的过程。这个过程是由事件触发的,并且通常伴随着一些动作的执行。例如,当自动售卖机处于 “正在选择商品” 状态,并且接收到 “出货事件” 时,如果库存足够,它会转换到 “正在出货” 状态,同时库存数量减 1。
  1. 代码体现
  • VendingMachine类的handleEvent方法中,通过一系列嵌套的switch语句来实现状态转换。外层switch根据当前状态进行分支,内层switch根据传入的事件决定转换到何种状态。这种方式详细地定义了在每个状态下,针对不同事件的状态转换规则。

动作(Actions)

  1. 定义
  • 动作是在状态转换过程中或者在特定状态下执行的操作。它可以是对系统内部数据的修改(如更新库存),也可以是与外部设备的交互(如控制出货电机)或者向用户提供反馈(如显示提示信息)。在自动售卖机出货时,减少库存数量和输出 “正在出货” 的提示信息就是动作。
  1. 代码体现
  • VendingMachine类的handleEvent方法中,许多地方都包含了动作的执行。例如,在handleSelectingProductState方法中,当处理 “出货事件” 并且库存足够时,会执行库存数量减 1 的操作(stockLevel--),同时输出相应的提示信息,这些操作就是动作在代码中的体现。

状态机的优势

提高代码的可读性

  1. 清晰的行为建模
  • 状态机通过将系统的行为抽象为状态、事件、转换和动作,使得系统的行为逻辑更加清晰。开发人员可以很容易地理解在不同状态下系统对事件的响应方式以及状态之间是如何转换的。以自动售卖机代码为例,通过查看handleEvent方法中的switch语句结构,就能清楚地了解自动售卖机在各个状态下的行为规则。
  1. 符合人类思维模式
  • 状态机的概念与人类对事物状态变化的认知方式相符合。人们在日常生活中经常会遇到各种状态机的例子,如交通信号灯的变化。这种熟悉的概念使得开发人员和其他维护人员能够更快地理解代码的意图,减少了学习成本。

增强代码的可维护性和可扩展性

  1. 易于修改和调试
  • 当需要修改某个状态下的行为或者状态转换规则时,只需要在状态机的相应部分进行修改。例如,如果要改变自动售卖机在 “缺货” 状态下对 “投币事件” 的响应方式,只需要在handleOutOfStockState方法中修改对应的switch分支即可,不会影响到其他状态和事件的处理逻辑。这种局部修改的特性使得代码的调试和维护更加容易。
  1. 方便添加新功能
  • 当需要添加新的状态或者事件时,状态机的结构可以很方便地进行扩展。例如,如果要为自动售卖机添加一个 “促销活动” 状态,只需要在VendingMachineState枚举中添加新的状态成员,然后在VendingMachine类的handleEvent方法中添加新的switch分支来定义该状态下对各种事件的响应和状态转换规则即可。这种扩展性使得系统能够更好地适应业务需求的变化。

自动售卖机(代码示例)

以下是一个简单的状态机代码示例,以模拟一个自动售卖机的工作流程为例,展示状态机在实际代码中的实现方式。这里使用 Java 语言来编写示例代码。

简单案例

定义状态枚举

首先,定义一个枚举来表示自动售卖机的不同状态:

public enum VendingMachineState {IDLE, // 空闲状态,等待投币或操作COIN_INSERTED, // 已投币状态,可选择商品SELECTING_PRODUCT, // 正在选择商品状态DISPENSING_PRODUCT, // 正在出货状态OUT_OF_STOCK, // 缺货状态ERROR // 故障状态
}
定义事件枚举

接着,定义一个枚举来表示可能发生的事件,这些事件会触发自动售卖机的状态转换:

public enum VendingMachineEvent {INSERT_COIN, // 投币事件SELECT_PRODUCT, // 选择商品事件DISPENSE_PRODUCT, // 出货事件REFILL_STOCK, // 补货事件REPORT_ERROR // 报告故障事件
}
定义状态机类

然后,创建一个状态机类,它负责管理状态转换和执行相应的动作:

public class VendingMachine {private VendingMachineState currentState;private int stockLevel; // 商品库存数量public VendingMachine() {this.currentState = VendingMachineState.IDLE;this.stockLevel = 10; // 初始库存设为10件商品}// 根据当前状态和传入的事件,执行状态转换并返回新的状态public VendingMachineState handleEvent(VendingMachineEvent event) {switch (currentState) {case IDLE:return handleIdleState(event);case COIN_INSERTED:return handleCoinInsertedState(event);case SELECTING_PRODUCT:return handleSelectingProductState(event);case DISPENSING_PRODUCT:return handleDispensingProductState(event);case OUT_OF_STOCK:return handleOutOfStockState(event);case ERROR:return handleErrorState(event);default:throw new IllegalArgumentException("Invalid state: " + currentState);}}private VendingMachineState handleIdleState(VendingMachineEvent event) {switch (event) {case INSERT_COIN:System.out.println("已投币,进入已投币状态。");return VendingMachineState.COIN_INSERTED;case REPORT_ERROR:System.out.println("报告故障,进入故障状态。");return VendingMachineState.ERROR;default:System.out.println("当前空闲,等待操作。");return currentState;}}private VendingMachineState handleCoinInsertedState(VendingMachineEvent event) {switch (event) {case SELECT_PRODUCT:System.out.println("开始选择商品,进入正在选择商品状态。");return VendingMachineState.SELECTING_PRODUCT;case INSERT_COIN:System.out.println("已再次投币,仍处于已投币状态。");return currentState;case REPORT_ERROR:System.out.println("报告故障,进入故障状态。");return VendingMachineState.ERROR;default:System.out.println("已投币,可选择商品。");return currentState;}}private VendingMachineState handleSelectingProductState(VendingMachineEvent event) {switch (event) {case DISPENSE_PRODUCT:if (stockLevel > 0) {System.out.println("正在出货,进入正在出货状态。");stockLevel--;return VendingMachineState.DISPENSING_PRODUCT;} else {System.out.println("商品缺货,进入缺货状态。");return VendingMachineState.OUT_OF_STOCK;}case SELECT_PRODUCT:System.out.println("重新选择商品,仍处于正在选择商品状态。");return currentState;case INSERT_COIN:System.out.println("已再次投币,仍处于正在选择商品状态。");return currentState;case REPORT_ERROR:System.out.println("报告故障,进入故障状态。");return VendingMachineState.ERROR;default:System.out.println("正在选择商品。");return currentState;}}private VendingMachineState handleDispensingProductState(VendingMachineEvent event) {switch (event) {case INSERT_COIN:System.out.println("已再次投币,等待完成出货操作。");return currentState;case REPORT_ERROR:System.out.println("报告故障,进入故障状态。");return VendingMachineState.ERROR;case DISPENSE_PRODUCT:System.out.println("出货完成,进入空闲状态。");return VendingMachineState.IDLE;default:System.out.println("正在出货。");return currentState;}}private VendingMachineState handleOutOfStockState(VendingMachineEvent event) {switch (event) {case REFILL_STOCK:System.out.println("已补货,进入空闲状态。");stockLevel = 10;return VendingMachineState.IDLE;case REPORT_ERROR:System.out.println("报告故障,进入故障状态。");return VendingMachineState.ERROR;default:System.out.println("商品缺货,请补货。");return currentState;}}private VendingMachineState handleErrorState(VendingMachineEvent event) {switch (event) {case REPORT_ERROR:System.out.println("已报告故障,仍处于故障状态。");return currentState;case REFILL_STOCK:System.out.println("故障未排除,无法补货。");return currentState;default:System.out.println("机器故障,请联系维修人员。");return currentState;}}public VendingMachineState getCurrentState() {return currentState;}public int getStockLevel() {return stockLevel;}
}
测试代码
public class Main {public static void main(String[] args) {VendingMachine vendingMachine = new VendingMachine();// 初始状态应该是空闲状态System.out.println("初始状态: " + vendingMachine.getCurrentState());// 投币vendingMachine.handleEvent(VendingMachineEvent.INSERT_COIN);// 选择商品vendingMachine.handleEvent(VendingMachineEvent.SELECT_PRODUCT);// 出货(假设库存足够)vendingMachine.handleEvent(VendingMachineEvent.DISPENSE_PRODUCT);// 再次投币vendingMachine.handleEvent(VendingMachineEvent.INSERT_COIN);// 缺货情况for (int i = 0; i < 10; i++) {vendingMachine.handleEvent(VendingMachineEvent.SELECT_PRODUCT);vendingMachine.handleEvent(VendingMachineEvent.DISPENSE_PRODUCT);}// 补货vendingMachine.handleEvent(VendingMachineEvent.REFILL_STOCK);// 报告故障vendingMachine.handleEvent(VendingMachineEvent.REPORT_ERROR);// 再次补货(故障状态下无法补货)vendingMachine.handleEvent(VendingMachineEvent.REFILL_STOCK);}
}

最后,编写测试代码来验证状态机的功能:

在这个示例中:

  • 通过枚举定义了自动售卖机的不同状态和可能发生的事件。
  • 状态机类 VendingMachine 根据当前状态和传入的事件,通过一系列的 switch 语句来执行相应的状态转换,并在必要时执行如更新库存、输出提示信息等动作。
  • 测试代码展示了自动售卖机在不同操作下的状态转换过程,模拟了一个较为完整的自动售卖机工作流程。

这样的状态机实现方式使得代码结构相对清晰,便于理解和维护,并且可以根据实际需求轻松地扩展状态和事件的定义以及相应的处理逻辑。

使用 Spring event 重构

定义事件相关抽象类和具体事件类
import org.springframework.context.ApplicationEvent;// 抽象的自动售卖机事件类,继承自ApplicationEvent,方便后续具体事件类扩展
public abstract class AbstractVendingMachineEvent extends ApplicationEvent {public AbstractVendingMachineEvent(Object source) {super(source);}// 抽象方法,用于在具体事件类中实现针对该事件的具体处理逻辑public abstract void handleEvent(VendingMachine vendingMachine);
}// 投币事件类
public class InsertCoinEvent extends AbstractVendingMachineEvent {public InsertCoinEvent(Object source) {super(source);}@Overridepublic void handleEvent(VendingMachine vendingMachine) {switch (vendingMachine.getCurrentState()) {case IDLE:vendingMachine.setCurrentState(VendingMachineState.COIN_INSERTED);System.out.println("已投币,进入已投币状态。");break;case COIN_INSERTED:System.out.println("已再次投币,可继续选择商品或进行其他操作。");break;default:System.out.println("当前状态下无法进行投币操作。");}}
}// 在已投币后选择商品的事件类
public class SelectProductAfterCoinInsertedEvent extends AbstractVendingMachineEvent {public SelectProductAfterCoinInsertedEvent(Object source) {super(source);}@Overridepublic void handleEvent(VendingMachine vendingMachine) {if (vendingMachine.getCurrentState() == VendingMachineState.COIN_INSERTED) {vendingMachine.setCurrentState(VendingMachineState.SELECTING_PRODUCT);System.out.println("开始选择商品,进入正在选择商品状态。");} else {System.out.println("当前状态下无法进行选择商品操作。");}}
}// 再次投币事件类(在已投币状态下再次投币)
public class InsertCoinAgainAfterCoinInsertedEvent extends AbstractVendingMachineEvent {public InsertCoinAgainAfterCoinInsertedEvent(Object source) {super(source);}@Overridepublic void handleEvent(VendingMachine vendingMachine) {if (vendingMachine.getCurrentState() == VendingMachineState.COIN_INSERTED) {System.out.println("已再次投币,仍处于已投币状态。");// 这里假设vendingMachine有对应的setter方法设置当前状态vendingMachine.setCurrentState(vendingMachine.getCurrentState());} else {System.out.println("当前状态下无法进行再次投币操作。");}}
}// 出货事件类
public class DispenseProductEvent extends AbstractVendingMachineEvent {private String product;public DispenseProductEvent(Object source, String product) {super(source);this.product = product;}@Overridepublic void handleEvent(VendingMachine vendingMachine) {switch (vendingMachine.getCurrentState()) {case SELECTING_PRODUCT:if (vendingMachine.getStockLevel() > 0) {vendingMachine.setCurrentState(VendingMachineState.DISPENSING_PRODUCT);vendingMachine.setStockLevel(vendingMachine.getStockLevel() - 1);System.out.println("正在出货,进入正在出货状态。");} else {vendingMachine.setCurrentState(VendingMachineState.OUT_OF_STOCK);System.out.println("商品缺货,进入缺货状态。");}break;case DISPENSING_PRODUCT:System.out.println("出货完成,进入空闲状态。");vendingMachine.setCurrentState(VendingMachineState.IDLE);break;default:System.out.println("当前状态下无法进行出货操作。");}}
}// 报告故障事件类
public class ReportErrorEvent extends AbstractVendingMachineEvent {public ReportErrorEvent(Object source) {super(source);}@Overridepublic void handleEvent(VendingMachine vendingMachine) {switch (vendingMachine.getCurrentState()) {case IDLE:vendingMachine.setCurrentState(VendingMachineState.ERROR);System.out.println("报告故障,进入故障状态。");break;case COIN_INSERTED:vendingMachine.setCurrentState(VendingMachineState.ERROR);System.out.println("报告故障,进入故障状态。");break;case SELECTING_PRODUCT:vendingMachine.setCurrentState(VendingMachineState.ERROR);System.out.println("报告故障,进入故障状态。");break;case DISPENSING_PRODUCT:vendingMachine.setCurrentState(VendingMachineState.ERROR);System.out.println("报告故障,进入故障状态。");break;case OUT_OF_STOCK:vendingMachine.setCurrentState(VendingMachineState.ERROR);System.out.println("报告故障,进入故障状态。");break;case ERROR:System.out.println("已报告故障,仍处于故障状态。");break;default:System.out.println("当前状态下无法进行报告故障操作。");}}
}// 补货事件类
public class RefillStockEvent extends AbstractVendingMachineEvent {private String product;private int quantity;public RefillStockEvent(Object source, String product, int quantity) {super(source);this.product = product;this.quantity = quantity;}@Overridepublic void handleEvent(VendingMachine vendingMachine) {switch (vendingMachine.getCurrentState()) {case OUT_OF_STOCK:vendingMachine.setStockLevel(vendingMachine.getStockLevel() + quantity);vendingMachine.setCurrentState(VendingMachineState.IDLE);System.out.println("已补货,进入空闲状态。");break;case ERROR:System.out.println("故障未排除,无法补货。");break;default:System.out.println("当前状态下无法进行补货操作。");}}
}
定义投币后相关事件的抽象类
public abstract class CoinInsertedEvent extends ApplicationEvent {public CoinInsertedEvent(Object source) {super(source);}// 抽象方法,用于具体事件子类实现不同的行为public abstract void execute(VendingMachine vendingMachine);
}
定义事件监听器类
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;// 投币事件监听器
@Component
public class InsertCoinEventListener implements ApplicationListener<InsertCoinEvent> {@Overridepublic void onEvent(InsertCoinEvent event) {// 获取事件源对应的自动售卖机对象VendingMachine vendingMachine = (VendingMachine) event.getSource();// 调用事件对象的handleEvent方法来执行针对该事件的逻辑event.handleEvent(vendingMachine);// 这里也可以添加一些额外的逻辑,比如记录日志等System.out.println("已成功监听并处理投币事件。");}
}// 在已投币后选择商品事件监听器
@Component
public class SelectProductAfterCoinInsertedEventListener implements ApplicationListener<SelectProductAfterCoinInsertedEvent> {@Overridepublic void onEvent(SelectProductAfterCoinInsertedEvent event) {VendingMachine vendingMachine = (VendingMachine) event.getSource();event.handleEvent(vendingMachine);System.out.println("已成功监听并处理在已投币后选择商品事件。");}
}// 再次投币事件监听器(在已投币状态下再次投币)
@Component
public class InsertCoinAgainAfterCoinInsertedEventListener implements ApplicationListener<InsertCoinAgainAfterCoinInsertedEvent> {@Overridepublic void onEvent(InsertCoinAgainAfterCoinInsertedEvent event) {VendingMachine vendingMachine = (VendingMachine) event.getSource();event.handleEvent(vendingMachine);System.out.println("已成功监听并处理再次投币事件(在已投币状态下再次投币)。");}
}// 出货事件监听器
@Component
public class DispenseProductEventListener implements ApplicationListener<DispenseProductEvent> {@Override@SuppressWarnings("unchecked")public void onEvent(DispenseProductEvent event) {VendingMachine vendingMachine = (VendingMachine) event.getSource();event.handleEvent(vendingMachine);System.out.println("已成功监听并处理出货事件。");}
}// 报告故障事件监听器
@Component
public class ReportErrorEventListener implements ApplicationListener<ReportErrorEvent> {@Overridepublic void onEvent(ReportErrorEvent event) {VendingMachine vendingMachine = (VendingMachine) event.getSource();event.handleEvent(vendingMachine);System.out.println("已成功监听并处理报告故障事件。");}
}// 补货事件监听器
@Component
public class RefillStockEventListener implements ApplicationListener<RefillStockEvent> {@Overridepublic void onEvent(RefillStockEvent event) {VendingMachine vendingMachine = (VendingMachine) event.getSource();event.handleEvent(vendingMachine);System.out.println("已成功监听并处理补货事件。");}
}
自动售卖机类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;@Component
public class VendingMachine {private VendingMachineState currentState;private int stockLevel;@Autowiredprivate ApplicationContext applicationContext;public VendingMachine() {this.currentState = VendingMachineState.IDLE;this.stockLevel = 10;}// 根据传入的事件对象,处理事件并更新状态public void handleEvent(AbstractVendingMachineEvent event) {event.handleEvent(this);}// 获取当前状态的方法public VendingMachineState getCurrentState() {return currentState;}// 设置当前状态的方法public void setCurrentState(VendingMachineState currentState) {this.currentState = currentState;}public int getStockLevel() {return stockLevel;}public void setStockLevel(int stockLevel) {this.stockLevel = stockLevel;}
}
测试代码
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;@SpringBootApplication
public class VendingMachineApplication {public static void main(String[] args) {SpringApplication.run(VendingMachineApplication.class, args);}@Beanpublic CommandLineRunner commandLineRunner(VendingMachine vendingMachine) {return args -> {// 模拟投币操作InsertCoinEvent insertCoinEvent = new InsertCoinEvent(vendingMachine);vendingMachine.handleEvent(insertCoinEvent);// 模拟在已投币后选择商品操作SelectProductAfterCoinInsertedEvent selectProductAfterCoinInsertedEvent = new SelectProductAfterCoinInsertedEvent(vendingMachine);vendingMachine.handleEvent(selectProductAfterCoinInsertedEvent);// 模拟再次投币操作(在已投币状态下再次投币)InsertCoinAgainAfterCoinInsertedEvent insertCoinAgainAfterCoinInsertedEvent = new InsertCoinAgainAfterCoinInsertedEvent(vendingMachine);vendingMachine.handleEvent(insertCoinAgainAfterCoinInsertedEvent);// 模拟出货操作DispenseProductEvent dispenseProductEvent = new DispenseProductEvent(vendingMachine, "Coke");vendingMachine.handleEvent(dispenseProductEvent);// 模拟报告故障操作ReportErrorEvent reportErrorEvent = new ReportErrorEvent(vendingMachine);vendingMachine.handleEvent(reportErrorEvent);// 模拟补货操作RefillStockEvent refillStockEvent = new RefillStockEvent(vendingMachine, "Coke", 5);vendingMachine.handleEvent(refillStockEvent);};}
}

使用策略模式重构

定义状态机上下文类

// 状态机上下文类,用于管理状态和执行事件处理
public class VendingMachine {private VendingMachineState currentState;private int stockLevel; // 商品库存数量// 存储不同状态下不同事件对应的策略private Map<VendingMachineState, Map<VendingMachineEvent, StateMachineStrategy>> stateStrategies;public VendingMachine() {this.currentState = VendingMachineState.IDLE;this.stockLevel = 10; // 初始库存设为10件商品// 初始化策略映射stateStrategies = new HashMap<>();initStrategies();}// 初始化各个状态下的事件处理策略private void initStrategies() {// 空闲状态策略Map<VendingMachineEvent, StateMachineStrategy> idleStateStrategies = new HashMap<>();idleStateStrategies.put(VendingMachineEvent.INSERT_COIN, new IdleInsertCoinStrategy());stateStrategies.put(VendingMachineState.IDLE, idleStateStrategies);// 已投币状态策略Map<VendingMachineEvent, StateMachineStrategy> coinInsertedStateStrategies = new HashMap<>();coinInsertedStateStrategies.put(VendingMachineEvent.SELECT_PRODUCT, new CoinInsertedSelectProductStrategy());stateStrategies.put(VendingMachineState.COIN_INSERTED, coinInsertedStateStrategies);// 正在选择商品状态策略Map<VendingMachineEvent, StateMachineStrategy> selectingProductStateStrategies = new HashMap<>();selectingProductStateStrategies.put(VendingMachineEvent.DISPENSE_PRODUCT, new SelectingProductDispenseProductStrategy());stateStrategies.put(VendingMachineState.SELECTING_PRODUCT, selectingProductStateStrategies);// 正在出货状态策略Map<VendingMachineEvent, StateMachineStrategy> dispensingProductStateStrategies = new HashMap<>();dispensingProductStateStrategies.put(VendingMachineEvent.INSERT_COIN, new DispensingProductInsertCoinAgainStrategy());stateStrategies.put(VendingMachineState.DISPENSING_PRODUCT, dispensingProductStateStrategies);// 缺货状态策略Map<VendingMachineEvent, StateMachineStrategy> outOfStockStateStrategies = new HashMap<>();stateStrategies.put(VendingMachineState.OUT_OF_STOCK, outOfStockStateStrategies);// 故障状态策略Map<VendingMachineEvent, StateMachineStrategy> errorStateStrategies = new HashMap<>();stateStrategies.put(VendingMachineState.ERROR, errorStateStrategies);}// 处理事件的方法,完全通过策略模式处理,不再有状态分支的switch语句public VendingMachineState handleEvent(VendingMachineEvent event) {Map<VendingMachineEvent, StateMachineStrategy> currentStateStrategies = stateStrategies.get(currentState);if (currentStateStrategies!= null && currentStateStrategies.containsKey(event)) {StateMachineStrategy strategy = currentStateStrategies.get(event);return strategy.handle(this);}return currentState;}// 获取当前状态的方法public VendingMachineState getCurrentState() {return currentState;}// 设置当前状态的方法public void setCurrentState(VendingMachineState currentState) {this.currentState = currentState;}public int getStockLevel() {return stockLevel;}
}

定义策略接口
// 状态机策略接口,定义了处理事件并返回新状态的方法
public interface StateMachineStrategy {VendingMachineState handle(VendingMachine vendingMachine);
}
定义具体策略类
空闲状态投币策略类
public class IdleInsertCoinStrategy implements StateMachineStrategy {@Overridepublic VendingMachineState handle(VendingMachine vendingMachine) {System.out.println("已投币,进入已投币状态。");vendingMachine.setCurrentState(VendingMachineState.COIN_INSERTED);return VendingMachineState.COIN_INSERTED;}
}
已投币状态选择商品策略类
public class CoinInsertedSelectProductStrategy implements StateMachineStrategy {@Overridepublic VendingMachineState handle(VendingMachine vendingMachine) {System.out.println("开始选择商品,进入正在选择商品状态。");vendingMachine.setCurrentState(VendingMachineState.SELECTING_PRODUCT);return VendingMachineState.SELECTING_PRODUCT;}
}
正在选择商品状态出货策略类
public class SelectingProductDispenseProductStrategy implements StateMachineStrategy {@Overridepublic VendingMachineState handle(VendingMachine vendingMachine) {if (vendingMachine.getStockLevel() > 0) {System.out.println("正在出货,进入正在出货状态。");vendingMachine.setCurrentState(VendingMachineState.DISPENSING_PRODUCT);vendingMachine.setStockLevel(vendingInstance.getStockLevel() - 1);return VendingMachineState.DISPENSING_PRODUCT;} else {System.out.println("商品缺货,进入缺货状态。");vendingMachine.setCurrentState(VendingMachineState.OUT_OF_STOCK);return VendingMachineState.OUT_OF_STOCK;}}
}
测试代码
public class Main {public static void main(String[] args) {VendingMachine vendingMachine = new VendingMachine();// 初始状态应该是空闲状态System.out.println("初始状态: " + vendingMachine.getCurrentState());// 投币vendingMachine.handleEvent(VendingMachineEvent.INSERT_COIN);// 选择商品vendingMachine.handleEvent(VendingMachineEvent.SELECT_PRODUCT);// 出货(假设库存足够)vendingMachine.handleEvent(VendingMachineEvent.DISPENSE_PRODUCT);// 再次投币vendingMachine.handleEvent(VendingMachineEvent.INSERT_COIN);// 缺货情况for (int i = 0; i < 10; i++) {vendingMachine.handleEvent(VendingMachineEvent.SELECT_PRODUCT);vendingMachine.handleEvent(VendingMachineEvent.DISPENSE_PRODUCT);}// 补货vendingMachine.handleEvent(VendingMachineEvent.REFILL_STOCK);// 报告故障vendingMachine.handleEvent(VendingMachineEvent.REPORT_ERROR);// 再次补货(故障状态下无法补货)}
}

使用责任链模式重构状态链路

抽象状态处理者类
// 抽象状态处理者类
abstract class StateHandler {private StateHandler nextHandler;// 设置下一个处理者public StateHandler setNext(StateHandler nextHandler) {this.nextHandler = nextHandler;return nextHandler;}// 处理请求的抽象方法abstract public String handle(String currentState);// 获取下一个处理者protected StateHandler getNextHandler() {return nextHandler;}
}
具体状态处理者类
// IDLE状态处理者类
class IdleHandler extends StateHandler {@Overridepublic String handle(String currentState) {if ("IDLE".equals(currentState)) {System.out.println("当前状态为IDLE,已插入硬币,进入COIN_INSERTED状态。");return getNextHandler()!= null? getNextHandler().handle("COIN_INSERTED") : null;}return null;}
}// COIN_INSERTED状态处理者类
class CoinInsertedHandler extends StateHandler {@Overridepublic String handle(String currentState) {if ("COIN_INSERTED".equals(currentState)) {System.out.println("当前状态为COIN_INSERTED,正在选择产品,进入SELECTING_PRODUCT状态。");return getNextHandler()!= null? getNextHandler().handle("SELECTING_PRODUCT") : null;}return null;}
}// SELECTING_PRODUCT状态处理者类
class SelectingProductHandler extends StateHandler {@Overridepublic String handle(String currentState) {if ("SELECTING_PRODUCT".equals(currentState)) {System.out.println("当前状态为SELECTING_PRODUCT,正在分发产品,进入DISPENSING_PRODUCT状态。");return getNextHandler()!= null? getNextHandler().handle("DISPENSING_PRODUCT") : null;}return null;}
}// DISPENSING_PRODUCT状态处理者类
class DispensingProductHandler extends StateHandler {@Overridepublic String handle(String currentState) {if ("DISPENSING_PRODUCT".equals(currentState)) {System.out.println("当前状态为DISPENSING_PRODUCT,产品已分发完毕,若库存不足则进入OUT_OF_STOCK状态,否则回到IDLE状态。");// 这里假设可以通过某种方式获取库存情况,这里简单模拟一下boolean isOutOfStock = false; // 假设库存充足return isOutOfStock? getNextHandler().handle("OUT_OF_STOCK") : getNextHandler().handle("IDLE");}return null;}
}// OUT_OF_STOCK状态处理者类
class OutOfStockHandler extends StateHandler {@Overridepublic String handle(String currentState) {if ("OUT_OF_STOCK".equals(currentState)) {System.out.println("当前状态为OUT_OF_STOCK,处理库存不足情况,可进行补货等操作,之后回到IDLE状态。");return getNextHandler()!= null? getNextHandler().handle("IDLE") : null;}return null;}
}// ERROR状态处理者类
class ErrorHandler extends StateHandler {@Overridepublic String handle(String currentState) {if ("ERROR".equals(currentState)) {System.out.println("当前状态为ERROR,处理错误情况,可进行错误排查等操作,之后回到IDLE状态。");return getNextHandler()!= null? getNextHandler().handle("IDLE") : null;}return null;}
}
状态初始化
 // 创建各个状态处理者实例StateHandler idleHandler = new IdleHandler();StateHandler coinInsertedHandler = new CoinInsertedHandler();StateHandler selectingProductHandler = new SelectingProductHandler();StateHandler dispensingProductHandler = new DispensingProductHandler();StateHandler outOfStockHandler = new OutOfStockHandler();StateHandler errorHandler = new ErrorHandler();// 构建责任链idleHandler.setNext(coinInsertedHandler).setNext(selectingProductHandler).setNext(dispensingProductHandler).setNext(outOfStockHandler).setNext(errorHandler);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/476079.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【每日 C/C++ 问题】

一、什么是 C 中的初始化列表&#xff1f;它的作用是什么&#xff1f; 作用&#xff1a;c提供了初始化列表语法&#xff0c;用来初始化属性 语法&#xff1a;构造函数&#xff08;&#xff09;&#xff1a;属性1&#xff08;值1&#xff09;&#xff0c;属性2&#xff08;值…

【前端知识】Javascript前端框架Vue入门

前端框架VUE入门 概述基础语法介绍组件特性组件注册Props 属性声明事件组件 v-model(双向绑定)插槽Slots内容与出口 组件生命周期样式文件使用1. 直接在<style>标签中写CSS2. 引入外部CSS文件3. 使用CSS预处理器4. 在main.js中全局引入CSS文件5. 使用CSS Modules6. 使用P…

【代码pycharm】动手学深度学习v2-04 数据操作 + 数据预处理

数据操作 数据预处理 1.数据操作运行结果 2.数据预处理实现运行结果 第四课链接 1.数据操作 import torch # 张量的创建 x1 torch.arange(12) print(1.有12个元素的张量&#xff1a;\n,x1) print(2.张量的形状&#xff1a;\n,x1.shape) print(3.张量中元素的总数&#xff1…

《Python浪漫的烟花表白特效》

一、背景介绍 烟花象征着浪漫与激情&#xff0c;将它与表白结合在一起&#xff0c;会创造出别具一格的惊喜效果。使用Python的turtle模块&#xff0c;我们可以轻松绘制出动态的烟花特效&#xff0c;再配合文字表白&#xff0c;打造一段专属的浪漫体验。 接下来&#xff0c;让…

CSS中Flex布局应用实践总结

① 两端对齐 比如 要求ul下的li每行四个&#xff0c;中间间隔但是需要两段对齐&#xff0c;如下图所示&#xff1a; 这是除了基本的flex布局外&#xff0c;还需要用到:nth-of-type伪类来控制每行第一个与第四个的padding。 .hl_list{width: 100%;display: flex;align-items…

STM32与CS创世SD NAND(贴片SD卡)结合完成FATFS文件系统移植与测试是一个涉及硬件与软件综合应用的复杂过程

一、前言 在STM32项目开发中&#xff0c;经常会用到存储芯片存储数据。 比如&#xff1a;关机时保存机器运行过程中的状态数据&#xff0c;上电再从存储芯片里读取数据恢复&#xff1b;在存储芯片里也会存放很多资源文件。比如&#xff0c;开机音乐&#xff0c;界面上的菜单图…

Matlab实现海鸥优化算法优化随机森林算法模型 (SOA-RF)(附源码)

目录 1.内容介绍 2.部分代码 3.实验结果 4.内容获取 1内容介绍 海鸥优化算法&#xff08;Seagull Optimization Algorithm, SOA&#xff09;是一种基于海鸥群体行为的新型元启发式优化算法。SOA通过模拟海鸥在寻找食物时的飞行模式和集体行动来探索解空间&#xff0c;寻找最优…

C# Postman或者PostApi调试前端webapi接口发送带有request/body/head信息

知识&#xff1a; 前端接口&#xff0c;表单形式提交。 req.ContentType "application/x-www-form-urlencoded"; x-www-form-urlencoded 是一种常见的 MIME 类型&#xff0c;用于将键值对编码为 HTTP 请求体中的 URL 编码格式。在 Web API 中&#xff0c;x-www-for…

npm上传自己封装的插件(vue+vite)

一、npm账号及发包删包等命令 若没有账号&#xff0c;可在npm官网&#xff1a;https://www.npmjs.com/login 进行注册。 在当前项目根目录下打开终端命令窗口&#xff0c;常见命令如下&#xff1a; 1、登录命令&#xff1a;npm login&#xff08;不用每次都重新登录&#xff0…

案例精选 | 某知名教育集团基于安全运营平台的全域威胁溯源实践

某知名教育集团成立于1999年&#xff0c;总部位于北京海淀中关村。集团专注于K-12基础教育&#xff0c;构建了从幼儿园到高中的全面教育体系&#xff0c;涵盖学校管理、教学科研、师资培训、信息化服务等多个方面。集团在全国范围内设有15所小学、12所初中、9所高中、6个国际部…

鸿蒙多线程开发——线程间数据通信对象01

1、线程间通信 线程间通信指的是并发多线程间存在的数据交换行为。由于ArkTS语言兼容TS/JS&#xff0c;其运行时的实现与其它所有的JS引擎一样&#xff0c;都是基于Actor内存隔离的并发模型提供并发能力。 对于不同的数据对象&#xff0c;在ArkTS线程间通信的行为是有差异的&…

徒手从零搭建一套ELK日志平台

徒手从零搭建一套ELK日志平台 日志分析的概述日志分析的作用主要收集工具集中式日志系统主要特点采集日志分类ELK概述初级版ELK终极版ELK高级版ELKELK收集日志的两种形式 搭建ELK平台Logstash工作原理Logstash核心概念环境准备安装部署docker添加镜像加速器安装部署Elasticsear…

React基础知识一

写的东西太多了&#xff0c;照成csdn文档编辑器都开始卡顿了&#xff0c;所以分篇写。 1.安装React 需要安装下面三个包。 react:react核心包 react-dom:渲染需要用到的核心包 babel:将jsx语法转换成React代码的工具。&#xff08;没使用jsx可以不装&#xff09;1.1 在html中…

【FPGA开发】ZYNQ中PS与PL交互操作总结、原理浅析、仿真操作

文章目录 PL与PS交互综述交互端口性能&特点&#xff08;选择方案的凭据&#xff09;GPIO-AXI_GPDMA-DMACHP-AXI_HPACP-AXI_ACP 数据交互实验GP通过BRAMPS为主机&#xff0c;读写BRAMPL作为主机&#xff0c;读写BRAM DMA方式交互 PL与PS交互综述 网络上关于PS PL交互的教程…

【论文笔记】Large Brain Model (LaBraM, ICLR 2024)

Code: https://github.com/935963004/LaBraM Data: 无 目录 AbstractIntroductionMethodNeural tokenizer training&#xff1a;Pre-training LaBraM&#xff1a; ResultsExperimental setup&#xff1a;Pre-training result&#xff1a;Comparison with SOTA&#xff1a;Pre-t…

推荐几个 VSCode 流程图工具

Visual Studio Code&#xff08;简称VSCode&#xff09;是一个由微软开发的免费、开源的代码编辑器。 VSCode 发布于 2015 年&#xff0c;而且很快就成为开发者社区中广受欢迎的开发工具。 VSCode 可用于 Windows、macOS 和 Linux 等操作系统。 VSCode 拥有一个庞大的扩展市…

2024信创数据库TOP30之达梦DM8

近年来&#xff0c;中国信创产业快速崛起&#xff0c;其中数据库作为基础软件的重要组成部分&#xff0c;发挥了至关重要的作用。近日&#xff0c;由DBC联合CIW/CIS共同发布的“2024信创数据库TOP30”榜单正式揭晓&#xff0c;汇聚了国内顶尖的数据库企业及产品&#xff0c;成为…

将网站地址改成https地址需要哪些材料

HTTPS&#xff08;安全超文本传输协议&#xff09;是HTTP协议的扩展。它大大降低了个人数据&#xff08;用户名、密码、银行卡号等&#xff09;被拦截的风险&#xff0c;还有助于防止加载网站时的内容替换&#xff0c;包括广告替换。 在发送数据之前&#xff0c;信息会使用SSL…

RPC安全可靠的异常重试

当调用方调用服务提供方&#xff0c;由于网络抖动导致的请求失败&#xff0c;这个请求调用方希望执行成功。 调用方应该如何操作&#xff1f;catch异常再发起一次调用&#xff1f;显然不够优雅。这时可以考虑使用RPC框架的重试机制。 RPC框架的重试机制 RPC重试机制&#xff1…

【c++丨STL】priority_queue(优先级队列)的使用与模拟实现

&#x1f31f;&#x1f31f;作者主页&#xff1a;ephemerals__ &#x1f31f;&#x1f31f;所属专栏&#xff1a;C、STL 目录 前言 一、priority_queue简介 二、priority_queue的使用 构造函数(constructor) empty size top push和pop swap 仿函数的使用 三、prio…