事件流中的事件合成1、问题描述2、核心设计思路3、代码实现5、表达式解析器1、问题描述有一组连续的子事件流类似1 2 3 4 56…子事件会合成事件逻辑类似与(1 2 (3 | 4)) 5如何用状态机实现对于这种包含复杂逻辑嵌套与、或、顺序流转的连续子事件流如果单纯用前面提到的基础状态机比如一个状态变量或简单的集合代码会变得极难扩展且容易写出“屎山”。最优雅的工业级解法是状态机State Machine 抽象语法树 / 组合模式Composite Pattern。我们可以把(1 2 (3 | 4)) 5这种逻辑表达式看作是一个动态的生命周期树或者叫条件流转节点。状态机负责推进全局主状态而这个“树形节点”负责判断何时满足流转条件。2、核心设计思路基础事件/子事件Event代表流转中的 1, 2, 3, 4, 5…逻辑节点Node叶子节点LeafNode对应具体的单事件如 1。当该事件发生时它被标记为“已触发”。与节点AndNode只有当所有子节点都为 true 时它才为 true。或节点OrNode只要有一个子节点为 true它就为 true。3、代码实现第一步构建逻辑节点框架组合模式这是整个逻辑判断的核心。它能够完美嵌套任意复杂的类似 ((1 2) | (3 4)) 5 的逻辑publicinterfaceLogicNode{booleanisSatisfied();// 当前节点是否已满足voidtrigger(inteventId);// 触发子事件voidreset();// 重置状态}// 1. 叶子节点代表具体某个子事件如 123classEventLeafNodeimplementsLogicNode{privatefinalinteventId;privatebooleantriggeredfalse;publicEventLeafNode(inteventId){this.eventIdeventId;}OverridepublicbooleanisSatisfied(){returntriggered;}Overridepublicvoidtrigger(intid){if(this.eventIdid){this.triggeredtrue;}}Overridepublicvoidreset(){this.triggeredfalse;}}// 2. 与节点And所有子节点必须全部满足classAndNodeimplementsLogicNode{privatefinalListLogicNodechildrennewArrayList();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{privatefinalListLogicNodechildrennewArrayList();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{privateMainStatecurrentStateMainState.INIT;privatefinalLogicNoderootCondition;// 复合条件树publicEventStateMachine(LogicNoderootCondition){this.rootConditionrootCondition;}// 接收连续传入的子事件publicsynchronizedvoidfeedEvent(inteventId){if(currentStateMainState.COMPLETED){System.out.println(状态已是 COMPLETED忽略事件: eventId);return;}if(currentStateMainState.INIT){currentStateMainState.PROCESSING;}// 1. 将事件送入逻辑树中消费rootCondition.trigger(eventId);System.out.println(收到子事件 [eventId]);// 2. 状态机检查逻辑树是否整体达到了“满足”条件if(rootCondition.isSatisfied()){this.currentStateMainState.COMPLETED;System.out.println( 满足公式组合条件成功合成为目标事件。当前全局状态 - currentState);}}publicMainStategetCurrentState(){returncurrentState;}}** 测试运行验证 (1 2 (3 | 4)) 5**publicclassMain{publicstaticvoidmain(String[]args){// 1. 根据公式构建逻辑树: (1 2 (3 | 4)) 5LogicNodeleaf1newEventLeafNode(1);LogicNodeleaf2newEventLeafNode(2);LogicNodeleaf3newEventLeafNode(3);LogicNodeleaf4newEventLeafNode(4);LogicNodeleaf5newEventLeafNode(5);// (3 | 4)LogicNodeor34newOrNode(leaf3,leaf4);// (1 2 (3 | 4)) 5LogicNodeformulaRootnewAndNode(leaf1,leaf2,or34,leaf5);// 2. 传入状态机EventStateMachinestateMachinenewEventStateMachine(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 2) | (3 4)) (5 | 6)你不需要修改核心逻辑和状态机代码只需要改动几行组装逻辑树的代码甚至可以写一个简单的解析器把规则字符串直接变成这棵树。职责分离全局状态机只管大方向的生命周期开始 - 处理 - 完成复杂的子事件联动逻辑全封闭在 LogicNode 内符合面向对象的开闭原则OCP。5、表达式解析器要实现将规则字符串如(1 2 (3 | 4)) 5直接转换成前面定义的 LogicNode 树我们需要编写一个简易的表达式解析器Parser。最经典的实现方式是双栈法Dijkstra’s Shunting-yard 算法的变体一个栈用来存操作数Node另一个栈用来存操作符,|,(。由于我们的事件都是数字这种方法实现起来最快、最直观。publicclassRuleParser{publicstaticLogicNodeparse(Stringexpression){// 1. 去除所有空格expressionexpression.replaceAll(\\s,);//操作数栈StackLogicNodenodesnewStack();//操作符栈StackCharacteroperatorsnewStack();inti0;while(iexpression.length()){//遍历获取第一个字符charcexpression.charAt(i);// 情况 1: 如果是数字解析出完整的事件 ID 并生成叶子节点if(Character.isDigit(c)){StringBuildersbnewStringBuilder();//处理事件ID为多位数字的情况while(iexpression.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(StackLogicNodenodes,charop){if(nodes.size()2){thrownewIllegalArgumentException(非法规则表达式);}LogicNoderightnodes.pop();LogicNodeleftnodes.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. 一键解析成一棵树LogicNodeformulaRootRuleParser.parse(ruleStr);// 3. 丢进状态机EventStateMachinestateMachinenewEventStateMachine(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
状态机——事件流中的事件合成
发布时间:2026/5/19 7:49:51
事件流中的事件合成1、问题描述2、核心设计思路3、代码实现5、表达式解析器1、问题描述有一组连续的子事件流类似1 2 3 4 56…子事件会合成事件逻辑类似与(1 2 (3 | 4)) 5如何用状态机实现对于这种包含复杂逻辑嵌套与、或、顺序流转的连续子事件流如果单纯用前面提到的基础状态机比如一个状态变量或简单的集合代码会变得极难扩展且容易写出“屎山”。最优雅的工业级解法是状态机State Machine 抽象语法树 / 组合模式Composite Pattern。我们可以把(1 2 (3 | 4)) 5这种逻辑表达式看作是一个动态的生命周期树或者叫条件流转节点。状态机负责推进全局主状态而这个“树形节点”负责判断何时满足流转条件。2、核心设计思路基础事件/子事件Event代表流转中的 1, 2, 3, 4, 5…逻辑节点Node叶子节点LeafNode对应具体的单事件如 1。当该事件发生时它被标记为“已触发”。与节点AndNode只有当所有子节点都为 true 时它才为 true。或节点OrNode只要有一个子节点为 true它就为 true。3、代码实现第一步构建逻辑节点框架组合模式这是整个逻辑判断的核心。它能够完美嵌套任意复杂的类似 ((1 2) | (3 4)) 5 的逻辑publicinterfaceLogicNode{booleanisSatisfied();// 当前节点是否已满足voidtrigger(inteventId);// 触发子事件voidreset();// 重置状态}// 1. 叶子节点代表具体某个子事件如 123classEventLeafNodeimplementsLogicNode{privatefinalinteventId;privatebooleantriggeredfalse;publicEventLeafNode(inteventId){this.eventIdeventId;}OverridepublicbooleanisSatisfied(){returntriggered;}Overridepublicvoidtrigger(intid){if(this.eventIdid){this.triggeredtrue;}}Overridepublicvoidreset(){this.triggeredfalse;}}// 2. 与节点And所有子节点必须全部满足classAndNodeimplementsLogicNode{privatefinalListLogicNodechildrennewArrayList();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{privatefinalListLogicNodechildrennewArrayList();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{privateMainStatecurrentStateMainState.INIT;privatefinalLogicNoderootCondition;// 复合条件树publicEventStateMachine(LogicNoderootCondition){this.rootConditionrootCondition;}// 接收连续传入的子事件publicsynchronizedvoidfeedEvent(inteventId){if(currentStateMainState.COMPLETED){System.out.println(状态已是 COMPLETED忽略事件: eventId);return;}if(currentStateMainState.INIT){currentStateMainState.PROCESSING;}// 1. 将事件送入逻辑树中消费rootCondition.trigger(eventId);System.out.println(收到子事件 [eventId]);// 2. 状态机检查逻辑树是否整体达到了“满足”条件if(rootCondition.isSatisfied()){this.currentStateMainState.COMPLETED;System.out.println( 满足公式组合条件成功合成为目标事件。当前全局状态 - currentState);}}publicMainStategetCurrentState(){returncurrentState;}}** 测试运行验证 (1 2 (3 | 4)) 5**publicclassMain{publicstaticvoidmain(String[]args){// 1. 根据公式构建逻辑树: (1 2 (3 | 4)) 5LogicNodeleaf1newEventLeafNode(1);LogicNodeleaf2newEventLeafNode(2);LogicNodeleaf3newEventLeafNode(3);LogicNodeleaf4newEventLeafNode(4);LogicNodeleaf5newEventLeafNode(5);// (3 | 4)LogicNodeor34newOrNode(leaf3,leaf4);// (1 2 (3 | 4)) 5LogicNodeformulaRootnewAndNode(leaf1,leaf2,or34,leaf5);// 2. 传入状态机EventStateMachinestateMachinenewEventStateMachine(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 2) | (3 4)) (5 | 6)你不需要修改核心逻辑和状态机代码只需要改动几行组装逻辑树的代码甚至可以写一个简单的解析器把规则字符串直接变成这棵树。职责分离全局状态机只管大方向的生命周期开始 - 处理 - 完成复杂的子事件联动逻辑全封闭在 LogicNode 内符合面向对象的开闭原则OCP。5、表达式解析器要实现将规则字符串如(1 2 (3 | 4)) 5直接转换成前面定义的 LogicNode 树我们需要编写一个简易的表达式解析器Parser。最经典的实现方式是双栈法Dijkstra’s Shunting-yard 算法的变体一个栈用来存操作数Node另一个栈用来存操作符,|,(。由于我们的事件都是数字这种方法实现起来最快、最直观。publicclassRuleParser{publicstaticLogicNodeparse(Stringexpression){// 1. 去除所有空格expressionexpression.replaceAll(\\s,);//操作数栈StackLogicNodenodesnewStack();//操作符栈StackCharacteroperatorsnewStack();inti0;while(iexpression.length()){//遍历获取第一个字符charcexpression.charAt(i);// 情况 1: 如果是数字解析出完整的事件 ID 并生成叶子节点if(Character.isDigit(c)){StringBuildersbnewStringBuilder();//处理事件ID为多位数字的情况while(iexpression.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(StackLogicNodenodes,charop){if(nodes.size()2){thrownewIllegalArgumentException(非法规则表达式);}LogicNoderightnodes.pop();LogicNodeleftnodes.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. 一键解析成一棵树LogicNodeformulaRootRuleParser.parse(ruleStr);// 3. 丢进状态机EventStateMachinestateMachinenewEventStateMachine(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