状态机——并行分支聚合
并行分支聚合1、问题描述2、方案一利用 EnumSet 收集前置状态最推荐纯轻量级2.1、定义状态和事件2.2、状态机上下文2.3、测试运行3、方案二利用“二进制位Bitmask”进行状态压缩1、问题描述当你的状态机不再是简单的“单线流转”A - B - C而是涉及多条件聚合比如 A、B、C 三个前置条件都完成后才能触发转换为 D时这在状态机中被称为 Fork/Join并行分支聚合 场景。在 Java 枚举中实现这种“多合一”的逻辑最核心的变化是状态机不能再只依赖一个“当前状态”变量而是需要引入“状态位Flags”或者“事件收集池”来记录前置条件的完成情况。2、方案一利用 EnumSet 收集前置状态最推荐纯轻量级我们可以把 A、B、C 看作是 D 的三个前置子任务。当 A、B、C 各自完成后检查集合是否集齐集齐了就自动合成为 D。2.1、定义状态和事件importjava.util.EnumSet;publicenumCompositeState{INIT,// 初始状态A_DONE,// A部分完成B_DONE,// B部分完成C_DONE,// C部分完成D;// 最终合成态 D}// 触发的事件比如三个部门分别审批、或三个组件分别就绪enumEvent{FINISH_A,FINISH_B,FINISH_C}2.2、状态机上下文在 Context 中我们用 EnumSet 来实时记录已经完成了哪些部分。publicclassSynthesisContext{privatefinalStringid;privateCompositeStatecurrentStatusCompositeState.INIT;// 用来存放已经就绪的前置状态集合privatefinalEnumSetCompositeStatereadyPartsEnumSet.noneOf(CompositeState.class);publicSynthesisContext(Stringid){this.idid;}// 统一的事件触发入口publicsynchronizedvoidhandleEvent(Eventevent){if(currentStatusCompositeState.D){System.out.println(已经处于最终态 D忽略事件: event);return;}// 根据事件把对应的状态加入“已就绪”池switch(event){caseFINISH_A:readyParts.add(CompositeState.A_DONE);break;caseFINISH_B:readyParts.add(CompositeState.B_DONE);break;caseFINISH_C:readyParts.add(CompositeState.C_DONE);break;default:}System.out.println(事件 event 触发当前已就绪组件: readyParts);// 检查是否满足合成 D 的条件if(readyParts.contains(CompositeState.A_DONE)readyParts.contains(CompositeState.B_DONE)readyParts.contains(CompositeState.C_DONE)){this.currentStatusCompositeState.D;System.out.println( 【大功告成】ABC 满足条件成功合成为状态 currentStatus);}}publicCompositeStategetCurrentStatus(){returncurrentStatus;}}2.3、测试运行publicclassMain{publicstaticvoidmain(String[]args){SynthesisContextcontextnewSynthesisContext(Task_001);// 异步或乱序触发事件context.handleEvent(Event.FINISH_B);// 触发 Bcontext.handleEvent(Event.FINISH_A);// 触发 ASystem.out.println(当前整体状态: context.getCurrentStatus());// 依然不是 DSystem.out.println(-------------------------------------);context.handleEvent(Event.FINISH_C);// 触发 C此时 ABC 齐全System.out.println(当前整体状态: context.getCurrentStatus());// 变成 D}}事件 FINISH_B 触发当前已就绪组件: [B_DONE] 事件 FINISH_A 触发当前已就绪组件: [A_DONE, B_DONE] 当前整体状态: INIT ------------------------------------- 事件 FINISH_C 触发当前已就绪组件: [A_DONE, B_DONE, C_DONE] 【大功告成】ABC 满足条件成功合成为状态 D 当前整体状态: D3、方案二利用“二进制位Bitmask”进行状态压缩如果 A、B、C 是极其严格的布尔条件要么完要么没完并且你希望把这个状态极其轻量地存在数据库的一个数字字段里可以用位运算。A 完成 0b001 (十进制 1)B 完成 0b010 (十进制 2)C 完成 0b100 (十进制 4)D (全部完成) 0b111 (十进制 7)publicenumBitOrderState{// 定义每个事件对应的二进制位FINISH_A(10),// 1FINISH_B(11),// 2FINISH_C(12);// 4privatefinalintmask;BitOrderState(intmask){this.maskmask;}publicintgetMask(){returnmask;}// 核心逻辑当前状态码通过“位或”运算叠加满 7 则代表合成 DpublicstaticintnextState(intcurrentProgress,BitOrderStateevent){// 叠加状态intnewProgresscurrentProgress|event.getMask();if(newProgress7){System.out.println(位状态满 0b111(7)成功合成为 D);}else{System.out.println(当前进度二进制码: Integer.toBinaryString(newProgress));}returnnewProgress;}}使用方式intprogress0;// 初始状态 0progressBitOrderState.nextState(progress,BitOrderState.FINISH_A);// 变为 1progressBitOrderState.nextState(progress,BitOrderState.FINISH_C);// 变为 5 (1|4)progressBitOrderState.nextState(progress,BitOrderState.FINISH_B);// 变为 7 - 触发合成 D总结对于 A B C - D 这种多对一的合成业务逻辑清晰、可读性高 用 方案一EnumSet这也是最标准的面向对象写法。高并发、或者要存数据库高性能持久化 用 方案二Bitmask位运算数据库只需要存一个 int 字段即可代表 A/B/C 的任意组合完成状态。