【架构设计】无状态状态机在代码中的实践
无状态状态机
一:前言
在项目中经常有一些工单,申请之类需要对状态进行流转。这种需求一般都是满足什么条件然后就翻转状态。这些流程结构相似得逻辑,感觉可以抽象处理。用一个通用得结构处理,可以让系统更加整洁,代码逻辑更加单一。
发现阿里开源一种,轻量级得无状态状态机得组件。仔细研究一下,确实很适合这种场景下对代码逻辑得解耦,相比于if-else代码更加让人容易理解,也更加优雅。
二:状态机的模型
- State:状态
- Event:事件,状态由事件触发,引起变化
- Transition:流转,表示从一个状态到另一个状态
- External Transition:外部流转,两个不同状态之间的流转
- Internal Transition:内部流转,同一个状态之间的流转
- Condition:条件,表示是否允许到达某个状态
- Action:动作,到达某个状态之后,可以做什么
- StateMachine:状态机
三:怎么使用
这里我们假设几个简单的逻辑需要处理:
- 我们需要从待审核的状态需要流转到审核通过的状态,审核前需要做一个审核校验,审核通过后需要发送短信通知用户。
外部状态流转实现:
- 定义状态枚举(待审核,审核通过,拒绝)
static enum States {
WAITE, PASS, REJECT
}
- 定义一个事件枚举,审核事件/审核拒绝/提交申请/内部状态流转
static enum Events {
AUDIT, AUDIT_REJECT, COMMIT, COMPLETE_INFORMATION
}
// 上下文对象
static class Context{
String operator = "flw";
String entityId = "7758258";
}
- 按照上面需求配置一个状态机
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
------------------------
- 执行状态机
// 通过状态机执行 待审核状态执行审核操作,
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
------------------------
对于方法里面的异常也会抛出来,也是支持事务操作。
整体的流程图如下:
四:总结
状态机非常轻量,支持事务操作,对于状态流转比较对的场景逻辑解耦还是比较优雅的。
链接:https://github.com/alibaba/COLA/tree/master/cola-components/cola-component-statemachine