Squirrel 状态机的使用说明

379 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第6天,点击查看活动详情

这篇文章我们来介绍Squirrel 状态机的一些入门知识。

Squirrel 状态机的核心模型

我们来对Squirrel 中的几个重要的类做一些介绍。

StateMachineBuilderFactory

这个类看名字就知道,它是StateMachineBuilder 的工厂类,用来解析状态的定义,根据状态定义创建对应的StateMachineBuilder。

StateMachineBuilder

它是StateMachine 的构造器,是可复用的。

所有由生成器创建相同的状态机实例,共享相同的状态定义。

StateMachine

这个就是真正的状态机实例类,它由StateMachineBuilder 创建,是轻量级内存实例,不可共享。

对于afterTransitionCausedException、beforeTransitionBegin、afterTransitionCompleted、afterTransitionEnd、afterTransitionDeclined beforeActionInvoked、afterActionInvoked 这些事件的自定义全局处理流程,它都是支持的。

Condition

Condition 可以认为是一种“条件”。Squirrel 状态机支持动态过渡(transition),针对同一个state,在接受相同的trigger 的时候,如果statecontext 不一样,那么到达的目标状态也是可以不一样的。

StateMachineListener

这是全局事件监听器,它包括TransitionBeginListener、TransitionCompleteListener、TransitionExceptionListener 等这几类用于监听transition 所处的不同阶段的监听器。

squirrel 状态机既支持流式API 创建,又支持声明式创建状态机。

针对的StateMachine 的接口,需要以下这四种类型的泛型参数:

  1. T 代表状态机类型
  2. S 代表状态类型(State)
  3. E 代表事件类型(Event)
  4. C 代表外部上下文类型(Context)

使用示例

具体的使用实例如下,相关的解释也放在代码中了,对读者来说应该是很直观的。

public class StateMachineTest {
    // 自定义需要的状态机的类型,包括State 枚举、Event 枚举、上下文Context
    // 这个状态机其实就是我们业务对象的"状态抽象"
    @StateMachineParameters(stateType=MyState.class, eventType=MyEvent.class, contextType=MyContext.class)
    static class ExampleMachine extends AbstractUntypedStateMachine {
        
        // 在自定义的状态机中可以定义很多方法,在状态机流转过程中完成调用

        // 通过方法名称来标识我们想完成的业务
        // 这里面的逻辑就是在状态机处于第一个状态中的时候,会调用的逻辑
        protected void inFirst(MyState from, MyState to, MyEvent event, MyContext context) {
            System.out.println("--------");
            System.out.println("in first stage");
            System.out.println("context string: " + context.contestString);
            System.out.println("from: " + from.name());
            System.out.println("---------");
        }

        // 打log 的
        protected void log(MyState from, MyState to, MyEvent event, MyContext context) {
            System.out.println("=======");
            System.out.println("in log");
            System.out.println("this state from: " + from.name());
            System.out.println("this state to: " + to.name());
            System.out.println("the event: " + event.name());
            System.out.println("transition success");
            System.out.println("=======");
        }

        protected void toSecond(MyState from, MyState to, MyEvent event, MyContext context) {
            System.out.println("=======");
            System.out.println("in ToSecond");
            System.out.println("this state from: " + from.name());
            System.out.println("this state to: " + to.name());
            System.out.println("the event: " + event.name());
            System.out.println("transition success");
            System.out.println("=======");
        }

        protected void toThird(MyState from, MyState to, MyEvent event, MyContext context) {
            System.out.println("=======");
            System.out.println("in ToForth");
            System.out.println("this state from: " + from.name());
            System.out.println("this state to: " + to.name());
            System.out.println("the event: " + event.name());
            System.out.println("transition success");
            System.out.println("=======");
        }
    }

    //自定义状态类型
    enum MyState {
        FIRST, SECOND, THIRD, FORTH
    }

    //自定义事件类型
    enum MyEvent {
        InFirst, ToSecond, ToThird, ToForth
    }

    //自定义外部上下文类型
    static class MyContext {
        String contestString = "no value";
    }

    public static void main(String[] args) {
        // 首先使用状态机builder 构造状态机
        // 这里调用的方法参数分别是构造的状态机、状态枚举、事件枚举、外部上下文(T/S/E/C)
        StateMachineBuilder builder = StateMachineBuilderFactory.create(ExampleMachine.class, MyState.class, MyEvent.class, MyContext.class);

        // 接下来定义几个状态流转的触发流程
        // 以下包含externalTransition 和internalTransition
        // internal transition 是在transition 完成之后,没有状态的变化,即没有旧状态的退出和新状态的进入

        // 创建一个优先级为TransitionPriority.HIGH,内部状态为First,当事件为inFirst 的时候就会执行myAction 的状态机。
        // 这里的优先级是用来覆盖继承的状态机中的transition
        builder.internalTransition(TransitionPriority.HIGH).within(MyState.FIRST).on(MyEvent.InFirst).callMethod("inFirst");

        // 创建一个从First 状态到Second 状态,同时使用MyEvent.ToSecond 事件触发的external transition,并且会调用toSecond 方法
        builder.externalTransition().from(MyState.FIRST).to(MyState.SECOND).on(MyEvent.ToSecond).callMethod("toSecond");

        // 创建一个优先级为TransitionPriority.HIGH,内部状态为First,当事件为ToForth 便执行myAction 的状态机。
        builder.externalTransition().from(MyState.SECOND).to(MyState.THIRD).on(MyEvent.ToThird).when(new Condition<MyContext>() {
            public boolean isSatisfied(MyContext context) {
                if (context.contestString.equals("go ahead")) {
                    System.out.println("true");
                    return true;
                } else {
                    System.out.println("false");
                    context.contestString = "go ahead";
                    return false;
                }
            }
            public String name() {
                return "condition_toForth";
            }
        }).callMethod("toThird");

        // 对于任意 from / to / on 都可定义any
        builder.transit().fromAny().toAny().onAny().callMethod("log");

        // 这里就是去创建一个状态机实例
        // 一旦通过builder创建好状态机,该builder就不能再用于定义任何包含新元素的状态机。
        UntypedStateMachine fsm = ((UntypedStateMachineBuilder) builder).newUntypedStateMachine(MyState.FIRST);
        System.out.println("test 1, current state is " + fsm.getCurrentState());

        // 由InFirst 条件出发,同时会进入到inFirst 方法中
        fsm.fire(MyEvent.InFirst, new MyContext());
        System.out.println("test 2, current state is " + fsm.getCurrentState());

        // 转移到Second 状态中
        fsm.fire(MyEvent.ToSecond, new MyContext());
        System.out.println("test 3, current state is " + fsm.getCurrentState());

        MyContext myContext = new MyContext();
        myContext.contestString = "go ahead";
        fsm.fire(MyEvent.ToThird, myContext);
        System.out.println("test 4, current state is " + fsm.getCurrentState());

    }
}

总结

这篇文章主要介绍的是Squirrel 状态机的初级入门使用方法。相信读者可以很清晰直观的做个了解。