还在用ifelse来写业务?了解下Spring状态机

打印 上一主题 下一主题

主题 857|帖子 857|积分 2571

状态机之所以强大,是因为其举动在启动时就以固定的方式定义了操作规则,从而确保了一贯的连贯性和相对较高的可调试性。关键在于,应用程序处于且仅可能处于有限数量的状态中。然后,某些事件发生会使得应用从一个状态过渡到另一个状态。状态机由触发器驱动,这些触发器基于事件或计时器。
筹划高条理逻辑并将其置于应用程序外部,然后通过多种方式与状态机交互,这种方式要简单得多。可以通过发送事件、监听状态机的举动或请求当前状态来与状态机进行交互。
当开发者意识到代码库开始变得般混乱不堪时,就会在现有项目中引入状态机。面条代码体现为无尽的、层级化的IF、ELSE和BREAK子句布局,当事变变得过于复杂时,编译器大概应该发起开发者暂停一下,先苏息一下。状态机的引入有助于将复杂多变的应用程序状态转换过程构造得更为有序和清晰,从而避免代码陷入难以维护的境地。
什么是状态

状态是状态机可能处于的一种模型。相比于在通用文档中使用抽象概念,通过实际生活中的例子来描述状态通常更为直观易懂。以一个简单的键盘为例——我们大多数人天天都使用它。如果你有一个标准键盘,左侧有平凡键,右侧有数字小键盘,你可能会注意到,根据Numlock(数字锁定)是否激活,数字小键盘可以处于两种不同的状态。如果没有激活,按下数字小键盘的按键会实现方向导航等功能;如果数字小键盘被激活,则按下这些键将输入数字。本质上,键盘的数字小键盘部分可以处于两种不同的状态。
将状态的概念联系到编程上,这意味着我们可以不再依赖于标志位、嵌套的if/else/break语句或其他不切实际(有时甚至是曲折复杂的)逻辑,而是可以通过状态、状态变量或与状态机的交互来处理标题。换句话说,在编程中运用状态这一概念,能够帮助我们更清晰地构造和管理程序的不同状态及其转换过程。
什么是状态机

状态机是一种理论模型,它描述了一个对象在其生命周期内可能履历的有限数量的状态及其之间的转换规则。每个状态都有触发状态迁移的条件(通常是事件),并且可以关联执行的动作。
状态机的核心在于状态变迁和事件驱动,适合处理异步和并发的情况。状态机强调的是系统当前所处的状态,并且关注于系统如何根据吸收到的外部事件或内部条件进行状态转变。
状态机最常见于嵌入式系统、用户界面交互筹划、游戏开发、网络协议解析等领域。
以下以游戏马里奥的状态切换为例,来理解状态机的使用场景:
graph LR    A[小马里奥] -->|吃蘑菇| B[超级马里奥]    B -->|吃花| C[火焰马里奥]    C -->|被敌人碰到| B    B -->|被敌人碰到| A与状态筹划模式的区别

在面向对象编程中,状态筹划模式是一种举动型筹划模式,允许对象在其内部状态改变时改变其举动。该模式通过将每一个状态封装成一个类,使恰当对象的状态发生改变时,它的举动也随之改变,同时能够使代码更加清晰和模块化。
在状态筹划模式中,每个状态是一个单独的类实例,这些类通常会实现一个公共接口,以便上下文对象可以调用得当的方法,而无需知道详细当前处于哪种状态。上下文对象(context)持有对当前状态对象的引用,并在吸收到特定事件时调用状态对象的方法来处理事件并可能导致状态切换。
联系

  • 状态筹划模式是对状态机理论的一种实现,它把状态机的概念应用于软件筹划中,使用面向对象技术实现了状态的抽象、封装和扩展性。
区别

  • 状态机是一个抽象的概念,可以不依赖于任何特定的编程语言或筹划模式独立存在。
  • 状态筹划模式则是详细的编程实践,是针对解决状态转换标题标一种筹划解决方案,特别适用于面向对象情况下的复杂状态管理。
与流程引擎的区别

流程引擎(Business Process Management Engine, BPMN Engine)是实现业务流程管理(BPM)的软件组件,主要用于执行和监控预定义的工作流程。这些工作流程通常包括一系列顺序执行的任务或活动,具有明确的开始点、结束点和中间过程。
流程引擎支持更复杂的流程布局,如并行分支、同步归并、循环等,并提供了丰富的建模语言(如BPMN)来可视化表现流程逻辑。流程引擎不仅关注状态转移,还注意任务分配、资源调度、事件处理以及流程实例的团体生命周期管理。
流程引擎适用于企业级应用中需要自动化、规范化和优化的复杂业务流程,好比采购审批流程、贷款审批流程、订单处理流程等。
区别与联系:

  • 目标性不同: 状态机主要解决状态变化的标题,而流程引擎则更多地关注流程的团体构造和执行。
  • 布局灵活性: 状态机布局相对简单,特别适合清晰、固定的流程;流程引擎支持多条理、多路径的复杂流程,允许动态调整和扩展。
  • 参与角色: 状态机侧重于机器层面的自动化处理,流程引擎则常涉及人的参与决策和协同工作。
  • 集成度: 流程引擎通常包含更多的管理和监控功能,能够与构造其他系统紧密集成,提供强大的审计跟踪、异常处理和数据分析能力。
  • 联系: 在实际项目中,状态机的概念和机制可能会作为流程引擎的一部分被采用,尤其是在流程中有明显的状态变迁环节时。同时,两者都可以用作业务规则和流程规范的有效工具,只不过各自聚焦的领域和复杂程度有所差异。
什么是Spring状态机

Spring Statemachine(SSM)是一个框架,允许应用程序开发者在Spring应用中使用传统的状态机概念。SSM提供了以下功能:

  • 为简单用例提供易于使用的单层(一级)状态机。
  • 采用分层状态机布局,便于设置复杂状态。
  • 状态机区域以支持更为复杂的状态设置。
  • 支持触发器、转换、守卫和动作的使用。
  • 提供类型安全的设置适配器。
  • 集成了状态机事件监听器。
  • 与Spring IoC(控制反转)集成,可将Bean关联至状态机。
SSM有哪些使用场景

项目适于使用状态机的场景包括:

  • 当你可以将应用程序或其部分布局表现为一系列状态时,该项目是应用状态机的良好候选者。
  • 你希望将复杂的逻辑拆分为更小、更易于管理的任务。
  • 应用程序已经存在并发标题,比方异步操作导致的标题。
在以下情况下,实际上你已经在实行实现一个状态机:

  • 使用布尔标志或枚举来模拟各种情况。这意味着你的代码可能在通过这些标志和枚举跟踪不同状态。
  • 拥有仅在应用程序生命周期中的某些阶段才有意义的变量。这暗示了状态变化对程序流程的影响。
  • 正在循环遍历if-else布局(或者更糟糕的是,多个这样的布局),检查特定标志或枚举是否已设置,然后根据这些标志和枚举是否存在及其组合进一步判断接下来的操作。这种编程方式本质上是在手动处理状态转移,而采用状态机可以更清晰、规范地表述并简化此类复杂的状态转换逻辑。
如何集成SSM

需要在maven或者gradle中ssm的依赖。
  1. <dependency>
  2.     <groupId>org.springframework.statemachine</groupId>
  3.     <artifactId>spring-statemachine-starter</artifactId>
  4.     <version>4.0.0</version>
  5. </dependency>
复制代码
  1. implementation 'org.springframework.statemachine:spring-statemachine-starter:4.0.0'
复制代码
如何使用SSM

下面以一个简单的例子来说明如何使用SSM。
  1. // 定义对应状态和事件的枚举:
  2. public enum States {
  3.     SI, S1, S2
  4. }
  5. public enum Events {
  6.     E1, E2
  7. }
  8. // 定义状态机的配置
  9. import java.util.EnumSet;
  10. @Configuration // 标识为配置类
  11. @EnableStateMachine // 启用状态机功能
  12. public class StateMachineConfig
  13.         extends EnumStateMachineConfigurerAdapter<States, Events> {
  14.     /**
  15.      * 配置状态机的全局属性,如自动启动和状态监听器。
  16.      *
  17.      * @param config 状态机配置构建器
  18.      * @throws Exception 如果配置过程中发生错误
  19.      */
  20.     @Override
  21.     public void configure(StateMachineConfigurationConfigurer<States, Events> config)
  22.             throws Exception {
  23.         config
  24.                 .withConfiguration()
  25.                 .autoStartup(true) // 设置状态机自动启动
  26.                 .listener(listener()); // 注册状态改变监听器
  27.     }
  28.     /**
  29.      * 配置状态机的状态。
  30.      *
  31.      * @param states 状态配置构建器
  32.      * @throws Exception 如果配置过程中发生错误
  33.      */
  34.     @Override
  35.     public void configure(StateMachineStateConfigurer<States, Events> states)
  36.             throws Exception {
  37.         states
  38.                 .withStates()
  39.                 .initial(States.SI) // 设置初始状态为SI
  40.                 .states(EnumSet.allOf(States.class)); // 将所有枚举状态添加到状态机
  41.     }
  42.     /**
  43.      * 配置状态机的转换。
  44.      *
  45.      * @param transitions 转换配置构建器
  46.      * @throws Exception 如果配置过程中发生错误
  47.      */
  48.     @Override
  49.     public void configure(StateMachineTransitionConfigurer<States, Events> transitions)
  50.             throws Exception {
  51.         transitions
  52.                 .withExternal() // 配置外部触发的转换
  53.                 .source(States.SI).target(States.S1).event(Events.E1) // 定义从SI到S1的转换,由事件E1触发
  54.                 .and() // 连接另一个转换配置
  55.                 .withExternal() // 另一个外部触发的转换
  56.                 .source(States.S1).target(States.S2).event(Events.E2); // 定义从S1到S2的转换,由事件E2触发
  57.     }
  58.     /**
  59.      * 创建并返回一个状态机监听器,用于监听状态的改变。
  60.      *
  61.      * @return 状态机监听器实例
  62.      */
  63.     @Bean
  64.     public StateMachineListener<States, Events> listener() {
  65.         return new StateMachineListenerAdapter<States, Events>() {
  66.             @Override
  67.             public void stateChanged(State<States, Events> from, State<States, Events> to) {
  68.                 if(from != null){
  69.                     System.out.println("State change from " + from.getId());
  70.                 }
  71.                 System.out.println("State change to " + to.getId());
  72.             }
  73.         };
  74.     }
  75. }
复制代码
测试代码如下:
  1. @RestController
  2. @Tag(name = "状态机", description = "状态机")
  3. public class StateController{
  4.     @Autowired
  5.     private StateService stateService;
  6.     @GetMapping(value = "改变状态")
  7.     @Operation(description = "改变状态")
  8.     public void change() {
  9.         stateService.changeState();
  10.     }
  11. }
  12. /**
  13. * 状态机演示服务
  14. */
  15. @Service
  16. public class StateService {
  17.     @Autowired
  18.     private StateMachine<States, Events> stateMachine;
  19.     public void changeState() {
  20.         stateMachine.sendEvent(Events.E1);
  21.         stateMachine.sendEvent(Events.E2);
  22.     }
  23. }
复制代码
服务层的输出的效果如下:
  1. State change to SI
  2. State change from SI
  3. State change to S1
  4. State change from S1
  5. State change to S2
复制代码
以上代码只是简单演示了SSM的集成和使用demo。实际业务场景可能更为复杂,需要根据实际需求进行扩展。
除了状态,要更好的使用SSM还需要理解伪状态等很多概念,好比Junction(允许多个传入转换)、 Fork(一个或多个区域的显式入口)、Join (将源自不同区域的多个过渡归并在一起)。这部分内容将在后续文章中进行介绍。
参考

关于作者

来自一线全栈程序员nine的八年探索与实践,持续迭代中。接待关注公众号“雨林寻北”或添加个人卫星codetrend(备注技术)。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

悠扬随风

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表