状态机的深入理解与学习

1. 什么是状态机

1.1 定义

关于状态机的一个极度确切的描述是:它是一个有向图形,由一组节点和一组相应的转移函数组成。状态机通过响应定义的一系列事件而"执行"。是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。

也可以将状态机看成是一个控制中心,接受外部的事件信号进行状态转移,而状态转移也是在状态机初始化的时候就设置好了的。但实际业务中,我们不仅仅只是需要控制中心进行状态转移,还会需要进行一些业务的处理。

状态机机制中的事件处理器执行相关业务逻辑,就会需要获得业务的数据,这时候触发事件时候就需要传递业务数据到处理器中,正好spring的状态机类statemachine提供了传递事件消息的api。如下:

/**
     * Send an event {@code E} wrapped with a {@link Message} to the region.
     *
     * @param event the wrapped event to send
     * @return true if event was accepted
     */
    boolean sendEvent(Message<E> event);

    /**
     * Send an event {@code E} to the region.
     *
     * @param event the event to send
     * @return true if event was accepted
     */
    boolean sendEvent(E event);


Message实现类:

// 部分展示
public class GenericMessage<T> implements Message<T>, Serializable {

    private static final long serialVersionUID = 4268801052358035098L;


    private final T payload;//这个保存这事件枚举对象OrderEvents

    private final MessageHeaders headers;//Map类型,保存多个参数对象

private final T payload;//这个保存这事件枚举对象OrderEvents

private final MessageHeaders headers;//Map类型,保存多个参数对象
 

创建的事件消息类Message保存事件对象和参数对象,通过以下方式发送到状态机:

//用message传递数据
        Order order = new Order(orderId, "547568678", "广东省深圳市", "13435465465", "RECEIVE");
        Message<OrderEvents> message = MessageBuilder.withPayload(OrderEvents.RECEIVE).setHeader("order", order).setHeader("otherObj", "otherObjValue").build();
        stateMachine.sendEvent(message);

RECEIVE事件被触发,事件处理器被触发打印如下结果:

消息: Order [id=null, userId=547568678, address=广东省深圳市, phoneNum=13435465465, state=RECEIVE]

RECEIVE事件对应的处理器方法:

/**
     * WAITING_FOR_RECEIVE->DONE 执行的动作
     */
    @OnTransition(source = "WAITING_FOR_RECEIVE", target = "DONE")
    public void receive(Message<OrderEvents> message) {
        logger.info("---【WithStateMachine】用户已收货,订单完成---");
        Object object = message.getHeaders().get("order");
        Order order = null;
        if(!ObjectUtils.isEmpty(object))
        {
            order = (Order)object;
            System.out.println("消息: "+order.toString());
        }else {
            System.out.println("消息为空");
        }
    }

处理器类使用@WithStateMachine(name=“orderSingleMachine”)修饰,name属性关联着状态机。事件处理器方法使用@OnTransition(source = “WAITING_FOR_RECEIVE”, target = “DONE”)修饰,表明现态和次态。

小结:这样就实现了状态机业务层controller到状态机处理器的业务数据传递,以消息的形式传递。
 

1.2 四大概念

状态机的概念分别为 state 状态、event 事件、action 动作、transition 变换。

  • State ,状态:一个状态机至少要包含两个状态。例如上面自动门的例子,有 open 和 closed 两个状态。

  • ● Pseudo state , 伪状态,是状态机中的和事物本身没有关系的状态,用以描述状态机完整工作的状态,比如状态机的开始,结束等
    ● Simple State, 真实状态,表示的是真实的事物状态
    ● Composite State, 组合状态,将多个简单状态组合成一个新的状态
    ● Submachine State, 子状态机状态,状态机可以嵌套子状态机,而子状态机可以的整体可以Submachine State 表示。

  • Event ,事件:事件就是执行某个操作的触发条件或者口令。即状态流转时锁触发的事件。

  • Action ,动作:事件发生以后要执行动作。例如事件是“按开门按钮”,动作是“开门”。编程的时候,一个 Action一般就对应一个函数。

  • Transition ,状态流转:表达的是从一个状态到另外一个状态的转换,包含 source state,target state,event 信息
    ● ExternalTransition 两个不同状态之间的流转
    ● Internal Transition 一个状态下的内部流转,不会影响到当前状态改变

1.3 概念详解

1.3.1 state 状态

状态会包含如下几种组成部分:
❏ state 当前状态的描述
❏ entry (进入节点前应当处理的行为)
❏ exit (从当前状态退出时应当采取的行为)
其中,entry、exit 是状态定义的两种行为,而这两种方式就是留给业务拓展的地方。比如待收货状态下,可以在entry行为上,增加通知用户逻辑,而不影响整体状态流转。

1.3.2 Event 事件

状态内或者不同的状态之间,通过事件的方式进行触发转换。如下图所示, 从State A 到 State B 发生转换,通过Event 触发。

客户端可以对状态机实例发出事件请求,而状态机实例会根据当前实例所处的状态进行判断,是否可以接受并处理消息。一般接受消息,会有相应的状态机状态的转换(Transition)动作。

1.3.3 Transition 状态转换

状态机的状态之间的流转行为,如下图所示,状态和状态之间的连线,在定义上,被称为transition

一个transition 包含五个元素:
● source State (原状态): 状态变迁的起始状态
● Trigger(触发器): 触发器是指如何触发transition的形式,可以基于事件,也可以基于定时器。大部分场景下,是基于事件的触发器。根据事件的key 去寻找 transition, 唯一匹配到特定的transition;
●target State (目标状态):事件发生后,应当到达的状态

● guard (门卫):当事件请求触发时,可以定义校验规则,当满足此规则的时候,则正常执行状态变迁,否则提前终止
●actions(动作):当状态机判断transition 是合法时,会执行 actions。actions是框架层面开放出来的拓展点,请注意,actions 如果执行抛出了异常,则transition 状态变迁将会终止。

2. 状态机图

做需求时,需要了解以下六种元素:起始、终止、现态、次态(目标状态)、动作、条件,我们就可以完成一个状态机图了:

以订单为例:以从待支付状态转换为待发货状态为例:

  • ①现态:是指当前所处的状态。待支付

  • ②条件:又称为“事件”,当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。支付事件

  • ③动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。状态转换为待发货

  • ④次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。待发货 注意事项

1、避免把某个“程序动作”当作是一种“状态”来处理。那么如何区分“动作”和“状态”?“动作”是不稳定的,即使没有条件的触发,“动作”一旦执行完毕就结束了;而“状态”是相对稳定的,如果没有外部条件的触发,一个状态会一直持续下去。

2、状态划分时漏掉一些状态,导致跳转逻辑不完整。所以在设计状态机时,我们需要反复的查看设计的状态图或者状态表,最终达到一种牢不可破的设计方案。

  • 52
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值