4

Squirrel状态机-从原理探究到最佳实践 - 京东云开发者

 1 year ago
source link: https://www.cnblogs.com/Jcloud/p/17070001.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

作者:京东物流 郑朋辉

Squirrel状态机是一种用来进行对象行为建模的工具,主要描述对象在它的生命周期内所经历的状态,以及如何响应来自外界的各种事件。比如订单的创建、已支付、发货、收获、取消等等状态、状态之间的控制、触发事件的监听,可以用该框架进行清晰的管理实现。使用状态机来管理对象生命流的好处更多体现在代码的可维护性、可测试性上,明确的状态条件、原子的响应动作、事件驱动迁移目标状态,对于流程复杂易变的业务场景能大大减轻维护和测试的难度。

2 基本概念

2.1 Squirrel状态机定义

Squirrel状态机是一种有限状态机,有限状态机是指对象有一个明确并且复杂的生命流(一般而言三个以上状态),并且在状态变迁存在不同的触发条件以及处理行为。

2.2 Squirrel状态机要素

Squirrel状态机可归纳为4个要素,即现态、条件、动作、次态。“现态”和“条件”是因,“动作”和“次态”是果。

  • 现态:是指当前所处的状态。
  • 条件:又称为事件。当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。
  • 动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。
  • 次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。

3 实现原理

3.1 店铺审核CASE

举例,京东线上开店需要经过审核才能正式上线,店铺状态有待审核、已驳回、已审核,对应操作有提交审核,审核通过,审核驳回动作。现在需要实现一个店铺审核流程的需求。

3.2 方案对比

3.2.1 常用if-else或switch-case实现(分支模式)

47f5c883-71db-4370-8cea-63d90c756a7520221221161010.png

图1.if-else/switch-case模式实现流程图

3.2.2 状态机实现

3260d8db-4844-4dc2-b73a-2c690cb5fb9b20221221161046.png

图2.状态机模式实现流程图

3.2.3 对比

通过引入状态机,可以去除大量if-else if-else或者switch-case分支结构,直接通过当前状态和状态驱动表查询行为驱动表,找到具体行为执行操作,有利于代码的维护和扩展。

3.3 实现原理

0a39fd44-205f-4d1c-b263-f83c2fba784520221221161116.png

图3.状态机创建流程图

  • StateMachine: StateMachine实例由StateMachineBuilder创建不被共享,对于使用annotation方式(或fluent api)定义的StateMachine,StateMachine实例即根据此定义创建,相应的action也由本实例执行,与spring的集成最终要的就是讲spring的bean实例注入给由builder创建的状态机实例;
  • StateMachineBuilder: 本质上是由StateMachineBuilderFactory创建的动态代理。被代理的StateMachineBuilder默认实现为StateMachineBuilderImpl,内部描述了状态机实例创建细节包括State、Event、Context类型信息、constructor等,同时也包含了StateMachine的一些全局共享资源包括StateConverter、EventConverter、MvelScriptManager等。StateMachineBuilder可被复用,使用中可被实现为singleton;
  • StateMachineBuilderFactory: 为StateMachineBuilder创建的动态代理实例;

4 实践分享

4.1 环境依赖

<dependency>
<groupId>org.squirrelframework</groupId>
<artifactId>squirrel-foundation</artifactId>
<version>0.3.9</version>
</dependency>

4.2 状态机元素定义:状态、事件

// 店铺审核状态
public Enum ShopInfoAuditStatusEnum{
audit(0,"待审核"),
agree(1,"审核通过"),
reject(2,"审核驳回");
}
// 店铺审核事件
public Enum ShopInfoAuditEvent{
SUBMIT, // 提交
AGREE, // 同意
REJECT; // 驳回
}

4.3 构建StateMachineBuilder实例

/**
* StateMachineBuilder实例
*/
public class StateMachineEngine <T extends UntypedStateMachine, S, E, C> implements ApplicationContextAware{


private ApplicationContext applicationContext;


private static Map<String,UntypedStateMachineBuilder> builderMap = new HashMap<String,UntypedStateMachineBuilder>();

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}


@Transactional
public void fire(Class<T> machine, S state, E event, C context) {
StateMachineBuilder stateMachineBuilder = this.getStateMachineBuilder(machine);
StateMachine stateMachine = stateMachineBuilder.newStateMachine(state,applicationContext);
stateMachine.fire(event, context);
}


private StateMachineBuilder getStateMachineBuilder(Class<T> stateMachine){
UntypedStateMachineBuilder stateMachineBuilder = builderMap.get(stateMachine.getName());
if(stateMachineBuilder == null){
stateMachineBuilder = StateMachineBuilderFactory.create(stateMachine,ApplicationContext.class);
builderMap.put(stateMachine.getName(),stateMachineBuilder);
}
return stateMachineBuilder;

4.4 创建具体店铺状态审核状态机

/**
* 店铺审核状态机
*/
@States({
@State(name = "audit"),
@State(name = "agree"),
@State(name = "reject")
})
@Transitions({
@Transit(from = "audit", to = "agree", on = "AGREE", callMethod = "agree"),
@Transit(from = "audit", to = "reject", on = "REJECT", callMethod = "reject"),
@Transit(from = "reject", to = "audit", on = "SUBMIT", callMethod = "submit"),
@Transit(from = "agree", to = "audit", on = "SUBMIT", callMethod = "submit"),
@Transit(from = "audit", to = "audit", on = "SUBMIT", callMethod = "submit"),
})
@StateMachineParameters(stateType=ShopInfoAuditStatusEnum.class, eventType=ShopInfoAuditEvent.class, contextType=ShopInfoAuditStatusUpdateParam.class)
public class ShopInfoAuditStateMachine extends AbstractUntypedStateMachine {


private ApplicationContext applicationContext;


public ShopInfoAuditStateMachine(){}


public ShopInfoAuditStateMachine(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}


// 审核通过业务逻辑
public void agree(ShopInfoAuditStatusEnum fromState, ShopInfoAuditStatusEnum toState, ShopInfoAuditEvent event, ShopInfoAuditStatusUpdateParam param) {
this.agree(fromState,toState,event,param);
}

// 审核驳回业务逻辑
public void reject(ShopInfoAuditStatusEnum fromState, ShopInfoAuditStatusEnum toState, ShopInfoAuditEvent event, ShopInfoAuditStatusUpdateParam param) {
this.reject(fromState,toState,event,param);
}

// 提交业务逻辑
public void submit(ShopInfoAuditStatusEnum fromState, ShopInfoAuditStatusEnum toState, ShopInfoAuditEvent event, ShopInfoAuditStatusUpdateParam param) {
this.submit(fromState,toState,event,param);
}

4.5 客户端调用

// 调用端
main{
StateMachineEngine stateMachineEngine = applicationContext.getBean(StateMachineEngine.class);
// 审核通过调case
stateMachineEngine.fire(ShopInfoAuditStateMachine.class,ShopInfoAuditStatusEnum.audit,ShopInfoAuditEvent.AGREE,param);
// 审核驳回case
stateMachineEngine.fire(ShopInfoAuditStateMachine.class,ShopInfoAuditStatusEnum.audit,ShopInfoAuditEvent.REJECT,param);
}

状态机很好的帮我们处理了对象状态的流转、事件的监听以及外界的各种事件的响应。从代码设计角度减少了大量if-else/switch-case逻辑判断,提高了代码的可维护性、扩展性,方便管理和测试。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK