状态机——并行分支聚合

并行分支聚合

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 的任意组合完成状态。
相关推荐
·薯条大王5 小时前
经济实惠玩云服务器|一台云服务器多人共用,子账号配置教程
java·linux·运维·服务器·汇编·c++·python
ZJH__GO8 小时前
网络编程v4pro--实现聊天室文件传输功能
java·服务器·网络·计算机网络
程序员黑豆8 小时前
Windows 系统 Java 环境变量配置全攻略:解决“不是内部或外部命令”
java·前端·ai编程
命运之光9 小时前
【C语言完整代码】就诊信息管理系统
java·c语言·开发语言
凤山老林9 小时前
Spring Boot @Async 线上实战:从默认配置到生产级线程池治理
java·spring boot·后端
marvelyu10 小时前
每天10分钟学会OceanBase系列(Day 20):跨机房容灾实战——构建多数据中心高可用架构
java·大数据·数据库
AI人工智能+电脑小能手11 小时前
大白话说Java设计模式-04-工厂方法模式(业务实战篇)
java·设计模式·工厂方法模式·架构设计·代码解耦
BUG指挥官11 小时前
Sa-Token和Spring Security对比
java·后端·spring
小当家.10511 小时前
深入理解 ReAct Agent:从原理到 Java 实战
java·react.js·agent·react·架构设计·agent设计
geminigoth11 小时前
Spring AI Alibaba 入门开发一(备份)
java·人工智能·spring