当前位置: 首页 > news >正文

状态模式之状态机

状态机的背景

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

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

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

相关文章:

状态模式之状态机

状态机的背景 在软件开发过程中&#xff0c;尤其是涉及到复杂的系统行为控制时&#xff0c;我们常常会遇到这样的情况&#xff1a;一个对象或者系统会在多种状态之间进行转换&#xff0c;并且在不同状态下对相同事件的响应是不同的。 以自动售卖机为例&#xff0c;自动售卖机…...

NUXT3学习日记四(路由中间件、导航守卫)

前言 在 Nuxt 3 中&#xff0c;中间件&#xff08;Middleware&#xff09;是用于在页面渲染之前或导航发生之前执行的函数。它们允许你在路由切换时执行逻辑&#xff0c;像是身份验证、重定向、权限控制、数据预加载等任务。中间件可以被全局使用&#xff0c;也可以只在特定页…...

基于重复控制补偿的高精度 PID 控制

1. 背景与原理 重复控制&#xff08;Repetitive Control, RC&#xff09;是一种适用于周期性信号跟踪和周期性扰动抑制的控制方法&#xff0c;通过在控制回路中引入周期补偿器来提高系统的控制精度。将 RC 与 PID 控制相结合&#xff0c;利用 PID 的快速响应特性和 RC 的周期补…...

Linux之日志

日志 在编写网络服务器, 各种软件时, 程序一定要打印一些日志信息. 1. 可以向显示器打印, 也可以向文件中写入. 2. 日志是软件在运行时记录的流水账, 用于排查服务进程挂掉的信息. 其中必须要有的是: 日志等级, 时间, 日志内容.可选的是文件名, 代码行数, 进程pid 等 日志…...

【LeetCode面试150】——202快乐数

博客昵称&#xff1a;沈小农学编程 作者简介&#xff1a;一名在读硕士&#xff0c;定期更新相关算法面试题&#xff0c;欢迎关注小弟&#xff01; PS&#xff1a;哈喽&#xff01;各位CSDN的uu们&#xff0c;我是你的小弟沈小农&#xff0c;希望我的文章能帮助到你。欢迎大家在…...

云原生之k8s服务管理

文章目录 服务管理Service服务原理ClusterIP服务 对外发布应用服务类型NodePort服务Ingress安装配置Ingress规则 Dashboard概述 认证和授权ServiceAccount用户概述创建ServiceAccount 权限管理角色与授权 服务管理 Service 服务原理 容器化带来的问题 自动调度&#xff1a;…...

【Vue】 npm install amap-js-api-loader指南

前言 项目中的地图模块突然打不开了 正文 版本太低了&#xff0c;而且Vue项目就应该正经走项目流程啊喂&#xff01; npm i amap/amap-jsapi-loader --save 官方说这样执行完&#xff0c;就这结束啦&#xff01;它结束了&#xff0c;我还没有&#xff0c;不然不可能记录这篇文…...

RocketMQ: 部署结构与存储特点

RocketMQ 是什么 它是一个队列模型的消息中间件&#xff0c;具有高性能、高可靠、高实时、分布式特点 Producer、Consumer、队列都可以分布式Producer 向一些队列轮流发送消息 队列集合称为 TopicConsumer 如果做广播消费则一个 consumer 实例消费这个 Topic 对应的所有队列如果…...

Android OpenGL ES详解——绘制圆角矩形

1、绘制矩形 代码如下&#xff1a; renderer类&#xff1a; package com.example.roundrectimport android.content.Context import android.opengl.GLES30 import android.opengl.GLSurfaceView.Renderer import com.opengllib.data.VertexArray import com.opengllib.prog…...

【大数据学习 | Spark】Spark的改变分区的算子

当分区由多变少时&#xff0c;不需要shuffle&#xff0c;也就是父RDD与子RDD之间是窄依赖。 当分区由少变多时&#xff0c;是需要shuffle的。 但极端情况下&#xff08;1000个分区变成1个分区)&#xff0c;这时如果将shuffle设置为false&#xff0c;父子RDD是窄依赖关系&…...

前端知识点---rest(javascript)

文章目录 前端知识点---rest(javascript)rest的用法基本语法特点使用场景与扩展运算符&#xff08;spread&#xff09;区别小练习 前端知识点—rest(javascript) rest出现于ES2015 function doSum(a,b, ...args) //示例中的args就是一个rest参数 //它会将后续的所有参数存储…...

Spark使用过程中的 15 个常见问题、详细解决方案

目录 问题 1&#xff1a;Spark 作业超时问题描述解决方案Python 实现 问题 2&#xff1a;内存溢出问题描述解决方案Python 实现 问题 3&#xff1a;Shuffle 性能问题问题描述解决方案Python 实现 问题 4&#xff1a;Spark 作业调度不均问题描述解决方案Python 实现 问题 5&…...

ASP.NET MVC宠物商城系统

该系统采用B/S架构&#xff0c;使用C#编程语言进行开发&#xff0c;以ASP.NET MVC框架为基础&#xff0c;以Visual Studio 2019为开发工具&#xff0c;数据库采用SQL Server进行保存数据。系统主要功能包括登录注册、宠物展示、个人中心、我的订单、购物车、用户管理、宠物类别…...

DQN系列算法详解

代码链接见文末 1. Q-learning 1.1 概述 Q-Learning是一种强化学习算法,目的是通过选择能带来最大长期收益的行为来完成任务。 做事包含瞬时奖励和记忆经验奖励: 在Q-Learning中,每个动作都会带来“瞬时奖励”,同时也会根据过去的经验记住哪些行为更有利。瞬时奖励: 这里…...

uniapp发布android上架应用商店权限

先看效果&#xff1a; 实现原理&#xff1a; 一、利用uni.addInterceptor的拦截器&#xff0c;在一些调用系统权限前拦截&#xff0c;进行弹窗展示&#xff0c;监听确定取消实现业务逻辑。 二、弹窗是原生nativeObj进行drawRect绘制的 三、权限申请调用使用的 plus.android.…...

已阻止加载“http://localhost:8086/xxx.js”的模块,它使用了不允许的 MIME 类型 (“text/plain”)。

记录今天解决的一个小bug 在终端启动8080端口号监听后&#xff0c;打开网址http://localhost:8080&#xff0c;发现不能正确加载页面&#xff0c;打开检查-控制台&#xff0c;出现如下警告&#xff1a;已阻止加载“http://localhost:8086/xxx.js”的模块&#xff0c;它使用了不…...

JavaEE之线程初阶(上)

前文我们知道计算机中的每一个程序都对应着一个进程&#xff0c;进程是CPU申请资源的最小单位&#xff0c;那么线程是什么呢&#xff0c;线程中我们又能学习到什么新的知识呢&#xff1f;&#xff1f; 我们来一探究竟 1. 认识线程&#xff08;Thread&#xff09; 线程是什么 …...

Spring Security @PreAuthorize注解

PreAuthorize 注解在 Spring Security 中提供了一种声明式的方法&#xff0c;可以在您的 Spring Boot 应用中添加方法级别的安全检查。本教程将引导您设置并有效使用 PreAuthorize&#xff0c;确保用户只能在具有特定角色或权限的情况下调用 REST API。 什么是 PreAuthorize&a…...

windows已建立威胁IP排查

在应急响应的时候&#xff0c;需要筛选出服务器建立连接的进程、PID&#xff0c;此代码可满足该需求实现共计2步 首先windos netstat-ano > all.txt&#xff0c; 上传至pycharm路径 第一步获取服务器建立连接的ip import re# 从文件读取 netstat 输出 def read_netstat_f…...

AI 大模型如何重塑软件开发流程?——技术革新与未来展望

人工智能的蓬勃发展为许多领域注入了强劲动力&#xff0c;而在软件开发这一关键技术领域&#xff0c;AI 大模型的应用正在彻底改变传统流程。从代码自动生成到智能测试&#xff0c;再到协同开发和流程优化&#xff0c;AI 正逐步成为软件开发者的得力助手&#xff0c;也推动企业…...

Admin.NET框架前端由于keep-alive设置缓存导致的onUnmount未触发问题

bug版本&#xff1a;next分支&#xff0c;基于.NET6版本&#xff1b; 问题描述&#xff1a; 1、添加keep-alive后&#xff0c;在其下运行的组件会出现onActived(被关注时)和onDeactived(取消关注时)生命周期&#xff0c;而组件原有生命周期为onMounted(被创造时)和onUnmounted(…...

Rest-assured

依赖 <--rest-assured依赖--><dependency><groupId>io.rest-assured</groupId><artifactId>rest-assured</artifactId><version>4.4.0</version><scope>test</scope> </dependency><--junit5依赖-->…...

ubuntu24挂载硬盘记录

1、显示硬盘及所属分区情况。在终端窗口中输入如下命令&#xff1a; sudo fdisk -l 找到自己硬盘的分区 我的地址/dev/sda 2、显示硬盘及所属分区情况。在终端窗口中输入如下命令&#xff0c;格式化自己硬盘&#xff1a; sudo mkfs -t ext4 /dev/sda 3、在终端窗口中输入如下…...

Dubbo集成SpringBoot实现远程服务调用

SpringBoot集成Dubbo Dubbo介绍了解 Dubbo 核心概念和架构dubbo特性dubbo运行原理图 SpringBoot集成Dubbo技术实战一、Dubbo Spring Boot 版本关系二、引入Maven依赖demo项目基础结构引入依赖创建每个模块1&#xff09;api模块2&#xff09;provider模块3&#xff09;consumer模…...

Redis最终篇分布式锁以及数据一致性

在前三篇我们几乎说完了Redis的所有的基础知识以及Redis怎么实现高可用性,那么在这一篇文章中的话我们主要就是说明如果我们使用Redis出现什么问题以及解决方案是什么,这个如果在未来的工作中也有可能会遇到,希望对看这篇博客的人有帮助,话不多说直接开干 一.Hotkey以及BigKey…...

TCP协议

报文格式 源/目的端口号&#xff1a;数据从哪个进程来&#xff0c;到哪个进程去32位序号&#xff1a;TCP传输过程中&#xff0c;在发送端出的字节流中&#xff0c;传输报文中的数据部分的每一个字节都有它的编号32位确认号&#xff1a;标识了报文接收端期望接收的字节序列4位首…...

Vue3 源码解析(十):watch 的实现原理

本篇文章笔者会讲解 Vue3 中侦听器相关的 api&#xff1a;watchEffect 和 watch 。在 Vue3 之前 watch 是 option 写法中一个很常用的选项&#xff0c;使用它可以非常方便的监听一个数据源的变化&#xff0c;而在 Vue3 中随着 Composition API 的写法推行也将 watch 独立成了一…...

2023年9月GESPC++一级真题解析

一、单选题&#xff08;每题2分&#xff0c;共30分&#xff09; 题号 123456789101112131415 答案 CDBCDBACACBBDDA 1. 我们通常说的 “ 内存 ” 属于计算机中的&#xff08;&#xff09;。 A. 输出设备 B. 输 ⼊ 设备 C. 存储设备 D. 打印设备 【答案】 C 【考纲知识点】…...

vscode 远程连接ssh 密钥方式

目录 1. powershell 生成key&#xff1a; 2. 在服务器上安装公钥 linux测试成功&#xff1a; 3).为了确保连接成功&#xff0c;输入如下指令以保证以下文件权限正确&#xff1a; 3 开启 ssh 密钥登录 vscode 远程连接配置 python连接测试ok 查看日志&#xff1a; 1. po…...

字符函数和字符串函数

字符分类函数 C语言中有⼀系列的函数是专门做字符分类的&#xff0c;也就是⼀个字符是属于什么类型的字符的。 这些函数的使用都需要包含⼀个头文件&#xff1a;ctype.h 这些函数的用法非常类似。 int islower ( int c )islower是能够判断参数部分是否是小写字母的。 通过返…...

如何利用ChatGPT加速开发与学习:以BPMN编辑器为例

在现代开发中&#xff0c;开发者经常会遇到各种需要编写和学习新技术的任务。ChatGPT作为一种强大的自然语言处理工具&#xff0c;不仅可以辅助编写代码&#xff0c;还可以帮助学习新的编程概念和解决开发中的难题。本文将以开发一个BPMN&#xff08;业务流程建模与标注&#x…...

深度学习2

四、tensor常见操作 1、元素值 1.1、获取元素值 tensor.item() 返回tensor的元素&#xff1b;只能在一个元素值使用&#xff0c;多个报错&#xff0c;当存在多个元素值时需要使用索引进行获取到一个元素值时在使用 item。 1.2、元素值运算 tensor对元素值的运算&#xff1a;…...

工业生产安全-安全帽第二篇-用java语言看看opencv实现的目标检测使用过程

一.背景 公司是非煤采矿业&#xff0c;核心业务是采选&#xff0c;大型设备多&#xff0c;安全风险因素多。当下政府重视安全&#xff0c;头部技术企业的安全解决方案先进但价格不低&#xff0c;作为民营企业对安全投入的成本很敏感。利用我本身所学&#xff0c;准备搭建公司的…...

单片机学习笔记 9. 8×8LED点阵屏

更多单片机学习笔记&#xff1a;单片机学习笔记 1. 点亮一个LED灯单片机学习笔记 2. LED灯闪烁单片机学习笔记 3. LED灯流水灯单片机学习笔记 4. 蜂鸣器滴~滴~滴~单片机学习笔记 5. 数码管静态显示单片机学习笔记 6. 数码管动态显示单片机学习笔记 7. 独立键盘单片机学习笔记 8…...

c++ 力扣题(1)JZ64

JZ64求123...n_牛客题霸_牛客网 因此不能使用等差求和&#xff08;禁掉位运算为了禁掉等差求和&#xff0c;右移一位就是等差除二&#xff09;、循环、递归来解决 我们运用到在 统计构造次数所学到的内容&#xff1a; 1.可以利用静态全局变量的思想来做 2.构造n次对象&…...

logback动态获取nacos配置

文章目录 前言一、整体思路二、使用bootstrap.yml三、增加环境变量四、pom文件五、logback-spring.xml更改总结 前言 主要是logback动态获取nacos的配置信息,结尾完整代码 项目springcloudnacosplumelog&#xff0c;使用的时候、特别是部署的时候&#xff0c;需要改环境&#…...

基于零相差前馈补偿的 PID 控制

零相差前馈补偿是一种结合前馈补偿与反馈控制的策略&#xff0c;旨在提高控制系统对参考信号的跟踪精度。通过设计合理的前馈补偿器&#xff0c;使得系统对参考输入实现零相位差的跟踪&#xff0c;同时利用 PID 控制器保证系统的稳定性和动态性能。 1. 原理概述 目标&#xff…...

任务通知的本质(任务通知车辆运行) 软件定时器的本质(增加游戏音效)

任务通知的本质 没有任务通知 所谓"任务通知"&#xff0c;你可以反过来读"通知任务"。 我们使用队列、信号量、事件组等等方法时&#xff0c;并不知道对方是谁。使用任务通知时&#xff0c;可 以明确指定&#xff1a;通知哪个任务。 使用队列、信号量、…...

如何在 MySQL 中进行数据导入和导出?

在 MySQL 中进行数据的导入和导出是一项常见的任务&#xff0c;用于数据备份、恢复、迁移以及数据分析等多种用途。MySQL 提供了多种方法来进行数据的导入和导出&#xff0c;每种方法都有其适用的场景和特点。以下是几种常用的 MySQL 数据导入和导出方法&#xff0c;包括命令行…...

python语言基础-5 进阶语法-5.3 流式编程

声明&#xff1a;本内容非盈利性质&#xff0c;也不支持任何组织或个人将其用作盈利用途。本内容来源于参考书或网站&#xff0c;会尽量附上原文链接&#xff0c;并鼓励大家看原文。侵删。 5.3 流式编程&#xff08;参考链接&#xff1a;https://www.zhihu.com/question/59062…...

centos 服务器 docker 使用代理

宿主机使用代理 在宿主机的全局配置文件中添加代理信息 vim /etc/profile export http_proxyhttp://127.0.0.1:7897 export https_proxyhttp://127.0.0.1:7897 export no_proxy"localhost,127.0.0.1,::1,172.171.0.0" docker 命令使用代理 例如我想在使用使用 do…...

[个人专属博客] - docker安装

&#x1f389;&#x1f389;&#x1f389;&#x1f389;&#x1f389;&#x1f389; 欢迎访问的个人博客&#xff1a;http://swzbk.site/&#xff0c;加好友&#xff0c;一起赚&#x1f9e7;&#x1f9e7;&#x1f9e7; &#x1f389;&#x1f389;&#x1f389;&#x1f389;&…...

推荐一个QDirStat基于 Qt 的目录统计工具

QDirStat 是一款基于 Qt 的目录统计工具&#xff0c;旨在帮助用户分析磁盘空间使用情况并进行清理。QDirStat的一些主要特点和功能&#xff1a; 跨平台兼容性&#xff1a;QDirStat 支持 Linux、BSD、Unix-like 系统以及 macOS&#xff0c;确保了广泛的用户基础。 直观的数据展…...

yolo自动化项目实例解析(九) 导航

比如我们经常使用的导航&#xff0c;说白了就是寻找两点之间最近的路径&#xff0c;也就是所谓的寻路&#xff0c;我们需要想办法让程序知道他要去哪里&#xff0c;路径包含&#xff08;起点、轨迹、终点&#xff09; 一、录制轨迹 从平面角度来看&#xff0c;我们可以把区域视…...

MySQL 报错:1137 - Can‘t reopen table

MySQL 报错&#xff1a;1137 - Can’t reopen table 1. 问题 对临时表查询&#xff1a; select a.ts_code,a.tsnum,b.tsnum from (select t.ts_code ,count(*) tsnum from tmp_table t group by t.ts_code having count(*) > 20 and count(*)< 50 ) a ,(select t.ts_…...

Bokeh实现大规模数据可视化的最佳实践

目录 引言 一、Bokeh简介 二、安装Bokeh 三、数据准备 四、性能优化 五、创建图表 六、添加交互功能 七、应用案例 八、高级技巧 九、总结 引言 在数据科学领域,数据可视化是一个至关重要的环节。通过可视化,我们可以直观地理解数据的特征和趋势,为数据分析和决策…...

HTMLCSS:比赛记分卡

效果演示 这段 HTML 和 CSS 代码创建了一个卡片式的体育比赛信息展示组件&#xff0c;用于显示篮球比赛的两个队伍名称、比赛时间、比分以及一些装饰性的视觉元素。 HTML <div class"card"><div data-status"inprogress" class"teams"…...

什么是 Faiss?

好的&#xff0c;我来详细解释 Faiss&#xff0c;它的用途、使用场景&#xff0c;以及如何安装和使用。 什么是 Faiss&#xff1f; Faiss 是由 Facebook AI Research 开发的一个开源库&#xff0c;专门用于高效的相似性搜索和聚类。它非常擅长在高维向量空间中进行快速搜索&a…...

【prism】遇到一个坑,分享!

背景 我通用prism的方式写了一个弹窗,弹窗绑定一个 Loaded 事件,但是Loaded事件一直不触发!!! 具体过程 我的loaded事件也是通过命令的方式绑定的: <i:Interaction.Triggers><i:EventTrigger EventName="Loaded...

vue制作代码比较工具

前两天朋友问我 有没有vue可以做一个json代码在线比较工具 我也是在网上搜了一下找到的 废话不说 直接上代码 采用 v3 pnpm i v-code-diff <div><CodeDiff:old-string"oldStr":new-string"newStr"output-format"side-by-side"/>…...