状态机——并行分支聚合

并行分支聚合

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 的任意组合完成状态。
相关推荐
愚公移码几秒前
蓝凌EKP18产品:流程虚拟机(PVM)
java·开发语言·前端
驰骋工作流9 分钟前
流程引擎BPM设计之:流程消息
java·工作流引擎·bpm·jflow·ccflow
dogstarhuang15 分钟前
从 0 到 1 搭建可收费的 API 开放平台(实战)
java·架构·api
VortMall1 小时前
『平台去经营化』平台治理能力全新重构|VortMall微服务商城系统v1.3.10
java·大数据·微服务·商城系统·开源商城·vortmall·去经营化
吴声子夜歌1 小时前
Redis 3.x——集群故障转移
java·数据库·redis·集群
Java面试题总结1 小时前
mybatis插件
java·tomcat·mybatis
橘子海全栈攻城狮2 小时前
【最新源码】基于SpringBoot + Vue的超市管理系统的设计与实现D002
java·开发语言·vue.js·spring boot·后端·spring
smartvxworks2 小时前
Linux 实时内核(Linux Real-Time Kernel)详解:原理、实践与优化
java·linux·服务器
qizayaoshuap2 小时前
# [特殊字符] 密码生成器 — 鸿蒙ArkTS安全算法与密码强度评估系统
java·算法·安全·华为·harmonyos
AC赳赳老秦2 小时前
OpenClaw 采集任务日志审计:全程记录采集行为,满足合规溯源与企业审计要求
java·大数据·python·数据挖掘·数据分析·php·openclaw