状态机的设计和应用

本文探讨了状态机在处理流程中的应用,特别是在订单和送货单流程中的设计模式实现。通过事件驱动设计,结合责任链模式和状态机模式,构建可扩展和维护的业务逻辑。文章详细阐述了状态机如何处理异常情况,并提供了订单流程的实例,解释了开发者如何定义状态、事件和过渡逻辑,以及使用者如何简便地执行操作。
摘要由CSDN通过智能技术生成

状态机的设计和应用

状态机定义
在离散数学中可以用有向图来表示状态机,用于进行对象行为建模,描述对象在它的生命周期内所经历的状态序列(这里描述的状态机前提是有限状态);表示由一组状态节点和一组相应的转移函数组成,通过响应一系列事件而运作;上一个状态节点通过设定的一个事件进行动作完成状态的转移。

为什么选用
在状态转换的业务逻辑处理过程中,通过 if else 判断或者switch 处理进行状态的转换是最简单且容易的,可想而知,状态量多了,业务变复杂后,代码冗余等,维护是否是一件苦恼的事,那么总结两点方法:
(1)通过添加类来隔离代码
(2)通过应用设计模式让处理逻辑结构化可复用、配置化、可扩展

状态机其应用大多在处理流程上,对流程进行离散状态设定,并定义相互关联的上下游状态节点和相对的事件源,但其状态的、关系和事件都已建模好,所以在固定的流程处理业务上运用得较得心应手。


那么本章将通过使用者的角度来根据订单流程、送货单流程等业务进行设计模式的深入解剖。


怎么用

(1)整体以①事件驱动设计为前提,基于用户请求相关接口做事件源,应用程序接收并转换为消息,
(2)中间层通过②责任链模式进行,构建流程上下文、链路到流程处理,
(3)底层流程采用③状态机模式处理,通过事件源 e v e n t 、 当 前 状 态 {event}、当前状态 event{preState} 进行映射到具体的 t r a n s i t i o n 处 理 , 并 转 换 下 一 个 状 态 {transition} 处理,并转换下一个状态 transition{postState}实现整个状态流程,

流程处理中的异常情况才是最应该考虑和最复杂的,目前异常情况也是通过配置异常事件流程Transition进行处理。

订单流程
**
把订单处理分为两个层次,

(1)执行器处理
使用者通过工厂获取订单执行器,订单执行器处理两个逻辑:
第一步:将用户构建的参数通过上下文传递到 OrderPreHandler 进行前置处理,校验(订单是否存在主键编号,是否为新增校验是否存在、订单当前状态值)、初始化订单状态值等上下文信息;
第二步:OrderEventHandler只负责将信息(订单请求信息+事件类型)交给订单状态机处理。

(2)流程处理
状态机相关实现包含两个类:OrderStateMachine、StateMachineFactory
StateMachineFactory 是服务于包括订单、送货单等所有流程的状态机实现,包含4个属性信息
① transitionsListNode :链表型的Transition数据结构
② defaultInitialState:用户自定义的OrderState
③ optimized:用于判断是否初始化 stateMachineTable 结构信息
④ stateMachineTable:Map结构的状态拓扑图,preState->Transition(postState, OrderTransition)
每个StateMachineFactory的transitionsListNode构成一个链表型数据结构,每个node由当前的ApplicableTransition和下一个node组成,ApplicableTransition的实现类实现apply方法将作用于用户自定义的transition跟对应事件绑定在一起进行初始化,服务于stateMachineTable的状态分组;optimized=true 表示所有的transition添加完成,最后构建拓扑stateMachineTable。
OrderStateMachine 通过StateMachineFactory 具体服务订单流程,负责对订单状态的编排和transition初始化,preState、postState、eventType、Transition构成一个StateMachineFactory,

第一步:启动时向容器注入OrderStateMachine,通过实现InitializingBean#afterPropertiesSet初始化StateMachineFactory;
第二步:调用时OrderStateMachine通过订单当前状态preState 和事件类型构建内部状态机,默认为初始状态默认监听器方式构造,内部状态机InternalStateMachine是流程中具体的处理实现,处理当前状态prestate置换为postState等操作,从stateMachineTable 中通过当前状态和事件查询Transition并执行TransitionHook操作,调用SubTransition业务逻辑处理。

根据产品的业务将订单定义为四个状态、七个事件(代码不展示了):

在这里插入图片描述

分别定义不同的SubEvent继承OrderEvent,SubTransition继承OrderReleaseTransition实现,用于将Sub流程建立关联关系形成stateMachineTable,这里不直接暴露eventType给调用方。

开发使用
**
开发者需要定义相关实现:
(1)定义状态属性 OrderState
(2)定义事件属性 OrderEventType
(3)定义Sub事件类继承OrderEvent,主要是为了让事件属性不被调用者感知或暴露出去,如:InitSuccessEvent
(4)定义Sub过渡类继承OrderTransition,如:OrderInitTransition
(5)对状态进行编排,实现状态自动路由和处理


使用者只需要2步操作:
(1)构建请求参数(包括订单主键等)
(2)选定自定义事件源,通过ExecutorFactor构建订单的执行器,并执行,如:response = ExecutorFactory.buildOrderExecutor().execute(orderContext);


开发者需要扩展相关接口实现来具体处理;而使用者只关注当前接口选定哪种事件作为事件源即可,不需要关心底层的实现业务逻辑。


开发步骤

送货单流程

1.定义状态和事件
状态和事件类型定义是不可缺少的,然后抽象出事件,eg :

// 状态
public enum DeliveryState {
   
    
    UNKNOWN("UNKNOWN", "未知"), 
    DRAFT("DRAFT", "未提交"), 
    BE_CONFIRM("BE_CONFIRM", "待确认"),
     REJECTED("REJECTED", "已拒绝"), 
    DELIVERY("DELIVERY", "待收货"), 
    RECEIVED("RECEIVED", "已收货"), 
    CLOSED("CLOSED", "已关闭"),    
    ;
...
}

//事件类型
public enum DeliveryEventType {
   

    // 未知事件
    UNKNOWN,

    /**
     * Supplier, 1 -> 1, "成功提交送货单",2 -> 2, "成功提交送货单"
     * See {@link SaveUpdateEvent }
     */
    SAVE_UPDATE,

    /**
     * Supplier, 1 -> 2, "成功提交送货单"
     * See {@link SubmitSuccessEvent}
     */
    SUBMIT_SUCCESS,

    /**
     * Supplier, 2/4 -> 1, "送货单召回"
     * See {@link SetupFailedCancelEvent}
     */
    SETUP_FAILED_CANCEL,

    /**
     * Customer, 2 -> 4, "客户确认收货"
     * See {@link SetupConfirmEvent}
     */
    SETUP_CONFIRM,

    /**
     * Customer, 2 -> 3, "客户拒绝送货单"
     * See {@link SetupFailedRejectsEvent}
     */
    SETUP_FAILED_REJECTS,

    /**
     * Customer, 4 -> 5, "客户已收货"
     * see {@link ReceivedSuccessEvent}
     */
    RECEIVED_SUCCESS,
}


//抽象事件

abstract public class DeliveryEvent extends AbstractEvent<DeliveryEventType> {
   

    transient private BizId deliveryId;

    public DeliveryEvent(BizId deliveryId, DeliveryEventType type) {
   
        super(type);
        this.deliveryId = deliveryId;
    }

    public BizId getDeliveryId() {
   
        return deliveryId;
    }

    abstract public String getDiagnosticMessage();
}


**

2.定义子事件继承DeliveryEvent,并绑定事件源DeliveryEventType, eg:
**

// 保存修改事件

/**
 * This event is sent by the {@link com.maycur.purchaseorder.core.order.state.SupplyType#Supplier}
 * for the delivery update request.
 *
 * @author
 */
public class SaveUpdateEvent extends DeliveryEvent {
   

    private final String diagnosticMesage;
    private static final DeliveryEventType eventType = DeliveryEventType.SAVE_UPDATE;

    public SaveUpdateEvent(BizId bizId, String diagnosticMesage) {
   
        super(bizId, eventType);
        this.diagnosticMesage = diagnosticMesage;
    }

    public SaveUpdateEvent(BizId bizId) {
   
        this(bizId, "修改送货单 :" + eventType.name());
    }

    @Override
    public String getDiagnosticMessage() {
   
        return diagnosticMesage;
    }
}


// 提交事件


/**
 * This event is sent by the {@link com.maycur.purchaseorder.core.order.state.SupplyType#Supplier}
 * for the delivery submit success request.
 *
 * @author
 */
public class SubmitSuccessEvent extends DeliveryEvent {
   

    private static final DeliveryEventType eventType = DeliveryEventType.SUBMIT_SUCCESS;
    private final String diagnosticMesage;

    public SubmitSuccessEvent(BizId bizId, String diagnosticMesage) {
   
        super(bizId, eventType);
        this.diagnosticMesage = diagnosticMesage;
    }

    public SubmitSuccessEvent(BizId bizId) {
   
        this(bizId, "确认提交送货单 :" + eventType.name());
    }

    @Override
    public String getDiagnosticMessage() 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值