简易轻量级有限状态机

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

上一篇:温故知新之(六)图片模糊处理、transition过渡、动画animation


下一篇:九宫格布局,选中占4个元素位置