当前位置: 首页 > news >正文

状态机——事件流中的事件合成

事件流中的事件合成

    • 1、问题描述
    • 2、核心设计思路
    • 3、代码实现
    • 5、表达式解析器

1、问题描述

有一组连续的子事件流,类似1, 2, 3, 4, 5,6…,子事件会合成事件,逻辑类似与(1 & 2 & (3 | 4)) & 5,如何用状态机实现

对于这种包含复杂逻辑嵌套(与、或、顺序流转)的连续子事件流,如果单纯用前面提到的基础状态机(比如一个状态变量或简单的集合),代码会变得极难扩展且容易写出“屎山”。

最优雅的工业级解法是:状态机(State Machine) + 抽象语法树 / 组合模式(Composite Pattern)。

我们可以把(1 & 2 & (3 | 4)) & 5这种逻辑表达式看作是一个动态的生命周期树(或者叫条件流转节点)。状态机负责推进全局主状态,而这个“树形节点”负责判断何时满足流转条件。

2、核心设计思路

  1. 基础事件/子事件(Event):代表流转中的 1, 2, 3, 4, 5…
  2. 逻辑节点(Node):
    • 叶子节点(LeafNode):对应具体的单事件(如 1)。当该事件发生时,它被标记为“已触发”。
    • 与节点(AndNode):只有当所有子节点都为 true 时,它才为 true。
    • 或节点(OrNode):只要有一个子节点为 true,它就为 true。

3、代码实现

第一步:构建逻辑节点框架(组合模式)
这是整个逻辑判断的核心。它能够完美嵌套任意复杂的类似 ((1 & 2) | (3 & 4)) & 5 的逻辑

publicinterfaceLogicNode{booleanisSatisfied();// 当前节点是否已满足voidtrigger(inteventId);// 触发子事件voidreset();// 重置状态}// 1. 叶子节点:代表具体某个子事件(如 1,2,3)classEventLeafNodeimplementsLogicNode{privatefinalinteventId;privatebooleantriggered=false;publicEventLeafNode(inteventId){this.eventId=eventId;}@OverridepublicbooleanisSatisfied(){returntriggered;}@Overridepublicvoidtrigger(intid){if(this.eventId==id){this.triggered=true;}}@Overridepublicvoidreset(){this.triggered=false;}}// 2. 与节点(And):所有子节点必须全部满足classAndNodeimplementsLogicNode{privatefinalList<LogicNode>children=newArrayList<>();publicAndNode(LogicNode...nodes){children.addAll(Arrays.asList(nodes));}@OverridepublicbooleanisSatisfied(){returnchildren.stream().allMatch(LogicNode::isSatisfied);}@Overridepublicvoidtrigger(inteventId){children.forEach(child->child.trigger(eventId));}@Overridepublicvoidreset(){children.forEach(LogicNode::reset);}}// 3. 或节点(Or):子节点满足其一即可classOrNodeimplementsLogicNode{privatefinalList<LogicNode>children=newArrayList<>();publicOrNode(LogicNode...nodes){children.addAll(Arrays.asList(nodes));}@OverridepublicbooleanisSatisfied(){returnchildren.stream().anyMatch(LogicNode::isSatisfied);}@Overridepublicvoidtrigger(inteventId){children.forEach(child->child.trigger(eventId));}@Overridepublicvoidreset(){children.forEach(LogicNode::reset);}}

第二步:结合全局状态机(StateMachine)
现在我们定义全局的业务主状态,并让它持有上面构造出来的逻辑树。

//全局主状态publicenumMainState{INIT,// 初始PROCESSING,// 处理中(子事件收集阶段)COMPLETED// 目标事件合成成功}// 状态机上下文classEventStateMachine{privateMainStatecurrentState=MainState.INIT;privatefinalLogicNoderootCondition;// 复合条件树publicEventStateMachine(LogicNoderootCondition){this.rootCondition=rootCondition;}// 接收连续传入的子事件publicsynchronizedvoidfeedEvent(inteventId){if(currentState==MainState.COMPLETED){System.out.println("状态已是 COMPLETED,忽略事件: "+eventId);return;}if(currentState==MainState.INIT){currentState=MainState.PROCESSING;}// 1. 将事件送入逻辑树中消费rootCondition.trigger(eventId);System.out.println("收到子事件 ["+eventId+"]");// 2. 状态机检查逻辑树是否整体达到了“满足”条件if(rootCondition.isSatisfied()){this.currentState=MainState.COMPLETED;System.out.println("🎉 满足公式组合条件!成功合成为目标事件。当前全局状态 -> "+currentState);}}publicMainStategetCurrentState(){returncurrentState;}}

** 测试运行:验证 (1 & 2 & (3 | 4)) & 5**

publicclassMain{publicstaticvoidmain(String[]args){// 1. 根据公式构建逻辑树: (1 & 2 & (3 | 4)) & 5LogicNodeleaf1=newEventLeafNode(1);LogicNodeleaf2=newEventLeafNode(2);LogicNodeleaf3=newEventLeafNode(3);LogicNodeleaf4=newEventLeafNode(4);LogicNodeleaf5=newEventLeafNode(5);// (3 | 4)LogicNodeor34=newOrNode(leaf3,leaf4);// (1 & 2 & (3 | 4)) & 5LogicNodeformulaRoot=newAndNode(leaf1,leaf2,or34,leaf5);// 2. 传入状态机EventStateMachinestateMachine=newEventStateMachine(formulaRoot);// 3. 模拟事件流乱序流入stateMachine.feedEvent(1);// 满足 1stateMachine.feedEvent(99);stateMachine.feedEvent(5);// 满足 5stateMachine.feedEvent(4);// 满足 (3 | 4) 中的 4System.out.println("-> 当前全局状态: "+stateMachine.getCurrentState());// 应该仍是 PROCESSING,因为缺 2System.out.println("----------------------------------------");stateMachine.feedEvent(2);// 满足 2,此时公式全部闭环!System.out.println("-> 最终全局状态: "+stateMachine.getCurrentState());// 成功变为 COMPLETED}}
收到子事件 [1] 收到子事件 [99] 收到子事件 [5] 收到子事件 [4] -> 当前全局状态: PROCESSING ---------------------------------------- 收到子事件 [2] 🎉 满足公式组合条件!成功合成为目标事件。当前全局状态 -> COMPLETED -> 最终全局状态: COMPLETED

方案优势:

  1. 支持无限嵌套:后续如果业务调整,逻辑变成 ((1 & 2) | (3 & 4)) & (5 | 6),你不需要修改核心逻辑和状态机代码,只需要改动几行组装逻辑树的代码(甚至可以写一个简单的解析器,把规则字符串直接变成这棵树)。
  2. 职责分离:全局状态机只管大方向的生命周期(开始 -> 处理 -> 完成),复杂的子事件联动逻辑全封闭在 LogicNode 内,符合面向对象的开闭原则(OCP)。

5、表达式解析器

要实现将规则字符串(如(1 & 2 & (3 | 4)) & 5)直接转换成前面定义的 LogicNode 树,我们需要编写一个简易的表达式解析器(Parser)。

最经典的实现方式是双栈法(Dijkstra’s Shunting-yard 算法的变体),一个栈用来存操作数(Node),另一个栈用来存操作符(&,|,()。由于我们的事件都是数字,这种方法实现起来最快、最直观。

publicclassRuleParser{publicstaticLogicNodeparse(Stringexpression){// 1. 去除所有空格expression=expression.replaceAll("\\s+","");//操作数栈Stack<LogicNode>nodes=newStack<>();//操作符栈Stack<Character>operators=newStack<>();inti=0;while(i<expression.length()){//遍历获取第一个字符charc=expression.charAt(i);// 情况 1: 如果是数字,解析出完整的事件 ID 并生成叶子节点if(Character.isDigit(c)){StringBuildersb=newStringBuilder();//处理事件ID为多位数字的情况while(i<expression.length()&&Character.isDigit(expression.charAt(i))){sb.append(expression.charAt(i));i++;}//创建叶子节点并入操作数栈nodes.push(newEventLeafNode(Integer.parseInt(sb.toString())));continue;// 跳过后面的 i++,因为上面已经递增过了}// 情况 2: 左括号直接入栈elseif(c=='('){//入操作符栈operators.push(c);}// 情况 3: 右括号,触发计算直到遇到左括号elseif(c==')'){while(!operators.isEmpty()&&operators.peek()!='('){executeOperator(nodes,operators.pop());}operators.pop();// 弹出 '('}// 情况 4: 操作符 & 或 |elseif(c=='&'||c=='|'){// 当栈顶操作符优先级更高或相等时,先计算while(!operators.isEmpty()&&precedence(operators.peek())>=precedence(c)){executeOperator(nodes,operators.pop());}operators.push(c);}i++;}// 2. 遍历完字符串后,清空操作符栈while(!operators.isEmpty()){executeOperator(nodes,operators.pop());}// 3. 栈顶就是最终构建好的根节点returnnodes.pop();}// 定义操作符优先级 (括号最低,& 最高,遵循常规逻辑运算)privatestaticintprecedence(charop){if(op=='&')return2;if(op=='|')return1;return0;// '('}// 弹出栈顶的节点,用指定的操作符把它们结合成复合节点,再压回栈privatestaticvoidexecuteOperator(Stack<LogicNode>nodes,charop){if(nodes.size()<2){thrownewIllegalArgumentException("非法规则表达式");}LogicNoderight=nodes.pop();LogicNodeleft=nodes.pop();if(op=='&'){// 如果左节点本身已经是 AndNode,可以直接追加,优化树的层级(可选)nodes.push(newAndNode(left,right));}elseif(op=='|'){nodes.push(newOrNode(left,right));}}}

配合状态机进行测试验证

publicclassMain2{publicstaticvoidmain(String[]args){// 1. 定义动态规则字符串StringruleStr="((1 | 2) & 8 & (3 | 4)) & 5";System.out.println("正在解析规则: "+ruleStr);// 2. 一键解析成一棵树LogicNodeformulaRoot=RuleParser.parse(ruleStr);// 3. 丢进状态机EventStateMachinestateMachine=newEventStateMachine(formulaRoot);// 4. 模拟输入事件流进行测试stateMachine.feedEvent(1);// 满足 1stateMachine.feedEvent(5);// 满足 5stateMachine.feedEvent(4);// 满足 (3 | 4)stateMachine.feedEvent(2);stateMachine.feedEvent(8);System.out.println("-> 当前状态: "+stateMachine.getCurrentState());System.out.println("----------------------------------------");stateMachine.feedEvent(2);// 满足 2 -> 整个公式成立!System.out.println("-> 最终状态: "+stateMachine.getCurrentState());}}
正在解析规则:((1|2)&8&(3|4))&5收到子事件[1]收到子事件[5]收到子事件[4]收到子事件[2]收到子事件[8]🎉 满足公式组合条件!成功合成为目标事件。当前全局状态->COMPLETED->当前状态:COMPLETED----------------------------------------状态已是COMPLETED,忽略事件:2->最终状态:COMPLETED
http://www.zskr.cn/news/1316161.html

相关文章:

  • 青岛治疗焦虑抑郁的医院哪家靠谱 - 品牌排行榜
  • ARMv8浮点运算与SIMD指令优化实践
  • 前厅服务与数字化运营智慧实训室,数字驱动构建职教学习新生态
  • CODESYS硬件平台适配实战:从实时系统到工业控制生态
  • 石榴石固态电解质表面再生:氧气处理与气氛控制的关键突破
  • 手把手调优:如何榨干寒武纪MLU370系列卡的每一份算力?
  • CircuitPython嵌入式开发实战:内存管理、BLE通信与异步编程优化
  • 2025-2026年全球重卡充电桩品牌推荐:五大排名厂家专业评测矿区应对恶劣环境 - 品牌推荐
  • JetBrains IDE试用期重置终极解决方案:告别30天限制的完整指南
  • 3分钟掌握ncmdump:终极NCM音乐解密完全指南
  • 靠谱的朋友圈广告公司,如何选择与收费标准 - 工业品牌热点
  • GHelper技术深度解析:华硕硬件控制的轻量化革命与架构创新
  • 情绪消费崛起,打通全链路的不是卖点,而是选择理由
  • 一键解锁全球游戏:XUnity自动翻译器新手完全指南
  • AI文生视频闪烁问题排查:从源头定位到参数调优
  • iPhone内移植RFID公交卡:破解金属屏蔽,实现物理刷卡
  • GEO优化选购指南:靠谱品牌与价格分析 - 工业品牌热点
  • AI编程的优缺点
  • QMCDecode终极指南:三分钟解锁QQ音乐加密音频,实现跨平台自由播放
  • Office Custom UI Editor:终极指南:如何彻底改造你的Office工作界面?
  • 第1章:AI Agent认知与全景图
  • 从零打造动画电子猫:Arduino与针毡工艺的创客实践
  • 大模型应用开发:小白也能入门!收藏这份超全学习指南,掌握未来AI技能
  • 基于CircuitPython的多传感器物联网环境监测盒设计与实现
  • 遗传算法 训练俄罗斯方块策略
  • 如何用LizzieYzy免费围棋AI分析工具提升你的棋力:从入门到精通
  • 终极解决方案:TQVaultAE如何彻底改变《泰坦之旅》装备管理体验
  • MXFP混合精度优化:提升LLM推理效率的关键技术
  • 如何一键复刻抖音爆款视频?一链成片功能使用指南
  • 张琦式7天落地执行清单(每日任务+话术模板+检查项)