COLA下的cola-statemachine状态机

相比Spring statemachine状态机等的复杂,功能多;我们更需要常用的功能,简单使用,所以这类就显得不简洁;再看cola-statemachine相比就是小巧、无状态、简单、轻量、性能极高的状态机DSL实现,解决业务中的状态流转问题。
github:
https://github.com/alibaba/COLA/tree/master/cola-components/cola-component-statemachine

概念:
State:状态

Event:事件,状态由事件触发,引起变化

Transition:流转,表示从一个状态到另一个状态

External Transition:外部流转,两个不同状态之间的流转

Internal Transition:内部流转,同一个状态之间的流转

Condition:条件,表示是否允许到达某个状态

Action:动作,到达某个状态之后,可以做什么

StateMachine:状态机

外部过程描述:起始状态STATE1,结束状态STATE2,当发生EVENT1时执行状态转移,当满足checkCondition()时,执行doAction,执行成功则返回状态STATE2,否则返回STATE1。

public class StateMachineTest {

    static String MACHINE_ID = "TestStateMachine";

    static enum States {
        STATE1, STATE2, STATE3, STATE4
    }

    static enum Events {
        EVENT1, EVENT2, EVENT3, EVENT4, INTERNAL_EVENT
    }

    static class Context{
        String operator = "frank";
        String entityId = "123465";
    }

    @Test
    public void testExternalNormal(){
        // 第一步:生成一个状态机builder
        StateMachineBuilder builder = StateMachineBuilderFactory.create();
        // 第二步:设置一个外部状态转移类型的builder,并设置from\to\on\when\perform
        builder.externalTransition()
                .from(States.STATE1)
                .to(States.STATE2)
                .on(Events.EVENT1)
                .when(checkCondition())
                .perform(doAction()); //这个action 我们可以按自己所需修改,比如这种Action service的方法Service::method
 
        // 第三步:设置状态机的id和ready,并在StateMachineFactory中的stateMachineMap进行注册
        StateMachine stateMachine = builder.build(MACHINE_ID);
        // 第四步:触发状态机
        States target = stateMachine.fireEvent(States.STATE1, Events.EVENT1, new Context());
        Assert.assertEquals(States.STATE2, target);
    }
}

源码分析:这里只是简单介绍,整个流转的链路,因为具体的代码还是要根据自己的知识储备理解;
第一步:生成一个状态机builder
new com.alibaba.cola.statemachine.builder.StateMachineBuilderImpl

    /**
     * StateMap is the same with stateMachine, as the core of state machine is holding reference to states.
     */
    private final Map> stateMap = new ConcurrentHashMap<>();
    private final StateMachineImpl stateMachine = new StateMachineImpl<>(stateMap);

第二步:设置一个外部状态转移类型的builder,并设置from\to\on\when\perform

@Override
    public ExternalTransitionBuilder externalTransition() {
        return new TransitionBuilderImpl<>(stateMap, TransitionType.EXTERNAL);
    }

TransitionBuilderImpl为多实现接口实现类,这个设计加上不同接口的唯一方法实现,可以让from to ,按DSL方式运行

class TransitionBuilderImpl implements ExternalTransitionBuilder, InternalTransitionBuilder, From, On, To {

image.png

上述构建就是一个状态流转的过程

class TransitionBuilderImpl implements ExternalTransitionBuilder, InternalTransitionBuilder, From, On, To {
    //状态机
    final Map> stateMap;
    //原状态
    private State source;
    //目标状态
    protected State target;
    //该具体做事流转
    private Transition transition;
     //流转类型
    final TransitionType transitionType;
...
    // 此时只是把from的state新增到stateMap中,返回结果赋给本地变量source
    @Override
    public From from(S stateId) {
        source = StateHelper.getState(stateMap, stateId);
        return this;
    }
 
    // 此时只是把to的state新增到stateMap中,返回结果赋给本地变量target
    @Override
    public To to(S stateId) {
        target = StateHelper.getState(stateMap, stateId);
        return this;
    }
  //所以ON是在Form和to后面执行,因为它用本流程中的source,向来源state中加入Transition
  //Transition是一个具体做事的流转,其中包含事件,目标状态和流转类型
  //看下面源码就是一个事件对应Transition
  @Override
    public On on(E event) {
        transition = source.addTransition(event, target, transitionType);
        return this;
    }
//该执行流转的条件
@Override
    public When when(Condition condition) {
        transition.setCondition(condition);
        return this;
    }
//设置transition的action
 @Override
    public void perform(Action action) {
        transition.setAction(action);
    }

...
}

第三步:
builder.build(MACHINE_ID) 此步就是状态机已构建完成,一般直接@Bean注入,使用时直接调用


public class StateMachineBuilderImpl implements StateMachineBuilder {
 
...
 
    private final StateMachineImpl stateMachine = new StateMachineImpl<>(stateMap);
 
...
 
    // 设置状态机的id和ready,并在StateMachineFactory中的stateMachineMap进行注册
    @Override
    public StateMachine build(String machineId) {
        stateMachine.setMachineId(machineId);
        stateMachine.setReady(true);
        StateMachineFactory.register(stateMachine);
        return stateMachine;
    }
 
}

第四步:States target = stateMachine.fireEvent(States.STATE1, Events.EVENT1, new Context()); 业务具体使用时,这个方法入参是原状态和事件和action的入参,这个就按上面配置的状态机进行流转;from to on when perform

public class StateMachineImpl implements StateMachine {

    private String machineId;

    private final Map> stateMap;

    private boolean ready;

    public StateMachineImpl(Map> stateMap){
        this.stateMap = stateMap;
    }

    public S fireEvent(S sourceStateId, E event, C ctx){
        isReady();
        State sourceState = getState(sourceStateId);
        return doTransition(sourceState, event, ctx).getId();
    }

    private State doTransition(State sourceState, E event, C ctx) {
        //根据原状态和事件获取流转transition
        Optional> transition = sourceState.getTransition(event);
        if(transition.isPresent()){
            //ctx为执行入参
            return transition.get().transit(ctx);
        }
        Debugger.debug("There is no Transition for " + event);
        return sourceState;

Transition是具体做事的流转

/**
 * TransitionImpl。
 *
 * This should be designed to be immutable, so that there is no thread-safe risk
 *
 * @author Frank Zhang
 * @date 2020-02-07 10:32 PM
 */
public class TransitionImpl implements Transition {

    private State source;

    private State target;

    private E event;

    private Condition condition;

    private Action action;

    private TransitionType type = TransitionType.EXTERNAL;
    
...
 @Override
    public State transit(C ctx) {
        Debugger.debug("Do transition: "+this);
        this.verify();
        if(condition == null || condition.isSatisfied(ctx)){
            if(action != null){
                action.execute(source.getId(), target.getId(), event, ctx);
            }
            return target;
        }

        Debugger.debug("Condition is not satisfied, stay at the "+source+" state ");
        return source;
    }
...
    @Override
    public final String toString() {
        return source + "-[" + event.toString() +", "+type+"]->" + target;
    }

    @Override
    public boolean equals(Object anObject){
        if(anObject instanceof Transition){
            Transition other = (Transition)anObject;
            if(this.event.equals(other.getEvent())
                    && this.source.equals(other.getSource())
                    && this.target.equals(other.getTarget())){
                return true;
            }
        }
        return false;
    }

    @Override
    public void verify() {
        if(type== TransitionType.INTERNAL && source != target) {
            throw new StateMachineException(String.format("Internal transition source state '%s' " +
                    "and target state '%s' must be same.", source, target));
        }
    }

State是整个流转中状态类

public interface State extends Visitable{

    /**
     * Gets the state identifier.
     *
     * @return the state identifiers
     */
    S getId();

    /**
     * Add transition to the state
     * @param event the event of the Transition
     * @param target the target of the transition
     * @return
     */
    Transition addTransition(E event, State target, TransitionType transitionType);

    Optional> getTransition(E event);

    Collection> getTransitions();

}
public class StateImpl implements State {
    protected final S stateId;
    private HashMap> transitions = new HashMap<>();

    StateImpl(S stateId){
        this.stateId = stateId;
    }

    @Override
    public Transition addTransition(E event, State target, TransitionType transitionType) {
        Transition newTransition = new TransitionImpl<>();
        newTransition.setSource(this);
        newTransition.setTarget(target);
        newTransition.setEvent(event);
        newTransition.setType(transitionType);

        Debugger.debug("Begin to add new transition: "+ newTransition);
        verify(event, newTransition);
        transitions.put(event, newTransition);
        return newTransition;
    }
 /**
     * @param event
     * @param newTransition
     */
    private void verify(E event, Transition newTransition) {
        Transition existingTransition = transitions.get(event);
        if(existingTransition != null){
            if(existingTransition.equals(newTransition)){
                throw new StateMachineException(existingTransition+" already Exist, you can not add another one");
            }
        }
    }

最后用层级展示下源码的设计:存储下来类似树形分支
第一维度:状态
第二维度:原状态 对应 事件
第三维度:事件对应具体流转


image.png

你可能感兴趣的:(COLA下的cola-statemachine状态机)