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逻辑判断,提高了代码的可维护性、扩展性,方便管理和测试。
Squirrel状态机-从原理探究到最佳实践的更多相关文章
- KiCad EDA 原理图库的最佳实践
KiCad EDA 原理图库的最佳实践 由于有 Alias 别名元件,可以不用一个每一个元件都有一个元件. 对每种元件类型建议一个元件库. 因为 Value 和 元件名是一样的,所以元件名要尽可能的简 ...
- Guava Cache 原理分析与最佳实践
前言 目前大部分互联网架构 Cache 已经成为了必可不少的一环.常用的方案有大家熟知的 NoSQL 数据库(Redis.Memcached),也有大量的进程内缓存比如 EhCache .Guava ...
- 《深入理解Android:Telephon原理剖析与最佳实践》学习笔记(系统框架)
Android智能手机的系统结构: 智能手机的硬件基本结构大多采用双处理器架构:主处理器和从处理器,主处理器主要运行开放式操作系统以及操作系统之上的应用,负责整个系统的控制,称之为AP,从处理 ...
- java 读取文件最佳实践
1. 前言 Java应用中很常见的一个问题,如何读取jar/war包内和所在路径的配置文件,不同的人根据不同的实践总结出了不同的方案,但其他人应用却会因为环境等的差异发现各种问题,本文则从原理上解释 ...
- 转载--JAVA读取文件最佳实践
1. 前言 Java应用中很常见的一个问题,如何读取jar/war包内和所在路径的配置文件,不同的人根据不同的实践总结出了不同的方案,但其他人应用却会因为环境等的差异发现各种问题,本文则从原理上解释 ...
- atitit.文件上传带进度条的实现原理and组件选型and最佳实践总结O7
atitit.文件上传带进度条的实现原理and组件选型and最佳实践总结O7 1. 实现原理 1 2. 大的文件上传原理::使用applet 1 3. 新的bp 2 1. 性能提升---分割小文件上传 ...
- 《深入理解OSGi:Equinox原理、应用与最佳实践》笔记_1_运行最简单的bundlehelloworld
<深入理解OSGi:Equinox原理.应用与最佳实践>笔记_1_运行最简单的bundlehelloworld 买了周大大的OSGI的书看 先前完全没有基础 就靠这本书看看学学 顺便记一些 ...
- 学习笔记TF061:分布式TensorFlow,分布式原理、最佳实践
分布式TensorFlow由高性能gRPC库底层技术支持.Martin Abadi.Ashish Agarwal.Paul Barham论文<TensorFlow:Large-Scale Mac ...
- Session 的原理及最佳实践
Http协议是基于请求和响应的一种无状态的协议,而通过session可以使得Http应用变得有状态,即可以"记住"客户端的信息.今天就来说说这个session和cookie. Se ...
- Spark Shuffle调优原理和最佳实践
对性能消耗的原理详解 在分布式系统中,数据分布在不同的节点上,每一个节点计算一部份数据,如果不对各个节点上独立的部份进行汇聚的话,我们计算不到最终的结果.我们需要利用分布式来发挥Spark本身并行计算 ...
随机推荐
- Vitepress搭建组件库文档(上)—— 基本配置
在 vite 出现以前,vuepress 是搭建组件库文档不错的工具,支持以 Markdown 方式编写文档.伴随着 vite 的发展,vitepress 已经到了 1.0.0-alpha.22 版本 ...
- k8s运维之pod排错
k8s运维之pod排错 K8S是一个开源的,用于管理云平台中多个主机上的容器化应用,Kubernetes的目标是让部署容器化变得简单并且高效 K8S的核心优势: 1,基于yaml文件实现容器的自动创建 ...
- .NET性能优化-复用StringBuilder
在之前的文章中,我们介绍了dotnet在字符串拼接时可以使用的一些性能优化技巧.比如: 为StringBuilder设置Buffer初始大小 使用ValueStringBuilder等等 不过这些都多 ...
- UML建模语言、设计原则、设计模式
1.UML统一建模语言 定义:用于软件系统设计与分析的语言工具 目的:帮助开发人员更好的梳理逻辑.思路 学习地址:UML概述_w3cschool 官网:https://www.omg.org/spec ...
- 卸载virtualbox中linux虚拟机的增强工具
报错信息 vboxclient:the virtualbox kernel service is not running 前言 我由virtualbox换到vmware 遇到了这个问题,很烦每次都通知 ...
- Linux禁止摄像头自动曝光(手动调节曝光)
前言 很多摄像头具有自动曝光的功能,例如在较暗的调节下,提高曝光率,在较亮的调节下降低曝光.下面简单介绍在linux平台俩种方式来修改自动曝光. 软件调节(图形化界面) 安装qv4l2 sudo ap ...
- Spring Boot实现任意位置的properties及yml文件内容配置与获取
〇.参考资料 1.Spring Boot 中文乱码问题解决方案汇总 https://blog.51cto.com/u_15236724/5372824 2.spring boot读取自定义配置prop ...
- 【Java SE进阶】Day02 Collection、Iterator、泛型
一.Collection集合 1.概述 数组存元素,集合存对象(类型可以不一样) 2.框架分类 单列:Collection List ArrayList LinkedList Set HashSet ...
- 【大数据面试】【框架】Linux命令、Shell工具、常见Shell脚本(群起脚本、数仓导入)
一.Linux 1.常用高级命令 ps -ef:查看进程详情,ps -ef|grep dae可以搜索指定进程,-e表示环境变量 ps -au:以用户为主的详细格式,显示进程平均占用资源,不包括cmd列 ...
- python模块/导入模块
索引取值与迭代取值的差异 l1 = [1,2,3,4,5] 1.索引取值 可以任意位置任意次数的取值 不支持无序类型的数据取值 print(l1[3]) print(l1[3]) #可以直接获取任意位 ...