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 环境依赖
<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); }
5 总结
状态机很好的帮我们处理了对象状态的流转、事件的监听以及外界的各种事件的响应。从代码设计角度减少了大量if-else/switch-case逻辑判断,提高了代码的可维护性、扩展性,方便管理和测试。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
感受Vue3的魔法力量
近半年有幸参与了一个创新项目,由于没有任何历史包袱,所以选择了Vue3技术栈,总体来说感受如下: • setup语法糖<script setup lang="ts">摆脱了书写声明式的代码,用起来很流畅,提升不少效率 • 可以通过Composition API(组合式API)封装可复用逻辑,将UI和逻辑分离,提高复用性,view层代码展示更清晰 • 和Vue3更搭配的状态管理库Pinia,少去了很多配置,使用起来更便捷 • 构建工具Vite,基于ESM和Rollup,省去本地开发时的编译步骤,但是build打包时还是会编译(考虑到兼容性) • 必备VSCode插件Volar,支持Vue3内置API的TS类型推断,但是不兼容Vue2,如果需要在Vue2和Vue3项目中切换,比较麻烦 当然也遇到一些问题,最典型的就是响应式相关的问题 响应式篇 本篇主要借助watch函数,理解ref、reactive等响应式数据/状态,有兴趣的同学可以查看Vue3源代码部分加深理解, watch数据源可以是ref (包括计算属性)、响应式对象、getter 函数、或多个数据源组成的数组 impo...
- 下一篇
EasyGoAdmin 敏捷开发框架 Beego+AntdVue 版本 v2.0.0 发布
v2.0.0 更新内容: 1、调整后端服务注册目录boot,启动项目时自动注册; 2、变成自定义模块目录,统一放在templdates目录下; 3、解决数据列表日期格式无法正常显示的问题; 4、优化用户管理模块和会员管理模块的城市选择功能; 5、解决广告管理、用户管理、会员管理等模块日期显示和存储报错的问题; 6、重写返回对象Vo自定义模板,新增列表和信息Vo; 7、更新代码生成器服务类,解决生成组件显示异常的问题; 8、修复近期用户使用过程中反馈的BUG; 一款 Go 语言基于 Beego、Vue、AntDesign、MySQL 等框架精心打造的一款模块化、插件化、高性能的前后端分离架构敏捷开发框架,可快速搭建前后端分离后台管理系统,本着简化开发、提升开发效率的初衷,框架自研了一套个性化的组件,实现了可插拔的组件式开发方式,同时为了敏捷快速开发,框架特地集成了代码生成器,完全自主研发了自定义 GO 后端服务模板和前端 Vue 自定义模板,可以根据已建好的表结构,可以快速的一键生成整个模块的所有代码和增删改查等等功能业务,真正实现了低代码开发方式,极大的节省了人力成本的同时提高了开发效...
相关文章
文章评论
共有0条评论来说两句吧...