從工作流狀態機實踐中總結狀態模式使用心得

banq發表於2003-12-07
狀態模式好像是很簡單的模式,正因為狀態好像是個簡單的物件,想複雜化實現設計模式就不是容易,誤用情況很多。

我個人曾經設計過一個大型遊戲系統的遊戲狀態機,遊戲狀態可以說是遊戲設計的主要架構,但是由於系統過分複雜
和時間倉促,並沒有真正實現狀態模式。

目前在實現一個電子政務專案中,需要進行流程狀態變化,在電子政務設計中,我發現,如果一開始完全按照工作流
規範開發,難度很大,它和具體專案實踐結合無法把握,而且工作流規範現在有wfmc,還有bpml,選擇也比較難。因
此,我決定走自創的中間道路。

因為,我需要做一個狀態機API,或者說狀態機框架,供具體系統呼叫:類如公文流轉應用或資訊發報送應用等。

好的狀態模式必須做到兩點:
1. 狀態變化必須從外界其它邏輯劃分出來。
2. 狀態必須可方便擴充,對其它程式碼影響非常小。

要做到這兩點,必須先明確狀態變化機制,狀態變化實際是由Event事件驅動的,可以認為是Event-condition-State,
MVC模式一般是Event-condition-Action實現。狀態模式需要封裝的是Event-condition-State中的condition-State
部分。

清晰理解狀態和流程的關係也非常重要,因為狀態不是孤立的,可以說和流程是點和線的關係,狀態從一個方面說明
了流程,流程是隨時間而改變,狀態是擷取流程某個時間片。因此,必須明白使用狀態模式實現狀態機實際是為了更
好地表達和說明流程。

狀態和流程以及事件的關係如下:

                 |Event
___currentState__|______newState___
<p class="indent">




圖中表示了是事件改變了流程的狀態,在業務邏輯中,經常發生的是事件,如果不使用狀態模式,需要在很多業務邏
輯處實現事件到狀態判定和轉換,這有很多危險性。

最大的危險是系統沒有一個一抓就靈的主體結構,以那個遊戲系統為例,在沒有狀態模式對狀態提煉的情況下,狀態
改變由每個程式設計師想當然實現,導致每個程式設計師開發的功能在整合時就無法除錯,因為這個程式設計師可能不知道那個程
序員的程式碼在什麼執行條件下改變了遊戲狀態,結果導致自己的程式碼無法執行。

這種現象實際上拒絕了專案管理的協作性,大大地拖延專案進度(程式設計師之間要反覆商量討論對方程式碼設計)。從這
一點也說明,一個好的架構設計是一個專案快速成功完成的基礎技術保證,沒有這個技術基礎,再先進的專案管理手
段也是沒有效率的,或者是笨拙的。

狀態模式對於很多系統來說,確實是架構組成一個重要部分。

下面繼續討論如何實現一個好的狀態模式,為了實現好的狀態模式,必須在狀態模式中封裝下面兩個部分:
1. 狀態轉換規則(行為)
2. 狀態屬性(屬性)

狀態轉換行為有兩種劃分標準:
1. run和next兩個行為,run是當前狀態執行行為,next是指在Event參與下,幾種可能轉向的下一個狀態。
2. stateEnter和stateExit, 狀態進入和狀態退出。

如果用進入一個個房間來表示狀態流程的話, 第一種分析是隻重視著“在房間裡”和“如何轉入下一個房間”,這兩種行
為一旦確定,可以被反覆使用,進而一個流程的狀態切換可以全部表達出來。

第二中分析方法有所區別,只重視進入房間和離開房間這個兩個行為,同樣,這種模型也可以被反覆利用在其它房間,
一個流程的狀態切換也可以全部表達出來。

具體選擇取決於你的需求,比如,如果你在進入一個狀態開始,要做很多初始化工作,那麼第二種模型就很適合。

狀態變化都離不開一個主體物件,主體物件可以說包含狀態變化(行為)和狀態屬性(屬性),假設為StateOwner,
StateOwner有兩個部分組成:Task/事情和狀態。任何一個Task/事情都會對應一個狀態。

這樣,我們已經抽象出兩個主要物件:狀態State和StateOwner。

為了封裝狀態變化細節,我們可以抽象出一個狀態機StateMachine來專門實現狀態根據事情實現轉換。

這樣,客戶端外界透過狀態機可訪問狀態模式這個匣子。在實踐中,外界客戶端需要和狀態機實現資料交換,我們把
它也分為兩種:屬性和行為。

其中屬性可能需要外界告訴狀態狀態變化的主體物件Task,解決狀態的主人問題,是誰的問題;行為可能是需要持久
化當前這個主體物件的狀態到資料庫。

這兩種資料交換可以分別透過StateOwner和StateMachine與整個狀態機實現資料交換,這樣,具體狀態和狀態切換也
和外界實現瞭解耦隔離。

因此好的狀態模式實現必須有下列步驟:
(1)將每個狀態變成State的子類,實現每個狀態物件化。
(2)在每個狀態中,封裝著進入下一個狀態可能規則,這些規則是狀態變化的核心,換句話說,統一了狀態轉換的規則。
具體可採取run和next這樣的轉換行為。

下面是一個子狀態程式碼:


public class Running extends StateT{

  //
  public void run(StateOwner stateOwner){
     stateOwner.setCurrentState(this);
  }

  //轉換到下一個狀態的規則
  //當前是Running狀態,下一個狀態可能是暫停、結束或者強制退出等
  //狀態,但是絕對不會是Not_Started這樣的狀態
  //轉換規則在這裡得到了體現。
  public State next(Event e) {
    if(transitions == null){
       addEventState(new EventImp("PAUSE"), new Suspended());
       addEventState(new EventImp("END"), new Completed());
       addEventState(new EventImp("STOP"), new Aborted());
    }
    return super.next(e);
  }

}     



外界直接呼叫 StateMachine的關鍵方法transition;實行狀態的自動轉變。  


public class StateMachine {

 /**
   * 狀態切換
   * 根據Event引數,執行相應的狀態。
   * 1. 獲得當前狀態
   * 2. 使用當前狀態的next()轉換
   *                  |Event
   * ___currentState__|______newState___
   *
   * @param inputs
   */
  public final void transition(String taskid, Event e) throws Exception {
    State currentState = readCurrentState(taskid); //從資料庫獲得當前狀態
    StateOwner stateOwner = new StateOwner(taskid, currentState);
    //轉換狀態
    currentState = currentState.next(e);
    if (currentState != null) {
      currentState.run(stateOwner);
      saveCurrentState(stateOwner); //儲存當前狀態
    }
  }

} 

相關文章