并行分支聚合
-
- 1、问题描述
- [2、方案一:利用 EnumSet 收集前置状态(最推荐,纯轻量级)](#2、方案一:利用 EnumSet 收集前置状态(最推荐,纯轻量级))
- 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、定义状态和事件
java
import java.util.EnumSet;
public enum CompositeState {
INIT, // 初始状态
A_DONE, // A部分完成
B_DONE, // B部分完成
C_DONE, // C部分完成
D; // 最终合成态 D
}
// 触发的事件(比如三个部门分别审批、或三个组件分别就绪)
enum Event {
FINISH_A, FINISH_B, FINISH_C
}
2.2、状态机上下文
在 Context 中,我们用 EnumSet 来实时记录已经完成了哪些部分。
java
public class SynthesisContext {
private final String id;
private CompositeState currentStatus = CompositeState.INIT;
// 用来存放已经就绪的前置状态集合
private final EnumSet<CompositeState> readyParts = EnumSet.noneOf(CompositeState.class);
public SynthesisContext(String id) {
this.id = id;
}
// 统一的事件触发入口
public synchronized void handleEvent(Event event) {
if (currentStatus == CompositeState.D) {
System.out.println("已经处于最终态 D,忽略事件: " + event);
return;
}
// 根据事件,把对应的状态加入"已就绪"池
switch (event) {
case FINISH_A :
readyParts.add(CompositeState.A_DONE);
break;
case FINISH_B :
readyParts.add(CompositeState.B_DONE);
break;
case FINISH_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.currentStatus = CompositeState.D;
System.out.println("🎉 【🎉大功告成】A+B+C 满足条件,成功合成为状态: " + currentStatus);
}
}
public CompositeState getCurrentStatus() {
return currentStatus;
}
}
2.3、测试运行
java
public class Main {
public static void main(String[] args) {
SynthesisContext context = new SynthesisContext("Task_001");
// 异步或乱序触发事件
context.handleEvent(Event.FINISH_B); // 触发 B
context.handleEvent(Event.FINISH_A); // 触发 A
System.out.println("当前整体状态: " + context.getCurrentStatus()); // 依然不是 D
System.out.println("-------------------------------------");
context.handleEvent(Event.FINISH_C); // 触发 C,此时 A+B+C 齐全
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]
🎉 【🎉大功告成】A+B+C 满足条件,成功合成为状态: D
当前整体状态: D
3、方案二:利用"二进制位(Bitmask)"进行状态压缩
如果 A、B、C 是极其严格的布尔条件(要么完,要么没完),并且你希望把这个状态极其轻量地存在数据库的一个数字字段里,可以用位运算。
- A 完成 = 0b001 (十进制 1)
- B 完成 = 0b010 (十进制 2)
- C 完成 = 0b100 (十进制 4)
- D (全部完成) = 0b111 (十进制 7)
java
public enum BitOrderState {
// 定义每个事件对应的二进制位
FINISH_A(1 << 0), // 1
FINISH_B(1 << 1), // 2
FINISH_C(1 << 2); // 4
private final int mask;
BitOrderState(int mask) { this.mask = mask; }
public int getMask() { return mask; }
// 核心逻辑:当前状态码通过"位或"运算叠加,满 7 则代表合成 D
public static int nextState(int currentProgress, BitOrderState event) {
// 叠加状态
int newProgress = currentProgress | event.getMask();
if (newProgress == 7) {
System.out.println("位状态满 0b111(7),成功合成为 D!");
} else {
System.out.println("当前进度二进制码: " + Integer.toBinaryString(newProgress));
}
return newProgress;
}
}
使用方式:
java
int progress = 0; // 初始状态 0
progress = BitOrderState.nextState(progress, BitOrderState.FINISH_A); // 变为 1
progress = BitOrderState.nextState(progress, BitOrderState.FINISH_C); // 变为 5 (1|4)
progress = BitOrderState.nextState(progress, BitOrderState.FINISH_B); // 变为 7 -> 触发合成 D
总结:对于 A + B + C -> D 这种多对一的合成:
- 业务逻辑清晰、可读性高: 用 方案一(EnumSet),这也是最标准的面向对象写法。
- 高并发、或者要存数据库高性能持久化: 用 方案二(Bitmask位运算),数据库只需要存一个 int 字段即可代表 A/B/C 的任意组合完成状态。