自定义实现简版状态机

news2025/2/24 14:02:39

状态机(State Machine)是一种用于描述系统行为的数学模型,广泛应用于计算机科学、工程和自动化等领域。它通过定义系统的状态、事件和转移来模拟系统的动态行为。

基本概念

  • 状态(State):系统在某一时刻的特定条件或模式。

  • 事件(Event):触发状态转移的外部输入或条件变化。

  • 转移(Transition):状态因事件而发生的改变。

  • 动作(Action):在状态转移或处于某一状态时执行的操作。

简单点来说就是某一个流程当前状态A->事件1->状态B, 或者状态A->事件2->C的一个流程, 实现起来也是比较简单的

自定义状态机 

基本原理就是根据状态机的概念简单实现一下, 根据读取json文件来配置整个流程

状态机配置

@Data
public class StateMachineConfig {
    /**
     * 状态机名称(Bean名称)
     */
    private String name;
    /**
     * 状态机配置
     */
    private List<Transition> transitions;

    @Data
    public static class Transition {
        /**
         * 当前状态
         */
        private String from;
        /**
         * 事件
         */
        private String event;
        /**
         * 下一个状态
         */
        private String to;
    }
}
{
  "name": "orderProcess",
  "transitions": [
    {
      "from": "A",
      "event": "event1",
      "to": "B"
    },
    {
      "from": "A",
      "event": "event2",
      "to": "C"
    },
    {
      "from": "B",
      "event": "event3",
      "to": "C"
    },
    {
      "from": "C",
      "event": "event1",
      "to": "D"
    }
  ]
}

状态机实例

这个类主要是状态机的实例, 也是实现状态机的核心类, 主要实现为Table类, 简单点说就是两个key确定一个value

/**
 * 状态机实例类
 *
 * @author zzt
 * @version 1.0.0
 */
@Slf4j
public class SimpleStateMachine {

    /**
     * 状态转换关系
     */
    private final Table<String, String, String> table = HashBasedTable.create();

    /**
     * 添加状态转换关系
     * 该方法用于在状态机中添加从当前状态到新状态的转换关系。转换关系由当前状态、事件和新状态组成。
     *
     * @param fromState 当前状态,表示转换的起始状态。
     * @param event     触发状态转换的事件。
     * @param toState   转换后的新状态。
     */
    public void addTransition(String fromState, String event, String toState) {
        table.put(fromState, event, toState);
    }

    /**
     * 发送事件以触发状态转换
     * 此方法根据当前状态和接收到的事件来确定是否需要进行状态转换
     * 如果当前状态和事件的组合在转换表中定义了状态转换,则执行转换
     * 否则,输出没有可用转换的消息
     *
     * @param currentState 当前状态
     * @param event        触发状态转换的事件
     * @return 返回新状态
     */
    public String sendEvent(String currentState, String event) {
        //获取下一个状态
        String to = table.get(currentState, event);
        if (StringUtils.isBlank(to)) {
            log.error("没有可用转换, 请检查配置文件,currentState: [{}], event: [{}]", currentState, event);
            throw new BusinessException("获取状态异常!");
        }
        return to;
    }
}

 状态机工厂

状态机工厂主要就是用来生产状态机实例的, 在实际项目中可能会配置多个流程,因此需要生成多个状态机实例, 当时用时只需要从容器中获取一个实例即可

/**
 * 状态机实例工厂,负责创建状态机并将其注册到 Spring IOC 容器中
 *
 * @author zzt
 * @version 1.0.0
 */
@Slf4j
@Data
@Configuration
@ConfigurationProperties(prefix = "state-machine")
public class StateMachineFactory {

    /**
     * 是否启用状态机
     */
    private Boolean enable = false;

    /**
     * 状态机配置文件路径(resources目录下的包)
     */
    private String path = "";

    private final ApplicationContext applicationContext;

    /**
     * 初始化所有状态机并注册到 Spring IOC 容器中
     */
    @Bean
    public void registerStateMachines() {
        if (enable) {
            // 获取所有状态机配置
            List<StateMachineConfig> stateMachineConfigs = StateMachineJsonUtil.readAllJsonFiles(path);
            // 获取状态机配置名称
            BeanDefinitionRegistry registry = (BeanDefinitionRegistry) applicationContext.getAutowireCapableBeanFactory();
            for (StateMachineConfig config : stateMachineConfigs) {
                //判断是否已经存在Bean名称
                if (!registry.containsBeanDefinition(config.getName())) {
                    GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
                    beanDefinition.setBeanClass(SimpleStateMachine.class);
                    beanDefinition.setInstanceSupplier(() -> createStateMachine(config));
                    // 使用状态机名称作为 Bean 名称
                    registry.registerBeanDefinition(config.getName(), beanDefinition);
                } else {
                    log.error("状态机名称:[{}]重复,请检查配置文件!", config.getName());
                    throw new SpringBootPlusException("状态机名称重复,请检查配置文件!");
                }
            }
        }
    }

    /**
     * 创建并配置状态机实例
     *
     * @param stateMachineConfig 状态机配置
     * @return 配置好的状态机实例
     */
    private SimpleStateMachine createStateMachine(StateMachineConfig stateMachineConfig) {
        // 创建状态机并配置
        SimpleStateMachine stateMachine = new SimpleStateMachine();
        // 配置状态机的转换规则
        for (StateMachineConfig.Transition transition : stateMachineConfig.getTransitions()) {
            stateMachine.addTransition(transition.getFrom(), transition.getEvent(), transition.getTo());
        }

        return stateMachine;
    }
}

读取json文件

/**
 * 读取 json 文件
 *
 * @author zzt
 * @version 1.0.0
 */
@Slf4j
public class StateMachineJsonUtil {
    /**
     * 读取 resources/path 目录下的所有 .json 文件,并将其解析为 StateMachineConfig 对象的列表。
     *
     * @param path json文件路径
     * @return 包含所有解析后的 StateMachineConfig 对象的列表
     */
    public static List<StateMachineConfig> readAllJsonFiles(String path) {
        ObjectMapper objectMapper = MapperUtils.getInstance();
        List<StateMachineConfig> stateMachineConfigs = new ArrayList<>();
        try {
            ClassPathResource resource = new ClassPathResource(path);
            File[] files = resource.getFile().listFiles();
            if (files != null) {
                // 遍历每个资源文件,将其解析为 StateMachineConfig 对象并添加到列表中
                for (File file : files) {
                    if (file.exists()) {
                        FileInputStream inputStream = new FileInputStream(file);
                        StateMachineConfig stateMachineConfig = objectMapper.readValue(inputStream, StateMachineConfig.class);
                        stateMachineConfigs.add(stateMachineConfig);
                    }
                }
            }

        } catch (Exception e) {
            log.error("读取json文件失败,[{}]", e.getMessage(), e);
            throw new SpringBootPlusException("读取json文件失败");
        }
        return stateMachineConfigs;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2304425.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于 Python Django 的校园互助平台(附源码,文档)

博主介绍&#xff1a;✌Java徐师兄、7年大厂程序员经历。全网粉丝13w、csdn博客专家、掘金/华为云等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3fb; 不…

智慧废品回收小程序php+uniapp

废品回收小程序&#xff1a;数字化赋能环保&#xff0c;开启资源循环新时代 城市垃圾治理难题&#xff0c;废品回收小程序成破局关键 随着城市化进程加速与消费水平提升&#xff0c;我国生活垃圾总量逐年攀升&#xff0c;年均增速达5%-8%&#xff0c;其中超30%为可回收物。然…

网页版的俄罗斯方块

1、新建一个txt文件 2、打开后将代码复制进去保存 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>俄…

创建虚拟环境以及配置对应的项目依赖

文章目录 首先创建一个虚拟环境&#xff0c;创建一个名字为myenv,并且版本为xxx的虚拟环境 conda create --name myenv pythonxxx激活虚拟环境 conda activate myenv下载所需的依赖&#xff0c;如果有requirements.txt文件 pip install -r requirements.txt容易出现的错误&a…

网络安全第三次练习

一、实验拓扑 二、实验要求 配置真实DNS服务信息&#xff0c;创建虚拟服务&#xff0c;配置DNS透明代理功能 三、需求分析 1.创建用户并配置认证策略 2.安全策略划分接口 3.ip与策略配置 四、实验步骤 1.划分安全策略接口 2.创建用户并进行策略认证 3.配置安全策略 4.NAT配…

写大论文的word版本格式整理,实现自动生成目录、参考文献序号、公式序号、图表序号

前情提要&#xff1a;最近开始写大论文&#xff0c;发现由于内容很多导致用老方法一个一个改的话超级麻烦&#xff0c;需要批量自动化处理&#xff0c;尤其是序号&#xff0c;在不断有增添删减的情况时序号手动调整很慢也容易出错&#xff0c;所以搞一个格式总结&#xff0c;记…

STM32——HAL库开发笔记22(定时器3—呼吸灯实验)(参考来源:b站铁头山羊)

本文利用前几节所学知识来实现一个呼吸灯实验&#xff1a;两颗led灯交替呼吸。 一、STM32CubeMX配置 step1&#xff1a;配置调试接口 step2&#xff1a;配置定时器 定时器1位于APB2总线上&#xff0c;如上图所示。 step3&#xff1a;配置时基单元 按照下图配置 时钟来源配置…

玩转 Java 与 Python 交互,JEP 库来助力

文章目录 玩转 Java 与 Python 交互&#xff0c;JEP 库来助力一、背景介绍二、JEP 库是什么&#xff1f;三、如何安装 JEP 库&#xff1f;四、JEP 库的简单使用方法五、JEP 库的实际应用场景场景 1&#xff1a;数据处理场景 2&#xff1a;机器学习场景 3&#xff1a;科学计算场…

【单片机毕业设计14-基于stm32c8t6的智能宠物养护舱系统设计】

【单片机毕业设计14-基于stm32c8t6的智能宠物养护舱系统设计】 前言一、功能介绍二、硬件部分三、软件部分总结 前言 &#x1f525;这里是小殷学长&#xff0c;单片机毕业设计篇14-基于stm32c8t6的智能宠物养护舱系统设计 &#x1f9ff;创作不易&#xff0c;拒绝白嫖可私 一、功…

DevEco Studio常用快捷键以及如何跟AndroidStudio的保持同步

DevEco Studio快捷键 DevEco Studio是华为推出的用于开发HarmonyOS应用的集成开发环境&#xff0c;它提供了丰富的快捷键以提高开发效率&#xff0c;以下为你详细介绍不同操作场景下的常用快捷键&#xff1a; 通用操作快捷键 操作描述Windows/Linux 快捷键Mac 快捷键打开设置窗…

[Windows] 全国油价实时查询,可具体到城市

[Windows] 全国油价实时查询&#xff0c;可具体到城市 链接&#xff1a;https://pan.xunlei.com/s/VOJnS3aOPeBwGaSvS0O0E1hwA1?pwdx83j# 出于代码练习的目的&#xff0c;调用公共免费api做的py程序&#xff0c;已经一键打包&#xff0c;双击启动即可 使用&#xff1a;选择…

【CSS】---- CSS 变量,实现样式和动画函数复用

1. 前言 本文介绍 CSS 的自定义属性(变量)来实现样式、动画等 CSS 的复用。都是知道在 CSS 和 JS 复用一个很重要的事情,比如 JS 的函数封装,各个设计模式的使用等等,CSS 中样式的复用,同样重要。MDN 使用 CSS 自定义属性(变量):自定义属性(有时候也被称作CSS 变量或…

装修流程图: 装修前准备 → 设计阶段 → 施工阶段 → 安装阶段 → 收尾阶段 → 入住

文章目录 引言I 毛坯房装修的全流程**1. 装修前准备****1.1 确定装修预算****1.2 选择装修方式****1.3 选择装修公司****1.4 办理装修手续****2. 设计阶段****2.1 量房****2.2 设计方案****2.3 确认方案****3. 施工阶段****3.1 主体拆改****3.2 水电改造****3.3 防水工程****3.…

【论文解读】《Training Large Language Models to Reason in a Continuous Latent Space》

论文链接 1. 背景与动机 语言空间与推理的矛盾 目前大多数大语言模型&#xff08;LLMs&#xff09;在解决复杂问题时采用链式思维&#xff08;Chain-of-Thought, CoT&#xff09;方法&#xff0c;即利用自然语言逐步推导出答案。然而&#xff0c;论文指出&#xff1a; 自然语言…

深度剖析 C 语言函数递归:原理、应用与优化

在 C 语言的函数世界里&#xff0c;递归是一个独特且强大的概念。它不仅仅是函数调用自身这么简单&#xff0c;背后还蕴含着丰富的思想和广泛的应用。今天&#xff0c;让我们跟随这份课件&#xff0c;深入探索函数递归的奥秘。 一、递归基础&#xff1a;概念与思想 递归是一种…

goredis常见基础命令

基本操作 //删除键 exists,err: rdb.Exists(ctx,"key").Result() if err!nil{panic(err) } if exists>0{err rdb.Del(ctx,"key").Err()if err!nil{panic(err)} }string类型 //设置一个键值对 //0表示没有过期时间 err:rdb.Set(ctx,"key1",…

【Linux网络】序列化、守护进程、应用层协议HTTP、Cookie和Session

⭐️个人主页&#xff1a;小羊 ⭐️所属专栏&#xff1a;Linux 很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~ 目录 1、序列化和反序列化2、守护进程2.1 什么是进程组&#xff1f;2.2 什么是会话&#xff1f; 3、应用层协议HTTP3.1 HTTP协议3.2 HT…

system verilog的流操作符

流操作符&#xff0c;有分为操作对象是一整个数组和单独的数据两种&#xff0c;例如bit [7:0] a[4]和bit [31:0] b&#xff0c;前者操作对象是数组&#xff0c;后者是单独一个较大位宽的数。 流操作符有<<和>>&#xff0c;代表从右向左打包和从左向右打包。 打包的…

LLM2CLIP论文学习笔记:强大的语言模型解锁更丰富的视觉表征

1. 写在前面 今天分享的一篇论文《LLM2CLIP: P OWERFUL L ANGUAGE M ODEL U NLOCKS R ICHER V ISUAL R EPRESENTATION》&#xff0c; 2024年9月微软和同济大学的一篇paper&#xff0c; 是多模态领域的一篇工作&#xff0c;主要探索了如何将大模型融合到Clip模型里面来进一步提…

计算机毕业设计SpringBoot+Vue.jst网上超市系统(源码+LW文档+PPT+讲解)

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…