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

article/2025/9/11 1:53:08

作者:京东物流 郑朋辉

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逻辑判断,提高了代码的可维护性、扩展性,方便管理和测试。


http://chatgpt.dhexx.cn/article/3JyFs5KX.shtml

相关文章

squirrel(松鼠)状态机的介绍及使用

squirrel&#xff08;松鼠&#xff09;状态机 依赖 <dependency><groupId>org.squirrelframework</groupId><artifactId>squirrel-foundation</artifactId><version>0.3.8</version> </dependency>状态机描述 参考&#xf…

将图片转换为base64编码

1、base64编码简介 Base64是一种可逆的编码方式&#xff0c;简单来讲就是一种将64个Ascii字符来表示成二进制数据的方法。主要用于将不可打印的字符转换成可打印字符&#xff0c;或者简单的说将二进制数据编码成Ascii字符。Base64是网络上最常用的传输8bit字节数据的编码方式之…

前端理解base64

一、背景&#xff1a;ascii码 字符>二进制 计算机中所有数据的存储都是以二进制模式&#xff0c;比如想要存储abcd需将其转化为二进制&#xff0c;具体用哪些二进制来表示哪个符号*&#xff0c;有一个统一的编码规则&#xff0c;这就是ascii。 ASCII 码使用指定的7 位或8…

Web 图片Base64编码

转载自: 【前端攻略】&#xff1a;玩转图片Base64编码 引言   图片处理在前端工作中可谓占据了很重要的一壁江山。而图片的 base64 编码可能相对一些人而言比较陌生&#xff0c;本文不是从纯技术的角度去讨论图片的 base64 编码。标题略大&#xff0c;不过只是希望通过一些…

base64编码解码器【C++】

在线编码解码工具https://base64.us/所有结果可以使用上述网站检验。 什么是base64编码&#xff1f; base64编码是一种编码方式 用64 1 个字符表示字符 本质是将三位8比特字符扩增为四位8比特字符&#xff0c;但是这么说开始可能很闷逼。 给个图&#xff0c;这个编码是以3字…

前端图片在线转换Base64 图片编码Base64

首先讲一下什么是图片Base64编码&#xff1a; 简单来说&#xff0c;图片的base64编码就是可以将一副图片数据编码成一串字符串&#xff0c;使用该字符串代替图像地址。 为什么要用Base64&#xff1f; 我们知道&#xff0c;我们所看到的网页上的每一个图片&#xff0c;都是需…

理解图片base64编码

什么是编码解码 编码&#xff1a;利用特定的算法&#xff0c;对原始内容进行处理&#xff0c;生成运算后的内容&#xff0c;形成另一种数据的表现形式&#xff0c;可以根据算法&#xff0c;再还原回来&#xff0c;这种操作称之为编码。 解码&#xff1a;利用编码使用的算法的…

【HTML5】------- 图片Base64编码详解

引言   图片处理在前端工作中可谓占据了很重要的一壁江山。而图片的 base64 编码可能相对一些人而言比较陌生&#xff0c;本文不是从纯技术的角度去讨论图片的 base64 编码。标题略大&#xff0c;不过只是希望通过一些浅显的论述&#xff0c;让你知道什么是图片的 base64 编码…

记录:Base64编码步骤详解

文章目录 具体转换步骤实例演示&#xff1a;AbC实例演示二&#xff1a;字节数不足三个例&#xff1a;A例&#xff1a;8H ASCII编码表Base64编码表 具体转换步骤 第一步&#xff0c;将待转换的字符串每三个字节分为一组&#xff0c;每个字节占8bit&#xff0c;那么共有24个二进…

对图片进行base64 编码

原文地址&#xff1a;https://www.cnblogs.com/coco1s/p/4375774.html 引言 图片处理在前端工作中可谓占据了很重要的一壁江山。而图片的 base64 编码可能相对一些人而言比较陌生&#xff0c;本文不是从纯技术的角度去讨论图片的 base64 编码。标题略大&#xff0c;不过只是希…

php将图片转成base64编码,利用PHP将图片转换成base64编码的实现方法

利用PHP将图片转换成base64编码的实现方法 先来说一下为什么我们要对图片base64编码 base64是当前网络上最为常见的传输8Bit字节代码的编码方式其中之一。base64主要不是加密,它主要的用途是把某些二进制数转成普通字符用于网络传输。由于这些二进制字符在传输协议中属于控制字…

Hex编码与Base64编码

Hex编码与Base64编码 什么是明文什么是编码Hex编码Hex编码实现细节自定义Hex编码 Base64编码Base64编码实现细节自定义Base64编码 什么是明文 介绍什么是编码之前&#xff0c;首先了解下什么是明文&#xff0c;表示其本身意思的&#xff0c;他就是明文 比如你看到"E6988…

Base64编码linux服务器与网页编码不一致问题

一、起因 在一次环境变量注入的时候&#xff0c;使用Base64将密码编码传入&#xff0c;但是传入后无法登录&#xff0c;但是登录到容器内&#xff0c;使用明文却能登录 二、排错过程 出现这种问题&#xff0c;首先判断密文是否有误&#xff0c;但是在linux服务器内反复base64&…

mysql 图片base64_关于图片的Base64编码

什么是Base64编码 Base64编码是一种图片处理格式,通过特定的算法将图片编码成一长串字符串,在页面上显示的时候,可以用该字符串来代替图片的url属性。 base64编码就是长得像下面这样子的代码: thunder://QUFodHRwOi8vd3d3LmJhaWR1LmNvbS9pbWcvc3NsbTFfbG9nby5naWZaWg== 上面…

前端Base64编码

Base64编码 什么是Base64编码 我们所看到的网页上的每一个图片&#xff0c;都是需要消耗一个 http 请求下载而来的&#xff0c;不管如何&#xff0c;图片的下载始终都要向服务器发出请求&#xff0c;要是图片的下载不用向服务器发出请求&#xff0c;而可以随着 HTML 的下载同…

图片Base64编码

图片处理在前端工作中可谓占据了很重要的一壁江山。而图片的 base64 编码可能相对一些人而言比较陌生&#xff0c;本文不是从纯技术的角度去讨论图片的 base64 编码。标题略大&#xff0c;不过只是希望通过一些浅显的论述&#xff0c;让你知道什么是图片的 base64 编码&#xf…

什么是Base64 编码,Base64 编码有哪些优缺点?

很多朋友在工作中&#xff0c;可能经常会用到Base64编码。Base64编码是网络上很常见的用于8Bit字节码的编码方式之一&#xff0c;那么&#xff0c;大家知道为什么要使用Base64 编码&#xff0c;Base64 编码有哪些优缺点呢&#xff1f;下面&#xff0c;我们一起来研究一下。 什…

前端Base64编码知识,一文打尽

原文: https://juejin.cn/post/6989391487200919566 作者: 云的世界 掘金专栏: 前端基础进阶 大厂技术 高级前端 Node进阶 点击上方 程序员成长指北&#xff0c;关注公众号 回复1&#xff0c;加入高级Node交流群 前言 本文将详细的介绍前端 Base64 编码知识&#xff0c;探索…

Python模板库Mako的语法

原文链接&#xff1a;http://www.yeolar.com/note/2012/08/28/mako-syntax/ Mako模板从一个包含各种类型的内容的文本流解析得到&#xff0c;包括XML、HTML、email文本等。模板还可以包含Mako指令&#xff0c;用来表示变量和表达式替换、控制结构、服务器端注释、整块Python代码…

Django mako 的使用(七)

Django mako 的使用 6.6 mako 的使用 6.6 mako 的使用 安装 mako pip install mako 创建子应用 makos python .\manage.py startapp makos 创建 base_render.py 文件 from django.http import HttpResponse from django.template import RequestContext from django.templat…