扩展:“状态机”从概念层演变落地

什么是状态机?

定义

我们先来给出状态机的基本定义。一句话:

状态机是有限状态自动机的简称,是现实事物运行规则抽象而成的一个数学模型。

先来解释什么是“状态”( State )。现实事物是有不同状态的,例如一个自动闸门,就有 open 和 closed 两种状态。我们通常所说的状态机是有限状态机,也就是被描述的事物的状态的数量是有限个,例如自动闸门的状态就是两个 open 和 closed。

状态机,也就是 State Machine ,不是指一台实际机器,而是指一个数学模型。说白了,一般就是指一张状态转换图。

自动闸门有两个状态,open 和 closed ,closed 状态下,如果读取开门信号,那么状态就会切换为 open 。open 状态下如果读取关门信号,状态就会切换为 closed 。

状态机的全称是有限状态自动机,自动两个字也是包含重要含义的。给定一个状态机,同时给定它的当前状态以及输入,那么输出状态时可以明确的运算出来的。例如对于自动门,给定初始状态 closed ,给定输入“开门”,那么下一个状态时可以运算出来的。

有限状态机(英语:finite-state machine,缩写:FSM)又称有限状态自动机(英语:finite-state automaton,缩写:FSA),简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学计算模型。

这样状态机的基本定义我们就介绍完毕了。重复一下:状态机是有限状态自动机的简称,是现实事物运行规则抽象而成的一个数学模型。

  • 投币式旋转闸门

  • 程序员的一天状态扭转图

用法

除了用于此处介绍的反应系统建模之外,有限状态机在许多不同领域都具有重要意义,包括电气工程语言学计算机科学哲学生物学数学视频游戏编程逻辑

有限状态机是自动机理论和计算理论研究的一类自动机。在计算机科学中,有限状态机广泛用于应用行为建模(控制理论)、硬件数字系统设计、软件工程编译器网络协议计算语言学

概念

下面来给出状态机的四大概念。

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

第二个是 Event ,事件。事件就是执行某个操作的触发条件或者口令。对于自动门,“按下开门按钮”就是一个事件。

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

第四个是 Transition ,变换。也就是从一个状态变化为另一个状态。例如“开门过程”就是一个变换。

正则表达式

如果你做过任何类型的编程,你可能会遇到过正则表达式。正则表达式和有限状态机在功能上是等价的。您可以接受或与正则表达式匹配的任何内容都可以被状态机接受或匹配。例如,上面的模式可以匹配:

a(b*c|c*d)

正则表达式和有限状态机也有同样的限制。特别是它们都只能匹配或接受可以用有限内存处理的模式。那么它们不能匹配什么类型的模式呢?假设你只想匹配 a 和 b 的字符串,其中有多个 a,后跟相等数量的 b。或者 n a 后跟 n b,其中 n 是某个数字。例子是:

  • 病毒 a
  • f s m
  • 一怀愁绪,几年离索;山盟虽在,锦书难托;
  • 啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊b

起初,对于有限状态机来说,这看起来很容易。问题是很快耗尽状态,或者不得不假设无限数量的状态——此时它不再是有限状态机。假设创建了一个有限状态机,它最多可以接受 20 个 a,然后是 20 个 b。这工作正常,直到你得到一个由 21 a 和 21 b 组成的字符串,此时你需要重写你的机器来处理更长的字符串。对于可以识别的任何字符串,你的机器无法识别稍长一点的字符串,因为它内存不足。

这被称为抽水引理,它基本上说,如果你的模式有一个可以像上面那样重复的部分,那么这个模式就是不规则的。换句话说,既不能构造正则表达式也不能构造有限状态机来识别所有匹配模式的_字符串_。

如果你仔细看,你会注意到这种类型的模式,其中每个_a_都有一个匹配的_b_,看起来非常类似于 HTML,在任何一对标签中,你可能有任意数量的其他匹配的标签对。因此,虽然您可以使用正则表达式或有限状态机来识别 HTML 页面是否具有正确顺序的_html_、_head_和_body_元素,但您不能使用正则表达式来判断整个 HTML 页面是否有效,因为 HTML 不是常规模式。

1
2
3
4
5
6

<html>
    <head>
    </head>
    <body>
    </body>
</html>

状态机可以移动到显示它已读取 html 标签的状态,循环直到它到达 head 标签,循环直到它到达 head close 标签,等等。如果它成功进入最终状态,那么你有这些特定标签以正确的顺序排列。

有限状态机也可用于表示停车计时器、弹出机、自动加油泵和各种其他事物的机制。

应用

最后再来说说状态机的应用。状态机是一个对真实世界的抽象,而且是逻辑严谨的数学抽象,所以明显非常适合用在互联网数字领域。可以应用到各个层面上,例如硬件设计,编译器设计,以及编程实现各种具体业务逻辑的时候。

来举个例子。街上的自动售货机中明显能看到状态机逻辑。我们做一下简化,假设这是一台只卖2元一瓶的汽水的售货机,只接受五毛和一块的硬币。初始状态是”未付款“,中间状态有”已付款5毛“,”已付款1块“,”已付款1.5块“,”已足额付款“,四个状态。状态切换的触发条件是”投一块硬币“和”投5毛硬币“两种,”到达足额付款“状态,还要进行余额清零和弹出汽水操作。所以如果画出一张完整的状态转换图,也会是比较复杂的一张图了。而实际中的售货机对应的状态机就会更加复杂了。

By the way,跟状态机类似的概念还有_图灵机_就是计算机底层采用的计算模型。

状态机在履约服务中的应用

我们先用一张图梳理下履约服务的业务场景:

不难看出来,针对最初版本的履约服务状态机还是非常的简单的,我们也可以称之为有限状态机。

迭代:状态越来越多后应该如何控制?

改一点则动其筋骨

迭代后履约服务的业务场景:

落地:履约服务OMS是如何落地状态机

最初版本:全部是乐观锁控制

第二版本:工厂模式+状态模式

第三版本:引入 squirrel-foundation

compile("org.squirrelframework:squirrel-foundation:0.3.9")

public class QuickStartSample {
// 1\. Define State Machine Event
enum FSMEvent {
    ToA, ToB, ToC, ToD
}

// 2\. Define State Machine Class
@StateMachineParameters(stateType=String.class, eventType=FSMEvent.class, contextType=Integer.class)
static class StateMachineSample extends AbstractUntypedStateMachine {
    protected void fromAToB(String from, String to, FSMEvent event, Integer context) {
        System.out.println("Transition from '"+from+"' to '"+to+"' on event '"+event+
            "' with context '"+context+"'.");
    }

    protected void ontoB(String from, String to, FSMEvent event, Integer context) {
        System.out.println("Entry State \\'"+to+"\\'.");
    }
}

public static void main(String\[\] args) {
    // 3\. Build State Transitions
    UntypedStateMachineBuilder builder = StateMachineBuilderFactory.create(StateMachineSample.class);
    builder.externalTransition().from("A").to("B").on(FSMEvent.ToB).callMethod("fromAToB");
    builder.onEntry("B").callMethod("ontoB");
    
    // 4\. Use State Machine
    UntypedStateMachine fsm = builder.newStateMachine("A");
    fsm.fire(FSMEvent.ToB, 10);
    
    System.out.println("Current state is "+fsm.getCurrentState());
}

}

/\*\*
 \* 状态枚举
 */
NULL(-999, "默认状态"),
ABANDON(-1, "已废弃"),
WAIT_ISSUED(0, "未下发"),
ISSUED(10, "已下发"),
OUT_STOCK(20, "已出库"),
DELIVERED(30, "配送完成"),
SIGNED(40, "已签收");

/\*\*
 \* 操作类型
 */
NEW_ORDER(10, "工厂单创建"),
ORDER\_OUT\_STOCK(20, "工厂单已出库"),
ORDER_DELIVERED(30, "工厂单已配送"),
ORDER_SIGNED(40, "工厂单已签收");

/\*\*
 \* 初始化工厂模型
 */
private StateMachineFactory() {
    this.orderStateMachineBuilder = StateMachineBuilderFactory.create(OrderStateMachine.class);
    for (ProductionOrderStatusChangeEnum event : ProductionOrderStatusChangeEnum.values()) {
        orderStateMachineBuilder.externalTransition().from(event.getFromStatus()).to(event.getToStatus())
                .on(event).callMethod(StateMachineConstants.METHOD_NAME);
    }
}

/\*\*
 \* 获取订单状态机
 \*
 \* @param initState 初始状态
 \* @return 状态机
 */
public OrderStateMachine getOrderStateMachine(ProductionOrderStatusEnum initState) {
    OrderStateMachine orderStateMachine = this.orderStateMachineBuilder.newUntypedStateMachine(initState);
    orderStateMachine.setOrderActionFactory(orderActionFactory);
    return orderStateMachine;
}


    /\*\*
     \* 状态流传
     */
    public void onStateChange(S fromStatus, S toState, E event, Object context) {
        try {
            // 状态机已经触发了一个事件,触发了以后,导致状态,state,从from里转到to去 event,from,to,传递进来的订单创建request对象会作为context传递进来
            onStateChangeInternal(fromStatus, toState, event, context);
        } catch (Exception e) {
            exceptionThreadLocal.set(e);
        }
    } 

总结

总结一下,状态机不是实际机器设备,而是一个数学模型,通常体现为一个状态转换图。落地到履约服务中我们可以简单的理解为一种事件带着一种状态的扭转,涉及到的相关概念是 State 状态,Event 事件,Action 动作,Transition 转换。

参考:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章