开启掘金成长之旅!这是我参与「掘金日新计划 · 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 的接口,需要以下这四种类型的泛型参数:
- T 代表状态机类型
- S 代表状态类型(State)
- E 代表事件类型(Event)
- 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 状态机的初级入门使用方法。相信读者可以很清晰直观的做个了解。