保姆式教程!如何使用Cola-statemachine构建高可靠性的状态机 ?

3,379 阅读6分钟

一、前言

1.1 Cola-statemachine简介

Cola-statemachine是一款基于JavaScript的状态机库,可以帮助开发人员管理和控制应用程序的状态流。它提供了一个可视化工具,帮助用户创建复杂的状态机图,并能够将代码生成为JavaScript类。cola-statemachine的主要目标是使状态机的实现变得简单且可扩展,同时保持代码的可读性。

1.2 Cola-statemachine作用

cola-statemachine可以帮助开发人员简化应用程序的状态管理和控制。它的主要作用包括:

  1. 简化状态流程:使用cola-statemachine,开发人员可以将应用程序拆分为不同的状态,避免了大量的if-else语句。状态之间的转换逻辑可以轻松定义,使得应用程序的状态流程更易于理解和管理。
  2. 提供可视化工具:cola-statemachine提供了一个可视化工具,使得开发人员可以更直观地创建和编辑状态机图,从而更容易跟踪应用程序的状态流程。
  3. 支持定制化:cola-statemachine提供了许多可定制的选项,例如定义状态之间的转换条件、执行动作等。这使得开发人员可以按照自己的需求调整状态机的行为。
  4. 增加可读性:通过使用cola-statemachine,开发人员可以将整个应用程序的状态流程描述为一个可读的状态机,使得代码更容易理解和维护。

1.3 Cola-statemachine包含以下核心概念:

  1. 状态(State):描述应用程序的特定场景或状态。
  2. 转换(Transition):定义从一个状态到另一个状态的逻辑。
  3. 事件(Event):触发状态转换的动作。
  4. 动作(Action):在特定状态下执行的逻辑或指令,例如输出日志、更新UI等。
  5. 状态机(StateMachine):一个包含所有状态、转换、事件和动作的整体结构。
  6. 初始化(Initialization):指在状态机开始执行前必须执行的一些初始化工作,例如设置默认状态或加载数据。
  7. 终止(Termination):指状态机停止执行前必须执行的一些清理工作,例如释放资源或保存数据。

二、Cola-statemachine的使用说明

2.1 依赖引用

<dependency>
<groupId>com.alibaba.cola</groupId>  
<artifactId>cola-component-statemachine</artifactId>  
<version>4.0.1</version>  
</dependency>

2.2 Cola-statemachine常见的转换状态

// 创建状态机生成器
StateMachineBuilder<States, Events, Context> builder = StateMachineBuilderFactory.create(); 

// 定义一个外部转换,从STATE1到STATE2,响应EVENT1事件,当checkCondition()返回true时,执行doAction()动作 
builder.externalTransition()
    .from(States.STATE1)
    .to(States.STATE2)
    .on(Events.EVENT1)
    .when(checkCondition())
    .perform(doAction());
    
// 定义多个外部转换,从STATE1, STATE2, STATE3中的任意一种状态到STATE4,响应EVENT4事件,当checkCondition()返回true时,执行doAction()动作
builder.externalTransitions()
    .fromAmong(States.STATE1, States.STATE2, States.STATE3)
    .to(States.STATE4)
    .on(Events.EVENT4)
    .when(checkCondition())
    .perform(doAction());
    
// 定义一个内部转换,在STATE2状态内部,响应INTERNAL_EVENT事件,当checkCondition()返回true时,执行doAction()动作
builder.internalTransition()
    .within(States.STATE2)
    .on(Events.INTERNAL_EVENT)
    .when(checkCondition())
    .perform(doAction());

// 构建状态机,指定状态机id为machineId
builder.build(machineId);

// 获取指定id的状态机实例
StateMachine<States, Events, Context> stateMachine = StateMachineFactory.get(machineId); 

// 在控制台展示状态机
stateMachine.showStateMachine();

2.3 相关说明:

externalTransition与internalTransition区别 externalTransition(外部转换)和internalTransition(内部转换)都是用于定义状态之间的转换。二者的区别主要在于响应事件的不同:

  • externalTransition通常用于处理从一个状态到另一个状态的转换,并且需要响应外部的事件,例如从Off状态到Low状态的转换需要响应FlipSwitch事件。
  • internalTransition通常用于处理状态内部的转换或行为, 不进行当前的状态变更,并不需要响应其他外部事件。例如在Low状态内部增加到下一个系统状态。

2.4 Cola-statemachine接口说明

2.4.1 StateMachineBuilder接口方法

StateMachineBuilder接口定义了一系列方法来帮助开发人员构建状态机。其中包括定义状态与转换、设置初始状态、配置上下文参数等,最后通过build()方法来构建并返回一个状态机实例。详情见源码。

image.png

2.4.2 StateMachine常用接口方法

StateMachine接口中主要包含了获取/设置状态机相关信息、触发状态机转换、获取已定义转换及状态信息等方法。这些方法可以帮助开发人员更容易地控制和管理应用程序状态,此处直接看源码吧 image.png

3.案例实战

3.1 枚举类,定义状态和事件

// 定义状态
public enum State {
    STATE1, 
    STATE2, 
    STATE3, 
    STATE4 
}
 // 定义事件
 public enum Event {
     EVENT1,
     EVENT2,
     EVENT3,
     EVENT4
}

3.2 定义上下文类,以及相关动作

public class Context { 

    public String message;  
} 
public class Action {

    // 执行转换前的准备动作
    public void prepare(State from, State to, Event event, Context context) {
        System.out.println(String.format("准备转换: %s -> %s [event:%s] [message:%s]",
from, to, event, context.message));
    }

    // 执行转换动作
    public void execute(State from, State to, Event event, Context context) {
        System.out.println(String.format("执行转换: %s -> %s [event:%s] [message:%s]",from, to, event, context.message));
    }

    // 执行转换后的清理动作
    public void clean(State from, State to, Event event, Context context) {
        System.out.println(String.format("结束转换: %s -> %s [event:%s] [message:%s]",
from, to, event, context.message));
    }
}

3.3 状态机生成代码

public class StateMachineDemo {  
  
    public static void main(String[] args) {  
    // 创建状态机生成器  
        StateMachineBuilder<State, Event, Context> builder = StateMachineBuilderFactory.create();  
  
        // 定义状态转换事件、条件和动作  
        builder.externalTransition()  
            .from(State.STATE1)  
            .to(State.STATE2)  
            .on(Event.EVENT1)  
            .when(checkCondition())  
            .perform(doAction());  
  
        builder.externalTransitions()  
            .fromAmong(State.STATE1, State.STATE2, State.STATE3)  
            .to(State.STATE4)  
            .on(Event.EVENT4)  
            .perform(doAction());  
  
        builder.externalTransition()  
            .from(State.STATE4)  
            .to(State.STATE1)  
            .on(Event.EVENT2)  
            .perform(doAction());  
  
        builder.internalTransition()  
            .within(State.STATE2)  
            .on(Event.EVENT3)  
            .perform(doAction());  
  
        // 根据状态机生成器创建状态机,并设置初始状态  
        StateMachine<State, Event, Context> stateMachine = builder.build(State.STATE1);  

        // 定义上下文,写入一个message字段  
        Context context = new Context();  
        context.message = "Hello, world!";  

        // 输出初始状态  
        System.out.println("初始状态:" + stateMachine.getState());  

        // 触发状态转换  
        stateMachine.fire(Event.EVENT1, context);  
        System.out.println("执行 EVENT1 后的状态:" + stateMachine.getState());  

        stateMachine.fire(Event.EVENT4, context);  
        System.out.println("执行 EVENT4 后的状态:" + stateMachine.getState());  

        stateMachine.fire(Event.EVENT2, context);  
        System.out.println("执行 EVENT2 后的状态:" + stateMachine.getState());  

        stateMachine.fire(Event.EVENT3, context);  
        System.out.println("执行 EVENT3 后的状态:" + stateMachine.getState());  

        // 可以获取当前状态机可以触发的事件,  
        // 这里将会输出 [EVENT1,EVENT4],即第一个触发STATE2,第二个触发STATE4。  
        System.out.println("当前状态可以触发的事件:" + stateMachine.getPermittedTriggers());  

        // 输出状态机的所有转换  
        System.out.println("状态机的所有转换:" + stateMachine.getTransitions());  

        // 输出状态机所有的状态  
        System.out.println("所有状态:" + stateMachine.getPossibleStateTransitions());  

        // 输出状态机的状态信息  
        System.out.println("状态机信息:" + stateMachine.getStateRepresentation());  
}  
  
        // 检查条件方法  
        private static Condition checkCondition() {  
            return new Condition() {  
        
            @Override  
            public boolean isSatisfied(State from, State to, Event event, Context context) {  
                return true;  
            }
        };
    }
  
    // 执行动作方法  
    private static Action doAction() {  
        return new Action() {  
            @Override  
            public void execute(State from, State to, Event event, Context context) {  
                prepare(from, to, event, context);  
                super.execute(from, to,Event, context);  
clean(from, to, event, context);  
            }  
        };  
    }  
}

---------上述执行结果--------:

初始状态:STATE1
准备转换: STATE1 -> STATE2 [event:EVENT1] [message:Hello, world!]
执行转换: STATE1 -> STATE2 [event:EVENT1] [message:Hello, world!]
结束转换: STATE1 -> STATE2 [event:EVENT1] [message:Hello, world!]
执行 EVENT1 后的状态:STATE2
准备转换: STATE2 -> STATE4 [event:EVENT4] [message:Hello, world!]
执行转换: STATE2 -> STATE4 [event:EVENT4] [message:Hello, world!]
结束转换: STATE2 -> STATE4 [event:EVENT4] [message:Hello, world!]
执行 EVENT4 后的状态:STATE4
准备转换: STATE4 -> STATE1 [event:EVENT2] [message:Hello, world!]
执行转换: STATE4 -> STATE1 [event:EVENT2] [message:Hello, world!]
结束转换: STATE4 -> STATE1 [event:EVENT2] [message:Hello, world!]
执行 EVENT2 后的状态:STATE1
准备转换: STATE2 -> STATE2 [event:EVENT3] [message:Hello, world!]
执行转换: STATE2 -> STATE2 [event:EVENT3] [message:Hello, world!]
结束转换: STATE2 -> STATE2 [event:EVENT3] [message:Hello, world!]
执行 EVENT3 后的状态:STATE2
当前状态可以触发的事件:[EVENT1, EVENT4]
状态机的所有转换:[
{ from=STATE1, to=STATE2, on=EVENT1 },
{ from=STATE1, to=STATE4, on=EVENT4 },
{ from=STATE2, to=STATE2, on=EVENT3 },
{ from=STATE2, to=STATE4, on=EVENT4 },
{ from=STATE4, to=STATE1, on=EVENT2 }
]
所有状态:[STATE1, STATE2, STATE3, STATE4]
状态机信息:State: STATE1 actions: []\n -> State: [EVENT1]STATE2 actions: [LambaAction]\n -> State: [EVENT3]STATE2 actions: [LambaAction]\n -> State: STATE4 actions: [LambaAction]\n -> State: [EVENT2]STATE1 actions: [LambaAction]\nFinal State: []STATE1