简易轻量级有限状态机
Spring StateMachine 功能强大,适用于较复杂的场景,理解起来有一定难度,对于简单的场景,使用起来相对繁琐。经过一番查阅和了解,并不能找到适合我当前工作场景的成熟框架,因此决定自己写一个。
组件构成:
1)基础组件:事件、状态、状态转换
2)状态机接口:定义状态机具备的行为
3)一个简单的有限状态机实现:SimpleFSMFrame
以下是代码示例:
public interface State { /** * 状态名称 * * @return */ String name(); } public interface Event { /** * 事件名称 * * @return */ String name(); } public interface Transition { /** * 源状态 * * @return */ State getSource(); /** * 目标状态 * * @return */ State getTarget(); /** * 事件 * * @return */ Event getEvent(); }
import java.util.List; /** * 状态机 */ public interface StateMachine { /** * 在当前状态下执行某个事件 * * @param event 事件 * @return 若执行成功则返回变更后的新状态 * @throws UnsupportedOperationException 如果当前状态不支持该操作则抛出此异常 */ State onEvent(Event event) throws UnsupportedOperationException; /** * 当前的状态 * * @return */ State getState(); /** * 当前状态可执行的事件清单 * * @return */ List<Event> acceptableEvents(); /** * 当前状态是否可以执行指定的某个事件(仅给出是否允许执行的判断结论,不会真的执行) * * @param event 事件 * @return */ boolean canPerformEvent(Event event); }
import java.util.*; import java.util.stream.Collectors; /** * 轻量级的状态机框架,通过集成此类可快速实现一个简易的有限状态机。 * <br> * 线程安全 */ public class SimpleFSMFrame implements StateMachine { /** * 存放有当前状态机中的状态与事件转换关系的box */ private final TransitionBox transitionBox; /** * 状态机当前状态 */ private State state; /** * 初始化一个状态机 * * @param initialState 初始状态 * @param transitions 状态与事件之间的转换关系 */ public SimpleFSMFrame(State initialState, Transition[] transitions) { state = initialState; this.transitionBox = new TransitionBox(transitions); } @Override synchronized public State onEvent(Event event) throws UnsupportedOperationException { state = execute(state, event); return state; } @Override public State getState() { return state; } @Override public List<Event> acceptableEvents() { return acceptableEvents(state); } @Override public boolean canPerformEvent(Event event) { return canPerformEvent(state, event); } /** * 在指定状态下执行某个事件,执行成功返回变更后的新状态 * * @param currentState 状态 * @param event 事件 * @return 变更后的新状态 * @throws UnsupportedOperationException 如果当前状态不支持该操作则抛出此异常 */ private State execute(State currentState, Event event) throws UnsupportedOperationException { List<Transition> transitions = transitionBox.getTransitionBySource(currentState); return transitions .stream() .filter(transition -> transition.getEvent().equals(event)) .findAny() .orElseThrow(() -> new UnsupportedOperationException("Event:" + event.name() + " can not be performed on State:" + currentState.name())) .getTarget(); } /** * 当前状态可执行的事件清单 * * @param state 状态 * @return */ private List<Event> acceptableEvents(State state) { List<Transition> transitions = transitionBox.getTransitionBySource(state); return transitions .stream() .map(transition -> transition.getEvent()) .collect(Collectors.toList()); } /** * 当前状态是否可以执行指定的某个事件(仅给出是否允许执行的判断结论,不会真的执行) * * @param state 状态 * @param event 事件 * @return */ private boolean canPerformEvent(State state, Event event) { List<Transition> transitions = transitionBox.getTransitionBySource(state); return transitions .stream() .anyMatch(transition -> transition.getEvent().equals(event)); } /** * 检验状态与事件转换关系是否合法 * * @param transitions * @throws IllegalArgumentException 如果校验不通过则抛出此异常 */ private void verifyTransition(Transition[] transitions) throws IllegalArgumentException { //检查源状态+事件不能重复 Set<String> set = new HashSet<>(); for (Transition transition : transitions) { String key = transition.getSource().name() + transition.getEvent().name(); boolean flag = set.add(key); if (!flag) throw new IllegalArgumentException(String.format("reduplicate transition source=%s event=%s", transition.getSource().name(), transition.getEvent().name())); } } /** * 存放整理后的状态与事件转换关系,并提供相应的访问方法 */ private class TransitionBox { private Map<State, List<Transition>> sourceMap = new HashMap<>(); private Map<State, List<Transition>> targetMap = new HashMap<>(); private Map<Event, List<Transition>> eventMap = new HashMap<>(); /** * 根据状态与事件的转换关系初始化一个box * * @param transitions 状态与事件的转换关系 */ public TransitionBox(Transition[] transitions) { //校验转换关系是否存在异常情况,如果存在则抛出异常 verifyTransition(transitions); for (Transition transition : transitions) { //sourceMap List<Transition> sourceList = sourceMap.get(transition.getSource()); if (sourceList == null) { sourceList = new ArrayList<>(); sourceMap.put(transition.getSource(), sourceList); } sourceList.add(transition); //targetMap List<Transition> targetList = targetMap.get(transition.getTarget()); if (targetList == null) { targetList = new ArrayList<>(); targetMap.put(transition.getTarget(), targetList); } targetList.add(transition); //eventMap List<Transition> eventList = eventMap.get(transition.getEvent()); if (eventList == null) { eventList = new ArrayList<>(); eventMap.put(transition.getEvent(), eventList); } eventList.add(transition); } } /** * 获取指定源状态的所有转换关系 * * @param source 源状态 * @return */ public List<Transition> getTransitionBySource(State source) {
List<Transition> list = sourceMap.get(source); return list != null ? list : new ArrayList<>(); } /** * 获取指定目标状态的所有转换关系 * * @param target 目标状态 * @return */ public List<Transition> getTransitionByTarget(State target) {
List<Transition> list = targetMap.get(target); return list != null list : new ArrayList<>(); } /** * 获取与指定事件相关的所有转换关系 * * @param event 事件 * @return */ public List<Transition> getTransitionByEvent(Event event) {
List<Transition> list = eventMap.get(event); return list != null ? list : new ArrayList<>(); } } }
用法:实现基础组件中的接口,并继承SimpleFSMFrame类即可快速实现一个状态机。
public enum States implements State { OPEN, CLOSED; } public enum Events implements Event { OPEN, CLOSE; } public class MyStateMachine extends SimpleFSMFrame { public MyStateMachine(State initialState) { super(initialState, Transitions.values()); } @Getter private enum Transitions implements Transition { T1(States.CLOSED, Events.OPEN, States.OPEN), T2(States.OPEN, Events.CLOSE, States.CLOSED), ; private final State source; private final State target; private final Event event; Transitions(State source, Event event, State target) { this.source = source; this.target = target; this.event = event; } } }
调用:
//创建一个带初始状态的状态机 StateMachine statemachine = new MyStateMachine(States.CLOSED); //发送事件,获得变更后的状态 State state = statemachine.onEvent(Events.OPEN);
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)