Squirrel状态机-从原理探究到最佳实践

作者:京东物流 郑朋辉

1 简介

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

2 基本概念

2.1 Squirrel状态机定义

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

2.2 Squirrel状态机要素

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

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

3 实现原理

3.1 店铺审核CASE

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

3.2 方案对比

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

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

3.2.2 状态机实现


 

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

3.2.3 对比

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

3.3 实现原理

图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 环境依赖

1<dependency> 2<groupId>org.squirrelframework</groupId> 3<artifactId>squirrel-foundation</artifactId> 4<version>0.3.9</version> 5</dependency>

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

1// 店铺审核状态 2public Enum ShopInfoAuditStatusEnum{ 3audit(0,"待审核"), 4agree(1,"审核通过"), 5reject(2,"审核驳回"); 6} 7// 店铺审核事件 8public Enum ShopInfoAuditEvent{ 9SUBMIT, // 提交 10AGREE, // 同意 11REJECT; // 驳回 12}

4.3 构建StateMachineBuilder实例

1/** 2* StateMachineBuilder实例 3*/ 4public class StateMachineEngine <T extends UntypedStateMachine, S, E, C> implements ApplicationContextAware{ 5 6 7private ApplicationContext applicationContext; 8 9 10private static Map<String,UntypedStateMachineBuilder> builderMap = new HashMap<String,UntypedStateMachineBuilder>(); 11 12@Override 13public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 14this.applicationContext = applicationContext; 15} 16 17 18@Transactional 19public void fire(Class<T> machine, S state, E event, C context) { 20StateMachineBuilder stateMachineBuilder = this.getStateMachineBuilder(machine); 21StateMachine stateMachine = stateMachineBuilder.newStateMachine(state,applicationContext); 22stateMachine.fire(event, context); 23} 24 25 26private StateMachineBuilder getStateMachineBuilder(Class<T> stateMachine){ 27UntypedStateMachineBuilder stateMachineBuilder = builderMap.get(stateMachine.getName()); 28if(stateMachineBuilder == null){ 29stateMachineBuilder = StateMachineBuilderFactory.create(stateMachine,ApplicationContext.class); 30builderMap.put(stateMachine.getName(),stateMachineBuilder); 31} 32return stateMachineBuilder;

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

1/** 2* 店铺审核状态机 3*/ 4@States({ 5@State(name = "audit"), 6@State(name = "agree"), 7@State(name = "reject") 8}) 9@Transitions({ 10@Transit(from = "audit", to = "agree", on = "AGREE", callMethod = "agree"), 11@Transit(from = "audit", to = "reject", on = "REJECT", callMethod = "reject"), 12@Transit(from = "reject", to = "audit", on = "SUBMIT", callMethod = "submit"), 13@Transit(from = "agree", to = "audit", on = "SUBMIT", callMethod = "submit"), 14@Transit(from = "audit", to = "audit", on = "SUBMIT", callMethod = "submit"), 15}) 16@StateMachineParameters(stateType=ShopInfoAuditStatusEnum.class, eventType=ShopInfoAuditEvent.class, contextType=ShopInfoAuditStatusUpdateParam.class) 17public class ShopInfoAuditStateMachine extends AbstractUntypedStateMachine { 18 19 20private ApplicationContext applicationContext; 21 22 23public ShopInfoAuditStateMachine(){} 24 25 26public ShopInfoAuditStateMachine(ApplicationContext applicationContext) { 27this.applicationContext = applicationContext; 28} 29 30 31// 审核通过业务逻辑 32public void agree(ShopInfoAuditStatusEnum fromState, ShopInfoAuditStatusEnum toState, ShopInfoAuditEvent event, ShopInfoAuditStatusUpdateParam param) { 33this.agree(fromState,toState,event,param); 34} 35 36// 审核驳回业务逻辑 37public void reject(ShopInfoAuditStatusEnum fromState, ShopInfoAuditStatusEnum toState, ShopInfoAuditEvent event, ShopInfoAuditStatusUpdateParam param) { 38this.reject(fromState,toState,event,param); 39} 40 41// 提交业务逻辑 42public void submit(ShopInfoAuditStatusEnum fromState, ShopInfoAuditStatusEnum toState, ShopInfoAuditEvent event, ShopInfoAuditStatusUpdateParam param) { 43this.submit(fromState,toState,event,param); 44}

4.5 客户端调用

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

5 总结

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

点赞
收藏

评论区

加载中...

相关推荐

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

面向状态机编程:复杂业务逻辑应对之道

在研发项目中,经常能遇到复杂的状态流转类的业务场景,比如游戏编程中NPC的跳跃、前进、转向等状态变化,电商领域订单的状态变化等。这类情况其实可以有一种优雅的实现方法:状态机。

EA&UML日拱一卒

点击上方【面向对象思考】可快速关注本公众号!行为状态机概念以下内容摘自UML2,今天的内容是说明状态机具有上下文类目的情况。行为状态机可以用于定义下面的场景(不限于)主动类的类目行为一个行为化类目的除了类目行为以外的拥有行为。如果状态机有某种行为类目

MySQL数据库InnoDB存储引擎Log漫游(1)

作者:宋利兵来源:MySQL代码研究(mysqlcode)0、导读本文介绍了InnoDB引擎如何利用UndoLog和RedoLog来保证事务的原子性、持久性原理,以及InnoDB引擎实现UndoLog和RedoLog的基本思路。00–UndoLogUndoLog是为了实现事务的原子性,

Java生鲜电商平台

Java生鲜电商平台订单模块状态机架构设计_说明:在Java生鲜电商平台中订单的状态流转业务__我们知道一个订单会有很多种状态:临时单、已下单、待支付、待收货、待评价、已完成,退货中等等。每一种状态都和其扭转前的状态、在扭转前状态所执行的操作有关。_一实例说明举例一个过程:用户将商品加入购物车,在后台生成了一个所谓的“临