【狀態管理】狀態機模型和狀態管理器模型


狀態

  本文只討論計算機里面的狀態,並且只是討論對象,對象其實是抽象的產物,所以狀態也取決於我們是如何對對象進行抽象和建模的,根據建模方法不同,對象也不同。對象分為有狀態的對象和無狀態的對象,無狀態對象特指那種特性形態固定不變的對象,通常他們在面向對象領域都是單例的,而有狀態對象不同,有狀態對象通常是多例的。舉個例子

  • 有狀態對象:Session對象,Session的概念在很多系統中會存在,因為它對每一次會話都會建立該次會話的特征
  • 無狀態對象:UserFactory對象,作為一個工廠對象,通常就是無狀態的

  以前的老系統EJB,有激活和鈍化的概念,但現在的編程系統都基本自己負責實現有狀態對象的存儲了,DDD中有一個概念就是rebuild模式,就是對象的重建。簡單的狀態是不需要關注的,但是如果要根據狀態進行驅動,那么就要對狀態進行管理,下面介紹幾種狀態驅動的模型,第一種是普通的狀態,第二種是狀態機,第三種是有限Action的狀態管理器

普通枚舉狀態

  這種方式的狀態是最簡單的對象狀態表示,我們時不時都會用上,例如你會在一個實例類中加一個字段:boolean isFinished ;用作表示對象是否已經是完成狀態,或者:boolean isRunning;用做表示對象是否在運行狀態,這種狀態一般都是可枚舉完的,而且不像狀態模式是狀態之間的扭轉,而是由調用者設置屬性狀態,例如 obj.setFinished ;

  注意,你可以不必拘泥於一種方式組織枚舉狀態,你也可以用一個棧組織這些狀態,例如:

/**
 *
 * 代表一個對話管理的目標狀態
 *
 **/
private Stack<Goal> interGoal = new Stack();

public void addGoal (Goal newGoal){
    interGoal.push(newGoal);  
}

public void doGoal (){
    Goal currentGoal =  interGoal.peek();
    currentGoal.doGoal;
    interGoal.poll;
}

  如上,對象的狀態是對話目標,對話目標可以用棧來表示,這樣的好處是:狀態可以記錄,回退;所以狀態的表示,只需要你巧妙利用數據結構,就能達到一些不可思議的效果。

FMS 有限狀態機

  狀態機是相對一個系統而言的,其實准確來說,是一個比較核心的對象而言,這個對象的職責和功能,是通過狀態機這種數學模型建模得以運作。例如我們的聚合根對象。

  狀態機,很多文章介紹會說直接等同於有限狀態機(FMS),FSM 解決一個輸入序列,經過 FSM,最終停留在什么狀態這樣一個問題。比較注重的是系統(對象)的狀態,和狀態之間的轉換,狀態是歷史輸入的一種結果,對於一個系統而言,狀態機基本包含以下幾種模型:

  • 事件(Event)
  • 動作(Action)
  • 狀態(State):現態,次態
  • 轉換

  狀態 State:狀態機的狀態是有限的,例如一個機器人對話系統的Session對象,就可以抽象出幾種狀態:

  • 初始狀態;
  • 思考決策狀態;
  • 動作執行狀態;
  • 待用戶回復狀態;
  • 待服務返回狀態;
  • 結束狀態。

  而當前時刻,Session只能處於一個狀態中;狀態轉換前后,前一個狀態稱為現態,后一個狀態稱為次態

  事件 Event:狀態機通常是通過事件驅動運行的,事件代表狀態機可以處理的事件,通常發送一件事后,會把事件類型和事件內容一並傳遞給狀態機。狀態機接收到事件消息后,調用相應的動作去處理該事件,處理結果可能會是現態到次態的轉換

  動作 Action:狀態機是如何響應不同的事件的呢,其實每一個狀態,都會遇到不同的事件,所以對不同的事件,也是有不同的處理的。比喻在待服務返回狀態,發生了待用戶說要轉換意圖的事件,那么事件就是《轉換意圖》,事件內容是《取消訂票》,那么就需要執行 《初始化意圖決策流程》這個動作,然后把狀態轉換為《思考決策狀態》。通常事件和狀態可以組合為一張表:

事件狀態表
屬性  初始狀態 思考決策狀態 動作執行狀態 待用戶回復狀態 待服務返回狀態 結束狀態
新意圖事件 Action1 Action2  ——  —— ——   Action3
服務返回事件  ——  —— Action2  Action1  ——  Action1
用戶返回事件 ——  Action1  ——  ——  Action2  ——
服務異常事件  ——  ——  Action3  ——  ——  Action3

 

   狀態轉換:介紹完動作和事件后,我們就清楚轉換是怎么得到的了,也就是狀態 s1 接收到事件 event1 后,執行了某些動作 action1,然后還把狀態轉換為了 s2,下圖是機器人Session對象的狀態轉換圖。  

 

TCP 連接協議狀態

  下面給出一個TCP協議的有限狀體機的示例,該例子估計大家都看得明白,Client客戶端和Server服務端,都對應有自己的狀態State和動作Action,分別對應着事件Event和轉換

  狀態:closed、syn_sent、established、fin_wait1、fin_wait2、time_wait、listen、syn_received、close_wait、last_act

  以上狀態區別,一般還按照自身是發起端還是主動關閉端區分,只有close和established兩種狀態是兩個端共有的。

  事件:事件即該端口接收到網絡的請求,sys\ack\fin等,這里還有事件附帶的數據,例如syn =a ,里面的a就是數據,我們一般稱之為 payload

  動作和狀態轉換關系具體可參考下圖:

 

 

狀態策略

  狀態模式是設計模式的內容,但和狀態機不態一樣。一個類如果有狀態,那么其狀態的表示是非常多的,而狀態模式很多時候就用一個狀態類代表其所處的狀態。有時候你需要的系統的狀態是很難窮舉的。所以狀態模式是一種比較低級的應用了。

  無限的狀態,有限的Action ——》狀態策略:通常,我們的對象建模很難做成狀態機,因為狀態很可能是無限的,但不管如何,我們的系統執行動作是有限的,也就是State是無限的,而Action是有限的,畢竟Action需要我們程序員手寫去實現,那么無限的狀態,如果對應這一個個有限的Action呢,我們可以抽象一個根據當前狀態選擇Action的算法,我們把該算法抽象為<狀態策略>。在介紹狀態策略之前,最好大家先學習一個框架Redux

  Redux:首先介紹下Redux,前端框架的一種,這個自稱是狀態機實現,每一個組件都是狀態機,這個是我認為最合適作為狀態機定義的實現,因為React接受外界輸入action后,會根據state重新渲染組件,所以會帶有觀察者模式的感覺,所以我認為它更接近狀態機的本質 —— 狀態驅動

  下面一段redux的小demo,代表整個redux的工作原理

function createStore(reducer, preloadedState) {
    let state = preloadedState
    let listeners = []

    function subscribe(listener) {
        listeners.push(listener)
    }

    function getState() {
        return state
    }

    function dispatch(action) {
        state = reducer(state, action)
        for (listener of listeners) {
            listener()
        }
    }
    /**通過一個不匹配任何 reducer的 type,來全部的初始值*/
    /**redux 中是使用一個隨機的字符串來保證不匹配任何 reducer, 這里使用了 ES6 的 Symbol 類型*/
    dispatch({ type: Symbol() });

    return {
        subscribe,
        getState,
        dispatch
    }
}

  策略:Policy,是一種Action選擇的算法實現,它的輸入是當前狀態,輸出是動作 Action,策略可以用規則實現,也可以用機器學習等算法實現。我們現場定義一個策略的接口

public class Policy{
    // 根據當前狀態,選擇處理這個狀態的策略
    Action chooseAction(State state);  

}

  有了接口,我們就不用擔心具體的實現,只需要執行Action即可。而Action的執行,當然是交給狀態管理器了。隨着狀態管理器執行完Action,Action會改變狀態,也會出發觀察者,所有外界就得到通知,外界處理完后會回饋到系統中,系統收到反饋后,這里注意的是,Action會收到這種反饋,然后改變狀態State,當State改變后,系統可以把新的State傳遞給Policy,然后繼續這個良性循環,從而整個狀態機,得到了驅動。

  狀態管理器:Redux就是一個狀態管理器,但是比較無奈的是,它接受的輸入是外界的Action,使得狀態改變,然后通知所有狀態的觀察者,這種模式的實現,也只有前端比較合適應用了,下面我給大家再介紹一個例子,那就是對話系統的例子,DST(Dialog State Tracker)和DP(Dialog Policy),前者為對話狀態跟蹤,后者為對話策略,其中對話策略可以用機器學習算法實現;

 

狀態機與線程安全

  狀態模式,一般情況下,都是一件事一件事發生和處理的,但是有時候我們需要處理的系統並非如此簡單,而是大量的事件在不同的事件發生,這個時候,我們可以有以下幾種建模方式:

  1、單線程:這種最簡單,一個狀態機維護一個事件隊列,所有事件按照時間的順序入隊,然后用單一的線程處理該隊列的事件,這樣可以保證每個事件的事務性,但缺點也是有的,處理速度會變得更慢,而且會導致無事件的時候,線程閑置,優點是簡單,而且可以給事件作優先級隊列,使得重要的事件可以優先處理。

  2、多狀態:如果事件數量較少,我們可以把事件的先來后到的所有組合情況的狀態都抽象出來,每一個事件的組合發生順序都有一個對應的狀態,優點是事件可以多線程,但缺點是狀態會過多,難維護,所以適合事件數量較少的情況,最好是兩三個事件。

  3、保持冪等:狀態按照業務需求設計,讓狀態機可以多線程運作事件,但也並非沒有同步機制,可以用狀態的改變的原子性同步,會有個缺點就是可能會在狀態切換的時候發生ABA問題,但ABA也可以理解的,畢竟事件都有權利按照自己的事務執行,所以在action的設計中,要特別的注意相關所有狀態對象的冪等處理,保證同一個消息被狀態機處理多次,效果是相同的,也就是包裝action獨立於事件即可。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM