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

2021-04-09 09:17:38 字數 3329 閱讀 3530

有限自動機(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) 

break;

}// 處理狀態closed的分支

case (closed):

// 檢查是否有lockdoor事件

if (lockdoor())

break;

}// 處理狀態locked的分支

case (locked):

break;

}// 處理狀態unlocked的分支

case (unlocked):

// 檢查是否有opendoor事件  

if (opendoor())

break;}}

使用switch語句實現的有限狀態機的確能夠很好地工作,但**的可讀性並不十分理想,主要原因是在實現狀態之間的轉換時,檢查轉換條件和進行狀態轉換都是混雜在當前狀態中來完成的。例如,當城門處於opened狀態時,需要在相應的case中呼叫closedoor()函式來檢查是否有必要進行狀態轉換,如果是的話則還需要呼叫changestate()函式將當前狀態切換到closed。顯然,如果在每種狀態下都需要分別檢查多個不同的轉換條件,並且需要根據檢查結果讓狀態機切換到不同的狀態,那麼這樣的**將是枯燥而難懂的。從**重構的角度來講,此時更好的做法是引入checkstatechange()和performstatechange()兩個函式,專門用來對轉換條件進行檢查,以及啟用轉換時所需要執行的各種動作。這樣一來,程式結構將變得更加清晰:

switch (state) 

break;

}// 處理狀態closed的分支

case (closed):

break;

}// 處理狀態locked的分支

case (locked):

break;

}// 處理狀態unlocked的分支

case (unlocked):

break;}}

但checkstatechange()和performstatechange()這兩個函式本身依然會在面對很複雜的狀態機時,內部邏輯變得異常臃腫,甚至可能是難以實現。

在很長一段時期內,使用switch語句一直是實現有限狀態機的唯一方法,甚至像編譯器這樣複雜的軟體系統,大部分也都直接採用這種實現方式。但之後隨著狀態機應用的逐漸深入,構造出來的狀態機越來越複雜,這種方法也開始面臨各種嚴峻的考驗,其中最令人頭痛的是如果狀態機中的狀態非常多,或者狀態之間的轉換關係異常複雜,那麼簡單地使用switch語句構造出來的狀態機將是不可維護的。

三、自動生成狀態機

為實用的軟體系統編寫狀態機並不是一件十分輕鬆的事情,特別是當狀態機本身比較複雜的時候尤其如此,許多有過類似經歷的程式設計師往往將其形容為"毫無創意"的過程,因為他們需要將大量的時間與精力傾注在如何管理好狀態機中的各種狀態上,而不是程式本身的執行邏輯。作為一種通用的軟體設計模式,各種軟體系統的狀態機之間肯定會或多或少地存在著一些共性,因此人們開始嘗試開發一些工具來自動生成有限狀態機的框架**,而在linux下就有乙個挺不錯的選擇──f**e(finite state machine editor)。

圖2 視覺化的f**e

f**e是乙個基於qt的有限狀態機工具,它能夠讓使用者通過圖形化的方式來對程式中所需要的狀態機進行建模,並且還能夠自動生成用c++或者python實現的狀態機框架**。下面就以圖1中城門的狀態機為例,來介紹如何利用f**e來自動生成程式中所需要的狀態機**。

3.1狀態機建模

首先執行fs  

有限狀態機

有限狀態機 finite state machine,fsm 又稱有限狀態自動機,簡稱狀態機,是表示有限個狀態以及在這些狀態之間的轉移和動作等行為的數學模型。狀態儲存關於過去的資訊,就是說 它反映從系統開始到現在時刻的輸入變化。轉移指示狀態變更,並且用必須滿足來確使轉移發生的條件來描述它。動作是在給...

有限狀態機

以前,只碰到過 陣列中所有數字只出現2次,只有乙個出現1次,找這個數的問題 每次迴圈異或陣列中元素,最後的結果就是single one。這次換作出現3次就懵逼了,主要原因,沒有使用過有限狀態機,應該說是連概念都沒有,所以這次一定要好好記錄一下 關於這道題的解釋discussion中woshidais...

有限狀態機

需要掌握的名詞 數字系統有兩大類有限狀態機 finite state machine,fsm moore狀態機和mealy狀態機。狀態機名 次態輸出 moore摩爾 f 現狀,輸入 g 現狀 mealy公尺粒 f 現狀,輸入 g 現狀,輸入 mealy型狀態機 下一狀態不但與當前狀態有關,還與當前輸...