擴展:“狀態機”從概念層演變落地

什麼是狀態機?

定義

我們先來給出狀態機的基本定義。一句話:

狀態機是有限狀態自動機的簡稱,是現實事物運行規則抽象而成的一個數學模型。

先來解釋什麼是“狀態”( 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 轉換。

參考:

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