状态模式之状态机

状态机的背景

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

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

如果不采用一种有效的建模方式,这种复杂的状态转换和行为响应逻辑将会变得非常混乱。例如,我们可能会使用大量的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);
    }

    @Override
    public 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);
    }

    @Override
    public 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);
    }

    @Override
    public 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;
    }

    @Override
    public 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);
    }

    @Override
    public 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;
    }

    @Override
    public 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> {

    @Override
    public void onEvent(InsertCoinEvent event) {
        // 获取事件源对应的自动售卖机对象
        VendingMachine vendingMachine = (VendingMachine) event.getSource();

        // 调用事件对象的handleEvent方法来执行针对该事件的逻辑
        event.handleEvent(vendingMachine);

        // 这里也可以添加一些额外的逻辑,比如记录日志等
        System.out.println("已成功监听并处理投币事件。");
    }
}

// 在已投币后选择商品事件监听器
@Component
public class SelectProductAfterCoinInsertedEventListener implements ApplicationListener<SelectProductAfterCoinInsertedEvent> {

    @Override
    public void onEvent(SelectProductAfterCoinInsertedEvent event) {
        VendingMachine vendingMachine = (VendingMachine) event.getSource();
        event.handleEvent(vendingMachine);
        System.out.println("已成功监听并处理在已投币后选择商品事件。");
    }
}

// 再次投币事件监听器(在已投币状态下再次投币)
@Component
public class InsertCoinAgainAfterCoinInsertedEventListener implements ApplicationListener<InsertCoinAgainAfterCoinInsertedEvent> {

    @Override
    public 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> {

    @Override
    public void onEvent(ReportErrorEvent event) {
        VendingMachine vendingMachine = (VendingMachine) event.getSource();
        event.handleEvent(vendingMachine);
        System.out.println("已成功监听并处理报告故障事件。");
    }
}

// 补货事件监听器
@Component
public class RefillStockEventListener implements ApplicationListener<RefillStockEvent> {

    @Override
    public 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;

    @Autowired
    private 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);
    }

    @Bean
    public 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 {
    @Override
    public VendingMachineState handle(VendingMachine vendingMachine) {
        System.out.println("已投币,进入已投币状态。");
        vendingMachine.setCurrentState(VendingMachineState.COIN_INSERTED);
        return VendingMachineState.COIN_INSERTED;
    }
}
已投币状态选择商品策略类
public class CoinInsertedSelectProductStrategy implements StateMachineStrategy {
    @Override
    public VendingMachineState handle(VendingMachine vendingMachine) {
        System.out.println("开始选择商品,进入正在选择商品状态。");
        vendingMachine.setCurrentState(VendingMachineState.SELECTING_PRODUCT);
        return VendingMachineState.SELECTING_PRODUCT;
    }
}
正在选择商品状态出货策略类
public class SelectingProductDispenseProductStrategy implements StateMachineStrategy {
    @Override
    public 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 {
    @Override
    public 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 {
    @Override
    public 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 {
    @Override
    public 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 {
    @Override
    public 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 {
    @Override
    public 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 {
    @Override
    public 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);
相关推荐
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭19 分钟前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
暮湫36 分钟前
泛型(2)
java
超爱吃士力架40 分钟前
邀请逻辑
java·linux·后端
南宫生1 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
转码的小石1 小时前
12/21java基础
java
李小白661 小时前
Spring MVC(上)
java·spring·mvc
m0_748235241 小时前
前端实现获取后端返回的文件流并下载
前端·状态模式
GoodStudyAndDayDayUp1 小时前
IDEA能够从mapper跳转到xml的插件
xml·java·intellij-idea
装不满的克莱因瓶2 小时前
【Redis经典面试题六】Redis的持久化机制是怎样的?
java·数据库·redis·持久化·aof·rdb
n北斗2 小时前
常用类晨考day15
java