状态机引擎的业务实践
前言
在开发中通常会有状态属性,例如订单状态、事件处置状态、OA请假单状态等,都会根据不同的动作,去更新对应的状态。如下:
int status = 0;
if (condition1){
status = 1;
}else if (condition2){
status = 2;
}else if (condition3){
status = 3;
}
上述示例存在的问题:
- 在需求增加的情况下,if.else会不断膨胀
- 代码可读性差,略微改动会导致各种问题
- 其他业务逻辑会耦合在if.else代码段中
针对系统中状态的管理,可以使用有限状态机去解决,有限状态机是表示有限个状态以及状态间转移的模型。状态机由事件、状态、动作三部分组成。
状态机的简单实现
条件分支判断
最简单的方案是通过条件分支控制状态的转移,这适合于业务状态较少,并且状态跳转的逻辑较简单的场景,但是当触发事件较多时,需要嵌套多层条件判断,当需求中状态变更事件改变时,需要改动的逻辑较大。案例代码如下:
public enum ActivityState {
PREPARE(0),
STARTED(1),
END(2)
;
public final int status;
ActivityState(int status) {
this.status = status;
}
public int getStatus() {
return status;
}
}
/**
* 活动状态-状态机
* @author l1nker4
*/
public class ActivityStateMachine {
/**
* 活动状态
*/
private ActivityState currentState;
public ActivityStateMachine() {
this.currentState = ActivityState.PREPARE;
}
/**
* 活动开始
*/
public void begin() {
if (currentState.equals(ActivityState.PREPARE)) {
this.currentState = ActivityState.STARTED;
//do something....
}
}
/**
* 活动结束
*/
public void end(){
if (currentState.equals(ActivityState.STARTED)){
this.currentState = ActivityState.END;
//do something...
}
}
}
状态模式实现
设计模式中的状态模式也是状态机的一种实现方式,定义了状态-行为的对应关系,并将各自的行为封装到状态类中。状态模式会引入较多的状态类和方法,需求变更时维护成本大。
public interface IActivityState {
/**
*
* @return
*/
ActivityState getName();
/**
*
*/
void prepareAction();
/**
*
*/
void startAction();
/**
*
*/
void endAction();
}
public class ActivityStateMachine {
private IActivityState currentState;
public ActivityStateMachine(IActivityState currentState) {
this.currentState = new ActivityPrepareState(this);
}
public void setCurrentState(IActivityState currentState) {
this.currentState = currentState;
}
public void prepareAction(){
currentState.prepareAction();
}
public void startAction(){
currentState.startAction();
}
public void endAction(){
currentState.endAction();
}
}
public class ActivityPrepareState implements IActivityState{
private final ActivityStateMachine stateMachine;
public ActivityPrepareState(ActivityStateMachine stateMachine) {
this.stateMachine = stateMachine;
}
@Override
public ActivityState getName() {
return ActivityState.PREPARE;
}
@Override
public void prepareAction() {
stateMachine.setCurrentState(new ActivityPrepareState(stateMachine));
}
@Override
public void startAction() {
}
@Override
public void endAction() {
}
}
public class ActivityStartState implements IActivityState{
private final ActivityStateMachine stateMachine;
public ActivityStartState(ActivityStateMachine stateMachine) {
this.stateMachine = stateMachine;
}
@Override
public ActivityState getName() {
return ActivityState.STARTED;
}
@Override
public void prepareAction() {
}
@Override
public void startAction() {
stateMachine.setCurrentState(new ActivityStartState(stateMachine));
}
@Override
public void endAction() {
}
}
public class ActivityEndState implements IActivityState {
private final ActivityStateMachine stateMachine;
public ActivityEndState(ActivityStateMachine stateMachine) {
this.stateMachine = stateMachine;
}
@Override
public ActivityState getName() {
return ActivityState.END;
}
@Override
public void prepareAction() {
}
@Override
public void startAction() {
}
@Override
public void endAction() {
stateMachine.setCurrentState(new ActivityEndState(stateMachine));
}
}
DSL方案
DSL(Domain-Specific Languages):针对以特定领域的语言,用于解决一类问题,例如:正则表达式可以解决字符串匹配这一类问题。可分为以下两种类型:
- Internal DSL:业务代码通过代码直接进行配置,Java Internal DSL一般直接利用建造者模式和流式接口构建
- External DSL:通过外部配置文件,运行时读取进行解析,例如XML、JSON等格式
COLA
COLA是阿里巴巴开源的DSL状态机实现,根据作者的介绍,相比较其他开源的状态机实现(Spring Statemachine等),其优点如下:
- 开源的状态机引擎性能较差,状态机有状态,需要考虑线程安全
- 开源的状态机较为复杂,太重。
领域模型
COLA状态机的核心概念有:
- State:状态
- Event:状态由事件触发
- Transition:状态间转换的过程
- External Transition:stateA -> stateB
- Internal Transition:steteA -> stateA
- Condition:是否允许到达某种状态
- Action:state变化时执行的动作
demo
pom.xml:
<dependency>
<groupId>com.alibaba.cola</groupId>
<artifactId>cola-component-statemachine</artifactId>
<version>4.3.1</version>
</dependency>
public enum Events {
STARTED("activity started"),
END("activity ended")
;
private String event;
Events(String event) {
this.event = event;
}
public void setEvent(String event) {
this.event = event;
}
}
@Test
public void testMachine() {
StateMachineBuilder<ActivityState, Events, Object> builder = StateMachineBuilderFactory.create();
builder.externalTransition()
.from(ActivityState.PREPARE)
.to(ActivityState.STARTED)
.on(Events.STARTED)
.when(checkCondition())
.perform(doAction());
StateMachine<ActivityState, Events, Object> machine = builder.build("test");
ActivityState activityState = machine.fireEvent(ActivityState.PREPARE, Events.STARTED, new Object());
log.info("new state: {}", activityState);
}
private Condition<Object> checkCondition() {
return (context -> {return true;});
}
private Action<ActivityState, Events, Object> doAction() {
return ((from, to, event, context) -> {
log.info("{} is doing, from {}, to {}, on: {}", context, from, to, event);
});
}
Reference
状态机引擎的业务实践
https://l1n.wang/2023/设计模式/状态机引擎的业务实践/