COLA状态机组件实战:用StateMachine接口构建复杂业务流转系统

引言:状态管理的困境与解决方案

你是否还在为业务系统中的状态流转逻辑混乱而头疼?订单从创建到支付再到发货的状态变化,用户从注册到认证再到活跃的生命周期管理,这些复杂的状态流转往往导致代码中充斥着大量的if-else判断和状态检查,不仅难以维护,还容易引发bug。

一、状态机核心概念与COLA组件架构

状态机基础概念

状态机(State Machine)是一种数学模型,用于描述对象在其生命周期中的状态变化过程。在软件工程中,状态机模式常用于处理具有复杂状态转换的业务逻辑。核心概念包括:

  • 状态(State):对象在特定时间点的状况,如订单的"待支付"、"已支付"状态
  • 事件(Event):触发状态转换的动作或条件,如"支付成功"事件
  • 转换(Transition):从一个状态到另一个状态的变化过程
  • 动作(Action):在状态转换过程中执行的业务逻辑
  • 条件(Condition):判断是否允许执行某个状态转换的规则

二、cola-statemachine介绍

COLA框架的状态机组件是一种轻量级的、无状态的、基于注解的状态机实现,可以方便地管理订单等业务对象的状态转换。COLA框架的状态机使用了连贯接口(Fluent Interfaces)来定义状态和事件,以及对应的动作和检查。COLA框架的状态机是COLA 4.0应用架构的一部分,旨在控制复杂度,提高开发效率。

COLA框架的状态机的优势有以下几点:

  • 简化了状态转换的逻辑,避免了大量的if-else判断
  • 提高了代码的可读性和可维护性,方便了单元测试和重构
  • 支持多种状态机模式,如同步、异步、延迟等
  • 与COLA框架的其他组件协同工作,实现领域驱动设计和六边形架构

2.2 COLA状态机基础模型

在Cola-StateMachine组件中有如下的抽象概念模型:

  • 1.State:状态
  • 2.Event:事件,状态由事件触发,引起变化
  • 3.Transition:流转,表示从一个状态到另一个状态
  • 4.External Transition:外部流转,两个不同状态之间的流转
  • 5.Internal Transition:内部流转,同一个状态之间的流转
  • 6.Condition:条件,表示是否允许到达某个状态
  • 7.Action:动作,到达某个状态之后,可以做什么
  • 8.StateMachine:状态机

2.3 COLA状态机组件架构

COLA框架的状态机组件(cola-component-statemachine)采用面向对象设计,提供了灵活的状态流转建模能力。其核心接口关系如下:


image.png

主要接口功能说明:

  • StateMachine:状态机核心接口,提供事件触发、状态验证等方法
  • State:状态接口,管理从该状态出发的所有转换
  • Transition:转换接口,定义状态间的转换规则和行为
  • StateMachineFactory:状态机工厂类,负责状态机的注册和获取

2.4 Cola-StateMachine链路图

image.png

三、快速入门:构建你的第一个状态机

3.1 基本状态机实现步骤

使用COLA状态机构建业务流转系统通常遵循以下步骤:

  • 定义状态和事件枚举:确定业务领域中的状态集合和触发事件
  • 创建上下文对象:封装状态转换过程中需要传递的数据
  • 构建状态机:使用构建器定义状态间的转换规则
  • 注册并获取状态机实例:通过工厂类管理状态机
  • 触发事件并处理结果:在业务逻辑中调用状态机处理状态转换

3.2 环境准备与依赖引入

要使用COLA状态机组件,首先需要在项目中引入依赖:

<dependency>
    <groupId>com.alibaba.cola</groupId>
    <artifactId>cola-component-statemachine</artifactId>
    <version>最新版本</version>
</dependency>

3.3 定义订单的实体类、订单状态的枚举值、订单事件的枚举值

@Getter
@AllArgsConstructor
public enum OrderState {

    CREATED(0,"已创建"),
    PAID(1,"已支付"),
    SHIPPED(2,"已发货"),
    DELIVERED(3,"已送达"),
    CANCELLED(4,"已取消"),
    ;

    private final Integer code;
    private final String msg;
}
@Getter
@AllArgsConstructor
public enum OrderEvent {

    PAY(0,"支付"),
    SHIP(1,"发货"),
    DELIVER(2,"送达"),
    CANCEL(3,"取消"),
    ;

    private final Integer code;
    private final String msg;
}
@Data
@Builder
public class OrderContext {

    @ApiModelProperty("订单ID")
    private Integer orderId;

    @ApiModelProperty("用户ID")
    private String userId;

    @ApiModelProperty("订单金额")
    private BigDecimal amount;

    @ApiModelProperty("备注信息")
    private String remark;

}

3.4 ColaStateMachineConfig,主要负责 Cola StateMachine 的配置

public class ColaStateMachineConfig {

    public StateMachineBuilder<OrderState, OrderEvent, OrderContext> createBuilder() {
        // 3.1 创建状态机构建器
        StateMachineBuilder<OrderState, OrderEvent, OrderContext> builder =
                StateMachineBuilderFactory.create();

        // 3.2 定义状态转换规则
        // 已创建 -> 已支付 (支付事件)
        builder.externalTransition()
                .from(OrderState.CREATED)
                .to(OrderState.PAID)
                .on(OrderEvent.PAY)
                .when(checkPaymentCondition())
                .perform(updateOrderStatusAction());

        // 已支付 -> 已发货 (发货事件)
        builder.externalTransition()
                .from(OrderState.PAID)
                .to(OrderState.SHIPPED)
                .on(OrderEvent.SHIP)
                .when(checkStockCondition())
                .perform(createShipmentAction());

        // 已发货 -> 已送达 (送达事件)
        builder.externalTransition()
                .from(OrderState.SHIPPED)
                .to(OrderState.DELIVERED)
                .on(OrderEvent.DELIVER)
                .perform(completeOrderAction());

        // 已创建 -> 已取消 (取消事件)
        builder.externalTransition()
                .from(OrderState.CREATED)
                .to(OrderState.CANCELLED)
                .on(OrderEvent.CANCEL)
                .when(checkCancelCondition())
                .perform(cancelOrderAction());
        return builder;
    }
    // 条件判断:检查支付条件
    private static Condition<OrderContext> checkPaymentCondition() {
        return context -> {
            // 实际业务中可能检查余额、支付方式等
            System.out.println("检查订单[" + context.getOrderId() + "]的支付条件");
            return context.getAmount().compareTo(BigDecimal.ZERO) > 0;
        };
    }

    // 条件判断:检查库存条件
    private static Condition<OrderContext> checkStockCondition() {
        return context -> {
            // 实际业务中可能检查商品库存
            System.out.println("检查订单[" + context.getOrderId() + "]的库存状况");
            return true;
        };
    }

    // 条件判断:检查取消条件
    private static Condition<OrderContext> checkCancelCondition() {
        return context -> {
            // 实际业务中可能检查取消时限、权限等
            System.out.println("检查订单[" + context.getOrderId() + "]的取消条件");
            return true;
        };
    }

    // 动作:更新订单状态
    private static Action<OrderState, OrderEvent, OrderContext> updateOrderStatusAction() {
        return (from, to, event, context) -> {
            System.out.println("执行订单[" + context.getOrderId() + "]状态更新: " + from + " -> " + to);
            // 实际业务中可能更新数据库记录、发送通知等
        };
    }

    // 动作:创建物流单
    private static Action<OrderState, OrderEvent, OrderContext> createShipmentAction() {
        return (from, to, event, context) -> {
            System.out.println("为订单[" + context.getOrderId() + "]创建物流单");
            // 实际业务中可能调用物流系统API
        };
    }

    // 动作:完成订单
    private static Action<OrderState, OrderEvent, OrderContext> completeOrderAction() {
        return (from, to, event, context) -> {
            System.out.println("订单[" + context.getOrderId() + "]已完成,执行后续处理");
            // 实际业务中可能触发积分发放、评价提醒等
        };
    }

    // 动作:取消订单
    private static Action<OrderState, OrderEvent, OrderContext> cancelOrderAction() {
        return (from, to, event, context) -> {
            System.out.println("订单[" + context.getOrderId() + "]已取消,执行取消处理");
            // 实际业务中可能触发退款、库存释放等
        };
    }
}

3.5 ColaStateMachineService,主要是封装了状态机的调用入口

@Service
@Slf4j
public class ColaStateMachineService {

    private static final String MACHINE_ID = "orderStateMachine";

    private StateMachine<OrderState, OrderEvent, OrderContext> stateMachine;

    @PostConstruct
    public void init() {
        ColaStateMachineConfig config = new ColaStateMachineConfig();
        StateMachineBuilder<OrderState, OrderEvent, OrderContext> stateMachineBuilder = config.createBuilder();
        // 3.3 构建并注册状态机
        stateMachine = stateMachineBuilder.build(MACHINE_ID);
        //StateMachineFactory.register(stateMachine);
    }

    public String getMachineId(){
        return stateMachine.getMachineId();
    }

    public String generatePlantUML(){
        return stateMachine.generatePlantUML();
    }

    public OrderState fireEvent(OrderState source, OrderEvent event, OrderContext context) {
        return stateMachine.fireEvent(source, event, context);
    }
    public Boolean verify(OrderState source, OrderEvent event) {
        return stateMachine.verify(source, event);
    }
}

3.6 定义一个 controller 的操作接口

@RequestMapping("/order")
@RestController
@Slf4j
public class OrderOperaController {

    @Resource
    private ColaStateMachineService colaStateMachineService;

    /**
     * 场景1-用户支付订单
     *
     * @return {@link Boolean}
     */
    @PostMapping("/pay")
    public Boolean pay() {
        String machineId = colaStateMachineService.getMachineId();
        System.out.println(machineId);
        OrderContext order = OrderContext.builder().orderId(1).userId("USER_123").amount(new BigDecimal("99.99")).build();
        OrderState orderState = colaStateMachineService.fireEvent(OrderState.CREATED, OrderEvent.PAY, order);
        System.out.println(orderState.toString());
        return true;
    }

    /**
     * 场景2-订单发货
     *
     * @return {@link Boolean}
     */
    @PostMapping("/ship")
    public Boolean ship() {
        String machineId = colaStateMachineService.getMachineId();
        System.out.println(machineId);
        OrderContext order = OrderContext.builder().orderId(1).userId("USER_123").amount(new BigDecimal("99.99")).build();
        OrderState orderState = colaStateMachineService.fireEvent(OrderState.PAID, OrderEvent.SHIP, order);
        System.out.println(orderState.toString());
        return true;
    }

    /**
     * 场景3-订单送达
     *
     * @return {@link Boolean}
     */
    @PostMapping("/deliver")
    public Boolean deliver() {
        String machineId = colaStateMachineService.getMachineId();
        System.out.println(machineId);
        OrderContext order = OrderContext.builder().orderId(1).userId("USER_123").amount(new BigDecimal("99.99")).build();
        boolean canShip = colaStateMachineService.verify(OrderState.SHIPPED, OrderEvent.DELIVER);
        System.out.println(canShip);
        OrderState orderState = colaStateMachineService.fireEvent(OrderState.SHIPPED, OrderEvent.DELIVER, order);
        System.out.println(orderState.toString());
        return true;
    }

    /**
     * 场景4-检查错误关闭订单
     *
     * @return {@link Boolean}
     */
    @PostMapping("/cancel")
    public Boolean cancel() {
        String machineId = colaStateMachineService.getMachineId();
        System.out.println(machineId);
        OrderContext order = OrderContext.builder().orderId(1).userId("USER_123").amount(new BigDecimal("99.99")).build();
        OrderState orderState = colaStateMachineService.fireEvent(OrderState.CREATED, OrderEvent.CANCEL, order);
        System.out.println(orderState.toString());
        return true;
    }
}

上述代码定义了一个简单的订单状态机,包含从创建到支付、发货、送达的正常流程,以及从创建到取消的异常流程。每个状态转换都定义了触发事件、前置条件和执行动作,使业务逻辑更加清晰和可维护。

四、COLA状态机核心接口详解

4.1 StateMachine接口

StateMachine接口是COLA状态机组件的核心,定义了状态机的基本操作:

public interface StateMachine<S, E, C> extends Visitable {
    /**
     * 验证事件是否可以从当前状态触发
     * @param sourceStateId 当前状态
     * @param event 要触发的事件
     * @return 如果可以触发则返回true,否则返回false
     */
    boolean verify(S sourceStateId, E event);
    
    /**
     * 触发事件并执行状态转换
     * @param sourceState 源状态
     * @param event 要触发的事件
     * @param ctx 上下文对象
     * @return 转换后的目标状态
     */
    S fireEvent(S sourceState, E event, C ctx);
    
    /**
     * 触发并行事件,返回多个目标状态
     * @param sourceState 源状态
     * @param event 要触发的事件
     * @param ctx 上下文对象
     * @return 转换后的多个目标状态列表
     */
    List<S> fireParallelEvent(S sourceState, E event, C ctx);
    
    /**
     * 获取状态机ID
     * @return 状态机唯一标识符
     */
    String getMachineId();
    
    /**
     * 显示状态机结构
     */
    void showStateMachine();
    
    /**
     * 生成PlantUML描述
     * @return PlantUML格式的状态机描述字符串
     */
    String generatePlantUML();
}

关键方法解析

  • verify():在实际触发事件前验证转换是否合法,避免无效的状态转换尝试
  • fireEvent():核心方法,处理单一路径的状态转换
  • fireParallelEvent():处理并行分支的状态转换,返回多个目标状态
  • generatePlantUML():生成可视化描述,便于状态机结构的理解和调试

4.2 State和Transition接口

  • State接口表示状态机中的一个状态节点,管理从该状态出发的所有转换:
public interface State<S, E, C> extends Visitable {
    /**
     * 获取状态ID
     * @return 状态标识符
     */
    S getId();
    
    /**
     * 为状态添加转换规则
     * @param event 触发事件
     * @param target 目标状态
     * @param transitionType 转换类型
     * @return 新创建的转换对象
     */
    Transition<S, E, C> addTransition(E event, State<S, E, C> target, TransitionType transitionType);
    
    /**
     * 获取指定事件的所有转换
     * @param event 事件
     * @return 转换列表
     */
    List<Transition<S, E, C>> getEventTransitions(E event);
    
    /**
     * 获取该状态的所有转换
     * @return 转换集合
     */
    Collection<Transition<S, E, C>> getAllTransitions();
}
  • Transition接口表示从一个状态到另一个状态的转换:
public interface Transition<S, E, C> {
    /**
     * 获取源状态
     * @return 源状态
     */
    State<S, E, C> getSource();
    
    /**
     * 获取触发事件
     * @return 事件
     */
    E getEvent();
    
    /**
     * 获取目标状态
     * @return 目标状态
     */
    State<S, E, C> getTarget();
    
    /**
     * 获取转换条件
     * @return 条件对象
     */
    Condition<C> getCondition();
    
    /**
     * 获取转换动作
     * @return 动作对象
     */
    Action<S, E, C> getAction();
    
    /**
     * 执行状态转换
     * @param ctx 上下文对象
     * @param checkCondition 是否检查条件
     * @return 转换后的目标状态
     */
    State<S, E, C> transit(C ctx, boolean checkCondition);
}

4.3 状态机工厂与构建器

COLA提供了StateMachineBuilder和StateMachineFactory来简化状态机的创建和管理:

// 创建状态机构建器
StateMachineBuilder<OrderState, OrderEvent, OrderContext> builder = 
    StateMachineBuilderFactory.create();
 
// 构建状态机
StateMachine<OrderState, OrderEvent, OrderContext> stateMachine = 
    builder.build(MACHINE_ID);
 
// 注册状态机
StateMachineFactory.register(stateMachine);
 
// 获取状态机实例
StateMachine<OrderState, OrderEvent, OrderContext> orderMachine = 
    StateMachineFactory.get(MACHINE_ID);

五、复杂业务流转场景实现

5.1 条件分支与选择转换

在实际业务中,常常需要根据不同条件执行不同的状态转换。例如,订单支付后根据金额大小决定后续处理流程:

// 定义订单金额阈值
BigDecimal VIP_THRESHOLD = new BigDecimal("1000");
 
// 已支付 -> 普通处理流程 (金额 < 1000)
builder.externalTransition()
    .from(OrderState.PAID)
    .to(OrderState.PROCESSING_NORMAL)
    .on(OrderEvent.PROCESS)
    .when(ctx -> ctx.getAmount().compareTo(VIP_THRESHOLD) < 0)
    .perform(normalProcessingAction());
    
// 已支付 -> VIP处理流程 (金额 >= 1000)
builder.externalTransition()
    .from(OrderState.PAID)
    .to(OrderState.PROCESSING_VIP)
    .on(OrderEvent.PROCESS)
    .when(ctx -> ctx.getAmount().compareTo(VIP_THRESHOLD) >= 0)
    .perform(vipProcessingAction());

上述代码中,相同的PROCESS事件根据订单金额触发不同的状态转换,实现了条件分支逻辑。

5.2 并行分支与合并

某些业务场景需要从一个状态同时转换到多个状态,然后在后续步骤中合并。例如,订单支付后同时触发物流和财务两个并行流程:

// 并行分支示例:支付后同时触发物流和财务流程
builder.externalParallelTransition()
    .from(OrderState.PAID)
    .toAmong(OrderState.LOGISTICS_PROCESSING, OrderState.FINANCE_PROCESSING)
    .on(OrderEvent.PROCESS)
    .when(checkParallelCondition())
    .perform(parallelProcessAction());
    
// 物流流程完成
builder.externalTransition()
    .from(OrderState.LOGISTICS_PROCESSING)
    .to(OrderState.LOGISTICS_COMPLETED)
    .on(OrderEvent.LOGISTICS_DONE)
    .perform(logisticsCompletedAction());
    
// 财务流程完成
builder.externalTransition()
    .from(OrderState.FINANCE_PROCESSING)
    .to(OrderState.FINANCE_COMPLETED)
    .on(OrderEvent.FINANCE_DONE)
    .perform(financeCompletedAction());
    
// 两个并行流程都完成后合并
builder.externalTransitions()
    .fromAmong(OrderState.LOGISTICS_COMPLETED, OrderState.FINANCE_COMPLETED)
    .to(OrderState.ALL_PROCESSED)
    .on(OrderEvent.MERGE)
    .when(checkAllCompletedCondition())
    .perform(mergeProcessAction());

触发并行事件并处理结果:

// 触发并行事件
List<OrderState> parallelStates = orderMachine.fireParallelEvent(
    OrderState.PAID, OrderEvent.PROCESS, context);
    
// 输出并行分支状态
System.out.println("并行分支状态:");
for (OrderState state : parallelStates) {
    System.out.println("- " + state);
}
// 输出: 
// 并行分支状态:
// - LOGISTICS_PROCESSING
// - FINANCE_PROCESSING

5.3 自循环与内部转换

内部转换(Internal Transition)是指不改变当前状态但执行某些动作的转换,常用于状态的自循环更新:

// 内部转换示例:订单处理中超时提醒
builder.internalTransition()
    .within(OrderState.PROCESSING)
    .on(OrderEvent.REMIND)
    .when(checkProcessingTimeCondition())
    .perform(sendReminderAction());

触发内部转换:

// 当前状态不变,但执行提醒动作
OrderState currentState = OrderState.PROCESSING;
OrderState newState = orderMachine.fireEvent(currentState, OrderEvent.REMIND, context);
 
System.out.println("订单状态: " + newState);  // 输出: 订单状态: PROCESSING

5.4 历史状态与子状态机

对于复杂业务领域,可以将状态机分层设计,通过历史状态记录和子状态机实现更高级的状态管理:

// 定义子状态机:退款流程
StateMachine<RefundState, RefundEvent, RefundContext> refundMachine = buildRefundSubMachine();
 
// 在主状态机中引用子状态机
builder.externalTransition()
    .from(OrderState.PAID)
    .to(OrderState.REFUND_PROCESSING)
    .on(OrderEvent.REFUND)
    .when(checkRefundCondition())
    .perform(ctx -> {
        // 调用子状态机处理退款流程
        RefundContext refundCtx = convertToRefundContext(ctx);
        refundMachine.fireEvent(RefundState.INITIATED, RefundEvent.PROCESS, refundCtx);
    });

六、可视化与调试

6.1 生成PlantUML状态图

COLA状态机提供了generatePlantUML()方法,可以生成PlantUML格式的状态图描述,便于可视化和调试:

// 生成PlantUML描述
String plantUml = stateMachine.generatePlantUML();
System.out.println(plantUml);
  • 生成的PlantUML描述可以通过PlantUML工具渲染为状态图,例如:
@startuml
[*] --> CREATED
CREATED --> PAID : PAY
CREATED --> CANCELLED : CANCEL
PAID --> SHIPPED : SHIP
SHIPPED --> DELIVERED : DELIVER
@enduml

上述描述渲染后将显示完整的订单状态流转图。

6.2 状态机结构展示

通过showStateMachine()方法可以在控制台打印状态机结构:

stateMachine.showStateMachine();
  • 输出示例:
StateMachine: orderStateMachine
States:
    CREATED
        Transitions:
            On: PAY, To: PAID, Type: EXTERNAL
            On: CANCEL, To: CANCELLED, Type: EXTERNAL
    PAID
        Transitions:
            On: SHIP, To: SHIPPED, Type: EXTERNAL
    SHIPPED
        Transitions:
            On: DELIVER, To: DELIVERED, Type: EXTERNAL
    DELIVERED
    CANCELLED

七、性能优化与最佳实践

状态机设计最佳实践

  • 状态粒度控制:状态划分过细会增加复杂度,过粗则失去状态机的优势,需找到平衡点
  • 单一职责原则:一个状态机只负责一个业务领域的状态流转
  • 事件命名规范:使用动词或动词短语命名事件(如PAY、SHIP),清晰表达触发动作
  • 状态命名规范:使用形容词或过去分词命名状态(如PAID、SHIPPED),表示对象状态

性能优化技巧

  • 状态机复用:通过StateMachineFactory共享状态机实例,避免重复创建
  • 条件缓存:对于复杂且频繁执行的条件判断,考虑添加缓存机制
  • 异步处理:对于耗时的动作,在Action中使用异步处理模式
  • 状态预加载:系统启动时预加载常用状态机,避免运行时构建开销

线程安全考量

COLA状态机组件本身是线程安全的,可以在多线程环境下共享使用。但需要注意:

  • 1.上下文对象(Context)可能被多个线程访问,需要确保其线程安全性
  • 2.Action中的业务逻辑应避免共享可变状态,或采取适当的同步措施
    *3. 并行触发同一状态机实例的不同事件是安全的,但同一业务对象的状态转换应保证串行执行

总结与展望

COLA状态机组件通过StateMachine接口提供了一种优雅的方式来处理复杂业务流转逻辑,将传统的if-else判断转换为清晰的状态转换规则,显著提升了代码的可读性和可维护性。本文介绍的核心内容包括:

  • 状态机的核心概念及COLA状态机组件的架构设计
  • 使用StateMachine接口构建基本状态流转的方法
  • 处理条件分支、并行流程等复杂业务场景的技巧
  • 状态机的可视化调试和性能优化策略

随着业务复杂度的增长,状态机模式的优势将更加明显。未来可以进一步探索状态机与事件驱动架构(EDA)的结合,以及基于状态机的业务流程自动化。

通过将状态机思想应用到实际业务系统中,开发团队可以构建更加健壮、灵活且易于扩展的业务流转系统,有效应对复杂多变的业务需求。

参考:
https://gitcode.com/gh_mirrors/col/COLA
https://blog.csdn.net/gitblog_00735/article/details/151329646
https://blog.csdn.net/qq_40890575/article/details/129495003
https://segmentfault.com/a/1190000044529545

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容