為Linux應用構造有限狀態機器的方法

來源:互聯網
上載者:User
有限自動機(Finite Automata Machine)是電腦科學的重要基石,它在軟體開發領域內通常被稱作有限狀態機器(Finite State Machine),是一種應用非常廣泛的軟體設計模式(Design Pattern)。本文介紹如何構建基於狀態機器的軟體系統,以及如何利用Linux下的工具來自動產生實用的狀態機器架構。
  
   一、什麼是狀態機器
  有限狀態機器是一種用來進行對象行為建模的工具,其作用主要是描述對象在它的生命週期內所經曆的狀態序列,以及如何響應來自外界的各種事件。在物件導向的軟體系統中,一個對象無論多麼簡單或者多麼複雜,都必然會經曆一個從開始建立到最終消亡的完整過程,這通常被稱為對象的生命週期。一般說來,對象在其生命期內是不可能完全孤立的,它必須通過發送訊息來影響其它對象,或者通過接受訊息來改變自身。在大多數情況下,這些訊息都只不過是些簡單的、同步的方法調用而已。例如,在銀行客戶管理系統中,客戶類(Customer)的執行個體在需要的時候,可能會調用帳戶(Account)類中定義的getBalance()方法。在這種簡單的情況下,類Customer並不需要一個有限狀態機器來描述自己的行為,主要原因在於它當前的行為並不依賴於過去的某個狀態。
  
  遺憾的是並不是所有情況都會如此簡單,事實上許多實用的軟體系統都必須維護一兩個非常關鍵的對象,它們通常具有非常複雜的狀態轉換關係,而且需要對來自外部的各種非同步事件進行響應。例如,在VoIP電話系統中,電話類(Telephone)的執行個體必須能夠響應來自對方的隨機呼叫,來自使用者的按鍵事件,以及來自網路的信令等。在處理這些訊息時,類Telephone所要採取的行為完全依賴於它當前所處的狀態,因而此時使用狀態機器就將是一個不錯的選擇。
  
  遊戲引擎是有限狀態機器最為成功的應用領域之一,由於設計良好的狀態機器能夠被用來取代部分的人工智慧演算法,因此遊戲中的每個角色或者器件都有可能內嵌一個狀態機器。考慮RPG遊戲中城門這樣一個簡單的對象,它具有開啟(Opened)、關閉(Closed)、上鎖(Locked)、解鎖(Unlocked)四種狀態,1所示。當玩家到達一個處於狀態Locked的門時,如果此時他已經找到了用來開門的鑰匙,那麼他就可以利用它將門的目前狀態轉變為Unlocked,進一步還可以通過旋轉門上的把手將其狀態轉變為Opened,從而成功地進入城內。
  
  圖1 控制城門的狀態機器
   
  在描述有限狀態機器時,狀態、事件、轉換和動作是經常會碰到的幾個基本概念。
  
  狀態(State) 指的是對象在其生命週期中的一種狀況,處於某個特定狀態中物件必然會滿足某些條件、執行某些動作或者是等待某些事件。
  事件(Event) 指的是在時間和空間上佔有一定位置,並且對狀態機器來講是有意義的那些事情。事件通常會引起狀態的變遷,促使狀態機器從一種狀態切換到另一種狀態。
  轉換(Transition) 指的是兩個狀態之間的一種關係,表明對象將在第一個狀態中執行一定的動作,並將在某個事件發生同時某個特定條件滿足時進入第二個狀態。
  動作(Action) 指的是狀態機器中可以執行的那些原子操作,所謂原子操作指的是它們在啟動並執行過程中不能被其他訊息所中斷,必須一直執行下去。
  
   二、手工編寫狀態機器
  與其他常用的設計模式有所不同,程式員想要在自己的軟體系統中加入狀態機器時,必須再額外編寫一部分用於邏輯控制的代碼,如果系統足夠複雜的話,這部分代碼實現和維護起來還是相當困難的。在實現有限狀態機器時,使用switch語句是最簡單也是最直接的一種方式,其基本思路是為狀態機器中的每一種狀態都設定一個case分支,專門用於對該狀態進行控制。下面的代碼示範了如何運用switch語句,來實現圖1中所示的狀態機器:
  
  switch (state) {
  
   // 處理狀態Opened的分支
   case (Opened): {
  // 執行動作Open
  open();
  // 檢查是否有CloseDoor事件
  if (closeDoor()) {
   // 目前狀態轉換為Closed
   changeState(Closed)
  }
  break;
   }
  
   // 處理狀態Closed的分支
   case (Closed): {
  // 執行動作Close
  close();
  // 檢查是否有OpenDoor事件
  if (openDoor()) {
   // 目前狀態轉換為Opened
   changeState(Opened);
  }
  // 檢查是否有LockDoor事件
  if (lockDoor()) {
   // 目前狀態轉換為Locked
   changeState(Locked);
  }
  break;
   }
  
   // 處理狀態Locked的分支
   case (Locked): {
  // 執行動作Lock
  lock();
  // 檢查是否有UnlockDoor事件
  if (unlockDoor()) {
   // 目前狀態轉換為Unlocked
   changeState(Unlocked);
  }
  break;
   }
  
   // 處理狀態Unlocked的分支
   case (Unlocked): {
  // 執行動作Unlock
  unlock();
  // 檢查是否有LockDoor事件
  if (lockDoor()) {
   // 目前狀態轉換為Locked  
   changeState(Locked)
  }
  // 檢查是否有OpenDoor事件  
  if (openDoor()) {
   // 目前狀態轉換為Opened
   changeSate(Opened);
  }
  break;
   }
  
  }
  使用switch語句實現的有限狀態機器的確能夠很好地工作,但代碼的可讀性並不十分理想,主要原因是在實現狀態之間的轉換時,檢查轉換條件和進行狀態轉換都是混雜在目前狀態中來完成的。例如,當城門處於Opened狀態時,需要在相應的case中調用closeDoor()函數來檢查是否有必要進行狀態轉換,如果是的話則還需要調用changeState()函數將目前狀態切換到Closed。顯然,如果在每種狀態下都需要分別檢查多個不同的轉換條件,並且需要根據檢查結果讓狀態機器切換到不同的狀態,那麼這樣的代碼將是枯燥而難懂的。從代碼重構的角度來講,此時更好的做法是引入checkStateChange()和performStateChange()兩個函數,專門用來對轉換條件進行檢查,以及啟用轉換時所需要執行的各種動作。這樣一來,程式結構將變得更加清晰:
  switch (state) {
  
   // 處理狀態Opened的分支
   case (Opened): {
  // 執行動作Open
  open();
  // 檢查是否有激髮狀態轉換的事件產生
  if (checkStateChange()) {
   // 對狀態機器的狀態進行轉換
   performStateChange();
  }
  break;
   }
  
   // 處理狀態Closed的分支
   case (Closed): {
  // 執行動作Close
  close();
  // 檢查是否有激髮狀態轉換的事件產生
  if (checkStateChange()) {
   // 對狀態機器的狀態進行轉換
   performStateChange();
  }
  break;
   }
  
   // 處理狀態Locked的分支
   case (Locked): {
  // 執行動作Lock
  lock();
  // 檢查是否有激髮狀態轉換的事件產生
  if (checkStateChange()) {
   // 對狀態機器的狀態進行轉換
   performStateChange();
  }
  break;
   }
  
   // 處理狀態Unlocked的分支
   case (Unlocked): {
  // 執行動作Lock
  unlock();
  // 檢查是否有激髮狀態轉換的事件產生
  if (checkStateChange()) {
   // 對狀態機器的狀態進行轉換
   performStateChange();
  }
  break;
   }
  
  }
  但checkStateChange()和performStateChange()這兩個函數本身依然會在面對很複雜的狀態機器時,內部邏輯變得異常臃腫,甚至可能是難以實現。
  
  在很長一段時期內,使用switch語句一直是實現有限狀態機器的唯一方法,甚至像編譯器這樣複雜的軟體系統,大部分也都直接採用這種實現方式。但之後隨著狀態機器應用的逐漸深入,構造出來的狀態機器越來越複雜,這種方法也開始面臨各種嚴峻的考驗,其中最令人頭痛的是如果狀態機器中的狀態非常多,或者狀態之間的轉換關係異常複雜,那麼簡單地使用switch語句構造出來的狀態機器將是不可維護的。
  
   三、自動產生狀態機器
  為實用的軟體系統編寫狀態機器並不是一件十分輕鬆的事情,特別是當狀態機器本身比較複雜的時候尤其如此,許多有過類似經曆的程式員往往將其形容為"毫無創意"的過程,因為他們需要將大量的時間與精力傾注在如何管理好狀態機器中的各種狀態上,而不是程式本身的運行邏輯。作為一種通用的軟體設計模式,各種軟體系統的狀態機器之間肯定會或多或少地存在著一些共性,因此人們開始嘗試開發一些工具來自動產生有限狀態機器的架構代碼,而在Linux下就有一個挺不錯的選擇──FSME(Finite State Machine Editor)。
  
  圖2 可視化的FSME
   
  FSME是一個基於Qt的有限狀態機器工具,它能夠讓使用者通過圖形化的方式來對程式中所需要的狀態機器進行建模,並且還能夠自動產生用C++或者Python實現的狀態機器架構代碼。下面就以圖1中城門的狀態機器為例,來介紹如何利用FSME來自動產生程式中所需要的狀態機器代碼。
  
  3.1狀態機器建模
  首先運行fs  
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.