Spring StateMachine 功能强大,适用于较复杂的场景,理解起来有一定难度,对于简单的场景,使用起来相对繁琐。经过一番查阅和了解,并不能找到适合我当前工作场景的成熟框架,因此决定自己写一个。
组件构成:
1)基础组件:事件、状态、状态转换
2)状态机接口:定义状态机具备的行为
3)一个简单的有限状态机实现:SimpleFSMFrame
用法:实现基础组件中的接口,并继承SimpleFSMFrame类即可快速实现一个状态机。
以下是代码示例:
/** * 可以触发状态发生变化的事件 */ public interface Event { /** * 事件名称 * * @return */ String name(); }事件
/** * 状态 */ public interface State { /** * 状态名称 * * @return */ String name(); }状态
/** * 状态与事件之间的转换关系 */ public interface Transition { /** * 源状态 * * @return */ State getSource(); /** * 目标状态 * * @return */ State getTarget(); /** * 事件 * * @return */ Event getEvent(); }状态转换
/** * 状态机 */ 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 canApplyEvent(Event event); }状态机接口
import org.apache.commons.lang3.StringUtils; 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 canApplyEvent(Event event) { return canApplyEvent(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(UnsupportedOperationException::new) .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 canApplyEvent(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 = StringUtils.join(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) { return sourceMap.get(source); } /** * 获取指定目标状态的所有转换关系 * * @param target 目标状态 * @return */ public List<Transition> getTransitionByTarget(State target) { return targetMap.get(target); } /** * 获取与指定事件相关的所有转换关系 * * @param event 事件 * @return */ public List<Transition> getTransitionByEvent(Event event) { return eventMap.get(event); } } }简单的有限状态机实现
使用案例:
实现自定义状态机//创建一个带初始状态的状态机 StateMachine statemachine = new MyStateMachine(States.CLOSED); //发送事件,获得变更后的状态 State state = statemachine.onEvent(Events.OPEN);使用
SimpleFSMFrame