当前位置 : 首页 » 文章分类 :  开发  »  squirrel-foundation

squirrel-foundation

squirrel-foundation 有限状态机框架


有限状态机开源框架

状态机引擎选型
https://segmentfault.com/a/1190000009906317

squirrel-foundation(1.9k星)

hekailiang / squirrel
https://github.com/hekailiang/squirrel

spring-statemachine(1.3k星)

spring-projects / spring-statemachine
https://github.com/spring-projects/spring-statemachine

stateless4j(0.7k星)

stateless4j / stateless4j
https://github.com/stateless4j/stateless4j


状态模式

https://refactoringguru.cn/design-patterns/state


squirrel-foundation 基本概念

StateMachine

StateMachine 状态机接口有 4 个泛型参数:

  • T 是状态机实现类本身
  • S 是状态类型
  • E 是事件类型
  • C 是上下文类型

常用的是一个 fire(E event, C context) 方法,表示带着 context 上下文触发 event 事件。

public interface StateMachine<T extends StateMachine<T, S, E, C>, S, E, C> extends Visitable, Observable {
    void fire(E event, C context);
    ...
}

StateMachineBuilder

通过 StateMachineBuilder 定义状态机的状态流转,StateMachineBuilder 可以通过 StateMachineBuilderFactory 创建。

StateMachineBuilder 由 TransitionBuilder 和 EntryExitActionBuilder 组成:

  • TransitionBuilder 有三种类型 InternalTransitionBuilder / LocalTransitionBuilder / ExternalTransitionBuilder,用于定义状态间的转换。
  • EntryExitActionBuilder 定义进入、退出状态时的动作

通过 StateMachineBuilderFactory.create() 创建 StateMachineBuilder,之后可通过 流式API 定义状态机的 状态/转换/动作。

StateMachineBuilder<MyStateMachine, MyState, MyEvent, MyContext> builder =
    StateMachineBuilderFactory.create(MyStateMachine.class, MyState.class, MyEvent.class, MyContext.class);

external transition 外部转换

external transition 外部转换 指的是不同状态之间的转换

transition() 等价于 externalTransition() 表示构建外部状态转换

ExternalTransitionBuilder<T, S, E, C> transition();
ExternalTransitionBuilder<T, S, E, C> externalTransition();

例1、事件 GoToB 驱动 状态A 转换为 状态B

builder.externalTransition().from(MyState.A).to(MyState.B).on(MyEvent.GoToB);

例2、状态A 遇到 事件ToB 时转换为 状态B,同时调用方法 fromAToB

builder.externalTransition().from("A").to("B").on(FSMEvent.ToB).callMethod("fromAToB");

transitions() 等价于 externalTransitions() 可构建从多个状态到1个状态的转换

MultiTransitionBuilder<T, S, E, C> externalTransitions();
MultiTransitionBuilder<T, S, E, C> transitions();

例3、A、B、C 状态遇到 ToD 事件时转换为 D 状态

builder.transitions().fromAmong("A","B","C").to("D").on(FSMEvent.ToD).callMethod("toD");

internal transition 内部转换

internal transition 内部转换 指的是同一状态内部的转换,也就是转换完成后状态不会发生变化,没有状态进入或退出

例1、在处于 状态A 时,遇到 WithinA 事件则执行 myAction 动作,但状态不变。

builder.internalTransition(TransitionPriority.HIGH).within(MyState.A).on(MyEvent.WithinA).perform(myAction);

conditional transition 条件转换

conditional transition 条件转换 表示当上下文满足某些条件时才发生转换

例1、状态C 遇到 GoToD事件 且上下文满足 when 中的条件时则转换为状态D,同时执行 myInternalTransitionCall 方法。

builder.externalTransition().from(MyState.C).to(MyState.D).on(MyEvent.GoToD).when(
    new Condition<MyContext>() {
        @Override
        public boolean isSatisfied(MyContext context) {
            return context!=null && context.getValue()>80;
        }
        
        @Override
        public String name() {
            return "MyCondition";
        }
}).callMethod("myInternalTransitionCall");

callMethod 回调方法

squirrel-foundation 支持 Convention Over Configuration(约定大于配置) 的回调方法命名格式,即当回调方法命名格式和参数满足要求后可被自动识别,并被添加到对应的 transition 转换定义中。
方法名格式为 transitFrom[SourceStateName]To[TargetStateName]On[EventName] 且参数为 [MyState, MyState, MyEvent, MyContext] 的方法会被自动添加到 SourceStateName-(EventName)->TargetStateName 的转换中。

例1、下面的方法会自动被添加到 A-(GoToB)->B 的转换动作列表中,当转换发生时自动被调用

protected void transitFromAToBOnGoToB(MyState from, MyState to, MyEvent event, MyContext context)

例2、方法名为 transitFromAnyTo[TargetStateName]On[EventName] 且参数列表符合的方法会自动被添加到 任意状态 -> (GoToB) -> B 的转换中

protected void transitFromAnyToBOnGoToB(MyState from, MyState to, MyEvent event, MyContext context)

例3、方法名为 exit[StateName] 且参数列表符合的方法会自动在退出状态A时被调用

protected void exitA(MyState from, MyState to, MyEvent event, MyContext context)

声明式注解

squirrel-foundation 支持通过注解定义状态机。注解可放在状态机实现类上,也可以放在状态机接口上。注解可以和 流式API 混合使用。

@States({
    @State(name="A", entryCallMethod="entryStateA", exitCallMethod="exitStateA"),
    @State(name="B", entryCallMethod="entryStateB", exitCallMethod="exitStateB")
})
@Transitions({
    @Transit(from="A", to="B", on="GoToB", callMethod="stateAToStateBOnGotoB"),
    @Transit(from="A", to="A", on="WithinA", callMethod="stateAToStateAOnWithinA", type=TransitionType.INTERNAL)
})
interface MyStateMachine extends StateMachine<MyStateMachine, MyState, MyEvent, MyContext> {
    void entryStateA(MyState from, MyState to, MyEvent event, MyContext context);
    void stateAToStateBOnGotoB(MyState from, MyState to, MyEvent event, MyContext context)
    void stateAToStateAOnWithinA(MyState from, MyState to, MyEvent event, MyContext context)
    void exitStateA(MyState from, MyState to, MyEvent event, MyContext context);
    ...
}

Transition from “State1” on “Event1” declined.

o.s.foundation.fsm.StateMachineLogger - DistributedTaskStateMachine(25ee8752-e486-469b-a93a-102c354d85fc): Transition from “State1” on “Event1” declined.
可能的原因:
1、事件配置错误
2、流转 transition 没配置


UntypedStateMachine 无类型状态机

为了简化状态机的使用,避免太多泛型比如 StateMachine<T, S, E, C> 使代码难懂,可以使用 UntypedStateMachine。
为了创建 UntypedStateMachine,需要先通过 StateMachineBuilderFactory.create() 创建一个 UntypedStateMachineBuilder,create() 支持只传入一个状态机class参数,很简单,然后使用 @StateMachineParameters 来描述状态机的其他参数。

enum TestEvent {
    toA, toB, toC, toD
}

@Transitions({
    @Transit(from="A", to="B", on="toB", callMethod="fromAToB"),
    @Transit(from="B", to="C", on="toC"),
    @Transit(from="C", to="D", on="toD")
})
@StateMachineParameters(stateType=String.class, eventType=TestEvent.class, contextType=Integer.class)
class UntypedStateMachineSample extends AbstractUntypedStateMachine {
    protected void fromAToB(String from, String to, TestEvent event, Integer context) {
        // transition action still type safe ...
    }

    protected void transitFromDToAOntoA(String from, String to, TestEvent event, Integer context) {
        // transition action still type safe ...
    }
}

UntypedStateMachineBuilder builder = StateMachineBuilderFactory.create(UntypedStateMachineSample.class);
// state machine builder not type safe anymore
builder.externalTransition().from("D").to("A").on(TestEvent.toA);
UntypedStateMachine fsm = builder.newStateMachine("A");

状态转换扩展方法

public abstract class AbstractStateMachine<T extends StateMachine<T, S, E, C>, S, E, C> extends AbstractSubject implements StateMachine<T, S, E, C> {
    protected void afterTransitionCausedException(S fromState, S toState, E event, C context) {
        if(getLastException().getTargetException()!=null)
            getLastException().getTargetException().printStackTrace();
        throw getLastException();
    }

    protected void beforeTransitionBegin(S fromState, E event, C context) {
    }
    
    protected void afterTransitionCompleted(S fromState, S toState, E event, C context) {
    }
    
    protected void afterTransitionEnd(S fromState, S toState, E event, C context) {
    }
    
    protected void afterTransitionDeclined(S fromState, E event, C context) {
    }
    
    protected void beforeActionInvoked(S fromState, S toState, E event, C context) {
    }
    
    protected void afterActionInvoked(S fromState, S toState, E event, C context) {
    }
}

状态转换异常处理

状态转换期间的全部异常,包括动作执行异常和外部监听器异常,都会被包装到 TransitionException 中(非受检异常),默认的异常处理策略是抛出异常。
可在自己的状态机实现类中通过覆盖 afterTransitionCausedException() 方法自定义异常处理策略。

状态转换完成时

afterTransitionCompleted() 在状态转换完成时触发,可以在其中打印日志,或者将状态转换记录持久化到数据库中


squirrel-foundation 状态机使用示例

1、定义 状态枚举、事件枚举、上下文结构

enum FSMState {
    StateA, StateB, StateC, StateD
}

enum FSMEvent {
    ToA, ToB, ToC, ToD
}

class FSMContext {
    private Object myContext;
}

2、定义状态机

@StateMachineParameters(
    stateType = FSMState.class, 
    eventType = FSMEvent.class, 
    contextType = FSMContext.class
)
static class StateMachineSample extends AbstractUntypedStateMachine {
    protected void fromAToB(FSMState from, FSMState to, FSMEvent event, FSMContext context) {
        System.out.println("Transition from '"+from+"' to '"+to+"' on event '"+event+ "' with context '"+context+"'.");
    }

    protected void ontoB(FSMState from, FSMState to, FSMEvent event, FSMContext context) {
        System.out.println("Entry State \'"+to+"\'.");
    }
}

3、创建状态机builder,定义状态流转,fire 触发使用。

public static void main(String[] args) {
    // 3. Build State Transitions
    UntypedStateMachineBuilder builder = StateMachineBuilderFactory.create(StateMachineSample.class);
    builder.externalTransition().from("A").to("B").on(FSMEvent.ToB).callMethod("fromAToB");
    builder.onEntry("B").callMethod("ontoB");

    // 4. Use State Machine
    UntypedStateMachine fsm = builder.newStateMachine("A");
    fsm.fire(FSMEvent.ToB, 10);

    System.out.println("Current state is "+fsm.getCurrentState());
}

Spring 集成 squirrel-foundation

squirrel-foundation状态机的使用细节
https://segmentfault.com/a/1190000009906469


上一篇 Vitess

下一篇 2021年运动记录

阅读
评论
1.9k
阅读预计9分钟
创建日期 2021-01-26
修改日期 2022-07-05
类别

页面信息

location:
protocol:
host:
hostname:
origin:
pathname:
href:
document:
referrer:
navigator:
platform:
userAgent:

评论