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

2024-03-15 16:44

本文主要是介绍还在用ifelse来写业务?了解下Spring状态机,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里插入图片描述

状态机之所以强大,是因为其行为在启动时就以固定的方式定义了操作规则,从而确保了一贯的连贯性和相对较高的可调试性。关键在于,应用程序处于且仅可能处于有限数量的状态中。然后,某些事件发生会使得应用从一个状态过渡到另一个状态。状态机由触发器驱动,这些触发器基于事件或计时器。

设计高层次逻辑并将其置于应用程序外部,然后通过多种方式与状态机交互,这种方式要简单得多。可以通过发送事件、监听状态机的行为或请求当前状态来与状态机进行交互。

当开发者意识到代码库开始变得般混乱不堪时,就会在现有项目中引入状态机。面条代码表现为无尽的、层级化的IF、ELSE和BREAK子句结构,当事情变得过于复杂时,编译器或许应该建议开发者暂停一下,先休息一下。状态机的引入有助于将复杂多变的应用程序状态转换过程组织得更为有序和清晰,从而避免代码陷入难以维护的境地。

什么是状态

状态是状态机可能处于的一种模型。相比于在通用文档中使用抽象概念,通过现实生活中的例子来描述状态通常更为直观易懂。以一个简单的键盘为例——我们大多数人每天都使用它。如果你有一个标准键盘,左侧有普通键,右侧有数字小键盘,你可能会注意到,根据Numlock(数字锁定)是否激活,数字小键盘可以处于两种不同的状态。如果没有激活,按下数字小键盘的按键会实现方向导航等功能;如果数字小键盘被激活,则按下这些键将输入数字。本质上,键盘的数字小键盘部分可以处于两种不同的状态。

将状态的概念联系到编程上,这意味着我们可以不再依赖于标志位、嵌套的if/else/break语句或其他不切实际(有时甚至是曲折复杂的)逻辑,而是可以通过状态、状态变量或与状态机的交互来处理问题。换句话说,在编程中运用状态这一概念,能够帮助我们更清晰地组织和管理程序的不同状态及其转换过程。

什么是状态机

状态机是一种理论模型,它描述了一个对象在其生命周期内可能经历的有限数量的状态及其之间的转换规则。每个状态都有触发状态迁移的条件(通常是事件),并且可以关联执行的动作。

状态机的核心在于状态变迁和事件驱动,适合处理异步和并发的情况。状态机强调的是系统当前所处的状态,并且关注于系统如何根据接收到的外部事件或内部条件进行状态转变。

状态机最常见于嵌入式系统、用户界面交互设计、游戏开发、网络协议解析等领域。

以下以游戏马里奥的状态切换为例,来理解状态机的使用场景:

吃蘑菇
吃花
被敌人碰到
被敌人碰到
小马里奥
超级马里奥
火焰马里奥

与状态设计模式的区别

在面向对象编程中,状态设计模式是一种行为型设计模式,允许对象在其内部状态改变时改变其行为。该模式通过将每一个状态封装成一个类,使得当对象的状态发生改变时,它的行为也随之改变,同时能够使代码更加清晰和模块化。

在状态设计模式中,每个状态是一个单独的类实例,这些类通常会实现一个公共接口,以便上下文对象可以调用适当的方法,而无需知道具体当前处于哪种状态。上下文对象(context)持有对当前状态对象的引用,并在接收到特定事件时调用状态对象的方法来处理事件并可能导致状态切换。

联系

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

区别

  • 状态机是一个抽象的概念,可以不依赖于任何特定的编程语言或设计模式独立存在。
  • 状态设计模式则是具体的编程实践,是针对解决状态转换问题的一种设计解决方案,特别适用于面向对象环境下的复杂状态管理。

与流程引擎的区别

**流程引擎(Business Process Management Engine, BPMN Engine)**是实现业务流程管理(BPM)的软件组件,主要用于执行和监控预定义的工作流程。这些工作流程通常包括一系列顺序执行的任务或活动,具有明确的开始点、结束点和中间过程。

流程引擎支持更复杂的流程结构,如并行分支、同步合并、循环等,并提供了丰富的建模语言(如BPMN)来可视化表示流程逻辑。流程引擎不仅关注状态转移,还注重任务分配、资源调度、事务处理以及流程实例的整体生命周期管理。

流程引擎适用于企业级应用中需要自动化、规范化和优化的复杂业务流程,比如采购审批流程、贷款审批流程、订单处理流程等。

区别与联系:

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

什么是Spring状态机

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

  1. 为简单用例提供易于使用的单层(一级)状态机。
  2. 采用分层状态机结构,便于配置复杂状态。
  3. 状态机区域以支持更为复杂的状态配置。
  4. 支持触发器、转换、守卫和动作的使用。
  5. 提供类型安全的配置适配器。
  6. 集成了状态机事件监听器。
  7. 与Spring IoC(控制反转)集成,可将Bean关联至状态机。

SSM有哪些使用场景

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

  1. 当你可以将应用程序或其部分结构表示为一系列状态时,该项目是应用状态机的良好候选者。
  2. 你希望将复杂的逻辑拆分为更小、更易于管理的任务。
  3. 应用程序已经存在并发问题,例如异步操作导致的问题。

在以下情况下,实际上你已经在尝试实现一个状态机:

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

如何集成SSM

需要在maven或者gradle中ssm的依赖。

<dependency><groupId>org.springframework.statemachine</groupId><artifactId>spring-statemachine-starter</artifactId><version>4.0.0</version>
</dependency>
implementation 'org.springframework.statemachine:spring-statemachine-starter:4.0.0'

如何使用SSM

下面以一个简单的例子来说明如何使用SSM。

// 定义对应状态和事件的枚举:
public enum States {SI, S1, S2
}public enum Events {E1, E2
}// 定义状态机的配置
import java.util.EnumSet;@Configuration // 标识为配置类
@EnableStateMachine // 启用状态机功能
public class StateMachineConfigextends EnumStateMachineConfigurerAdapter<States, Events> {/*** 配置状态机的全局属性,如自动启动和状态监听器。** @param config 状态机配置构建器* @throws Exception 如果配置过程中发生错误*/@Overridepublic void configure(StateMachineConfigurationConfigurer<States, Events> config)throws Exception {config.withConfiguration().autoStartup(true) // 设置状态机自动启动.listener(listener()); // 注册状态改变监听器}/*** 配置状态机的状态。** @param states 状态配置构建器* @throws Exception 如果配置过程中发生错误*/@Overridepublic void configure(StateMachineStateConfigurer<States, Events> states)throws Exception {states.withStates().initial(States.SI) // 设置初始状态为SI.states(EnumSet.allOf(States.class)); // 将所有枚举状态添加到状态机}/*** 配置状态机的转换。** @param transitions 转换配置构建器* @throws Exception 如果配置过程中发生错误*/@Overridepublic void configure(StateMachineTransitionConfigurer<States, Events> transitions)throws Exception {transitions.withExternal() // 配置外部触发的转换.source(States.SI).target(States.S1).event(Events.E1) // 定义从SI到S1的转换,由事件E1触发.and() // 连接另一个转换配置.withExternal() // 另一个外部触发的转换.source(States.S1).target(States.S2).event(Events.E2); // 定义从S1到S2的转换,由事件E2触发}/*** 创建并返回一个状态机监听器,用于监听状态的改变。** @return 状态机监听器实例*/@Beanpublic StateMachineListener<States, Events> listener() {return new StateMachineListenerAdapter<States, Events>() {@Overridepublic void stateChanged(State<States, Events> from, State<States, Events> to) {if(from != null){System.out.println("State change from " + from.getId());}System.out.println("State change to " + to.getId());}};}
}

测试代码如下:

@RestController
@Tag(name = "状态机", description = "状态机")
public class StateController{@Autowiredprivate StateService stateService;@GetMapping(value = "改变状态")@Operation(description = "改变状态")public void change() {stateService.changeState();}
}
/*** 状态机演示服务*/
@Service
public class StateService {@Autowiredprivate StateMachine<States, Events> stateMachine;public void changeState() {stateMachine.sendEvent(Events.E1);stateMachine.sendEvent(Events.E2);}
}

服务层的输出的结果如下:

State change to SI
State change from SI
State change to S1
State change from S1
State change to S2

以上代码只是简单演示了SSM的集成和使用demo。实际业务场景可能更为复杂,需要根据实际需求进行扩展。

除了状态,要更好的使用SSM还需要理解伪状态等很多概念,比如Junction(允许多个传入转换)、 Fork(一个或多个区域的显式入口)、Join (将源自不同区域的多个过渡合并在一起)。这部分内容将在后续文章中进行介绍。

参考

  • A State Machine Crash Course - spring-statemachine https://docs.spring.io/spring-statemachine/docs/4.0.0/reference/index.html#crashcourse

关于作者

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

这篇关于还在用ifelse来写业务?了解下Spring状态机的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/812607

相关文章

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Java判断多个时间段是否重合的方法小结

《Java判断多个时间段是否重合的方法小结》这篇文章主要为大家详细介绍了Java中判断多个时间段是否重合的方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录判断多个时间段是否有间隔判断时间段集合是否与某时间段重合判断多个时间段是否有间隔实体类内容public class D

IDEA编译报错“java: 常量字符串过长”的原因及解决方法

《IDEA编译报错“java:常量字符串过长”的原因及解决方法》今天在开发过程中,由于尝试将一个文件的Base64字符串设置为常量,结果导致IDEA编译的时候出现了如下报错java:常量字符串过长,... 目录一、问题描述二、问题原因2.1 理论角度2.2 源码角度三、解决方案解决方案①:StringBui

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

Java中ArrayList和LinkedList有什么区别举例详解

《Java中ArrayList和LinkedList有什么区别举例详解》:本文主要介绍Java中ArrayList和LinkedList区别的相关资料,包括数据结构特性、核心操作性能、内存与GC影... 目录一、底层数据结构二、核心操作性能对比三、内存与 GC 影响四、扩容机制五、线程安全与并发方案六、工程

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

Spring AI集成DeepSeek的详细步骤

《SpringAI集成DeepSeek的详细步骤》DeepSeek作为一款卓越的国产AI模型,越来越多的公司考虑在自己的应用中集成,对于Java应用来说,我们可以借助SpringAI集成DeepSe... 目录DeepSeek 介绍Spring AI 是什么?1、环境准备2、构建项目2.1、pom依赖2.2