【架构设计】无状态状态机在代码中的实践

无状态状态机

一:前言

​ 在项目中经常有一些工单,申请之类需要对状态进行流转。这种需求一般都是满足什么条件然后就翻转状态。这些流程结构相似得逻辑,感觉可以抽象处理。用一个通用得结构处理,可以让系统更加整洁,代码逻辑更加单一。

​ 发现阿里开源一种,轻量级得无状态状态机得组件。仔细研究一下,确实很适合这种场景下对代码逻辑得解耦,相比于if-else代码更加让人容易理解,也更加优雅。

二:状态机的模型

  • State:状态
  • Event:事件,状态由事件触发,引起变化
  • Transition:流转,表示从一个状态到另一个状态
  • External Transition:外部流转,两个不同状态之间的流转
  • Internal Transition:内部流转,同一个状态之间的流转
  • Condition:条件,表示是否允许到达某个状态
  • Action:动作,到达某个状态之后,可以做什么
  • StateMachine:状态机

三:怎么使用

​ 这里我们假设几个简单的逻辑需要处理:

  • 我们需要从待审核的状态需要流转到审核通过的状态,审核前需要做一个审核校验,审核通过后需要发送短信通知用户。

外部状态流转实现:

  1. 定义状态枚举(待审核,审核通过,拒绝)
 static enum States {
    WAITE, PASS, REJECT
}
  1. 定义一个事件枚举,审核事件/审核拒绝/提交申请/内部状态流转
static enum Events {
   AUDIT, AUDIT_REJECT, COMMIT, COMPLETE_INFORMATION
}

// 上下文对象
static class Context{
   String operator = "flw";
   String entityId = "7758258";
 }
  1. 按照上面需求配置一个状态机
   StateMachineBuilder<States, Events, Context> builder = StateMachineBuilderFactory.create();
        builder.externalTransition()    // 外部状态流转
                .from(States.WAITE)     // 起始状态:待审核
                .to(States.PASS)        // 目的状态:审核通过
                .on(Events.AUDIT)       // 事件:审核事件
                .when(checkCondition()) // 流转需要校验的条件,校验不通过不会进行doAction
                .perform(doAction());   // 执行流转操作

   StateMachine<States, Events, Context> stateMachine = builder.build(MACHINE_ID);
   // 打印状态机里面的流程流转图谱
   stateMachine.showStateMachine();

   private Condition<StateMachineMyTest.Context> checkCondition() {
        return (ctx) -> {return true;}; // 默认返回true
    }

    private Action<StateMachineMyTest.States, StateMachineMyTest.Events, StateMachineMyTest.Context> doAction() {
        return (from, to, event, ctx)->{
            System.out.println(ctx.operator+" is operating "+ctx.entityId+" from:"+from+" to:"+to+" on:"+event);
        };
    }

执行状态机的打印图谱结果:

-----StateMachine:TestStateMachine-------
State:PASS
State:WAITE
    Transition:WAITE-[AUDIT, EXTERNAL]->PASS
------------------------
  1. 执行状态机
// 通过状态机执行 待审核状态执行审核操作,
States target = stateMachine.fireEvent(States.WAITE, Events.AUDIT, new Context());
Assert.assertEquals(States.PASS, target); // 成功就会返回目标状态机,校验失败就会返回原来的状态

执行结果:

flw is operating 7758258 from:WAITE to:PASS on:AUDIT

如果 checkCondition 返回false ,就不会执行doAction 操作。

内部状态流转实现

  • 假设现在只是用户补全资料,只需要进行一些更新数据操作,不需要状态流转。这种需求可以通过内部状态流转实现
 @Test
    public void testExternalNormal(){
        StateMachineBuilder<States, Events, Context> builder = StateMachineBuilderFactory.create();
        builder.externalTransition()// 内部流转
                .from(States.WAITE)
                .to(States.PASS)
                .on(Events.COMPLETE_INFORMATION)
                .when(checkCondition())
                .perform(doAction());

        StateMachine<States, Events, Context> stateMachine = builder.build(MACHINE_ID);
        // 打印状态机里面的流程流转图谱
        stateMachine.showStateMachine();
        // 通过状态机执行 待审核状态执行审核操作,
        States target = stateMachine.fireEvent(States.WAITE, Events.AUDIT, new Context());
        Assert.assertEquals(States.WAITE, target);
    }

执行结果:

-----StateMachine:TestStateMachine-------
State:PASS
State:WAITE
    Transition:WAITE-[COMPLETE_INFORMATION, EXTERNAL]->PASS
------------------------

对于方法里面的异常也会抛出来,也是支持事务操作。

整体的流程图如下:

image-20211015223159331

四:总结

​ 状态机非常轻量,支持事务操作,对于状态流转比较对的场景逻辑解耦还是比较优雅的。

链接:https://github.com/alibaba/COLA/tree/master/cola-components/cola-component-statemachine

原文地址:https://www.cnblogs.com/simple-flw/p/15413022.html