如何以面向對象的思想設計有限狀態機

2021-01-07 計算機java編程

狀態機的概念

有限狀態機又稱有限狀態自動機,簡稱狀態機,是表示有限個狀態以及在這些狀態之間的轉移和動作等行為的數學計算模型,用英文縮寫也被簡稱為 FSM。FSM 會響應「事件」而改變狀態,當事件發生時,就會調用一個函數,而且 FSM 會執行動作產生輸出,所執行的動作會因為當前系統的狀態和輸入的事件不同而不同。

問題背景

為了更好地描述狀態機的應用,這裡用一個地鐵站的閘機為背景,簡單敘述一下閘機的工作流程:通常閘機默認是關閉的,當閘機檢測到有效的卡片信息後,打開閘機,當乘客通過後,關閉閘機;如果有人非法通過,那麼閘機就會產生報警,如果閘機已經打開,而乘客仍然在刷卡,那麼閘機將會顯示票價和餘額,並在屏幕輸出「請通過,謝謝」。在了解了閘機的工作流程之後,我們就可以畫出閘機的狀態圖,狀態圖如下:

在上圖中,線條上面的字表示的是:閘機輸入事件/閘機執行動作,方框內表示的是閘機的狀態。

除了使用狀態圖來表示系統的工作流程外,我們也可以採用狀態表的方式來表示系統的工作流程,狀態表如下所示:

通過上述我們已經知道閘機的工作流程了,接下來我們來看具體的實現。

代碼實現

嵌套的 switch 語句

使用嵌套的 switch 語句是最為直接的辦法,也是最容易想的方法,第一層 switch 用於狀態管理,第二層 switch 用於管理各個狀態下的各個事件。代碼實現可以用下述偽代碼來實現:

上述代碼雖然很直觀,但是狀態和事件都出現在一個處理函數中,對於一個大型的 FSM 中,可能存在大量的狀態和事件,那麼代碼量將是非常冗長的。為了解決這個問題,可以採用狀態轉移表的方法來處理。

狀態轉移表

為了減少代碼的長度,可以使用查表法,將各個信息存放於一個表中,根據事件和狀態查找表項,找到需要執行的動作以及即將轉換的狀態。

從上述我們可以看到如果要往狀態機中添加新的流程,那麼只需要往狀態表中添加東西就可以了,也就是說整個狀態機的維護及管理只需要把重心放到狀態轉移表的維護中就可以了,從代碼量也可以看出來,採用狀態轉移表的方法相比於第一種方法也大大地縮減了代碼量,而且也更容易維護。但是對於狀態轉移表來說,缺點也是顯而易見的,對於大型的 FSM 來說,遍歷狀態轉移表需要花費大量的時間,從而影響代碼的執行效率。那要怎樣設計代碼量少,又不需要以遍歷狀態轉移表的形式從而花費大量時間的狀態機呢?這個時候就需要以面向對象的思想來設計有限狀態機。

面向對象法設計狀態機

面向對象基本概念

以面向對象的思想實現的狀態機,大量涉及了對於函數指針的用法,必須對這個概念比較熟悉

上述所提到了兩個設計方法都是基於面向過程的一種設計思想,面向過程編程(POP)是一種以過程為中心的編程思想,以正在發生的事件為主要目標,指導開發者利用算法作為基本構建塊構建複雜系統。即將所要介紹的面向對象編程(OOP)是利用類和對象作為基本構建塊,因此分解系統時,可以從算法開始,也可以從對象開始,然後利用所得到的結構作為框架構建系統。提到面向對象編程,那自然繞不開面向對象的三個基本特徵:

封裝:隱藏對象的屬性和實現細節,僅僅對外公開接口繼承:使用現有類的所有功能,並在無需重新編寫原來的類的情況下對這些功能進行擴展,C 語言使用 struct 的特性實現繼承多態性:使用相同的方法,根據對象的類型調用不同的處理函數。上述對於面向對象的三個基本特徵做了一個簡單的介紹,封裝和繼承的概念都都比較清晰,多態性這個特點可能會有所迷惑,在這裡筆者用在書中看到一個例子來解釋多態性,例子是這樣的:要求畫一個形狀,這個形狀是可能是圓形,矩形,星形,無論是什麼圖形,其共性都是需要調用一個畫的方法來進行繪製,繪製的形狀可以通過函數指針調用各自的繪圖代碼繪製,這就是多態的意義,根據對象的類型調用不同的處理函數。在介紹了上述很基本的概念之後,我們來看狀態機的設計。

實現細節

上述代碼的思想實現的有限狀態機相比於前兩種不需要進行大量的遍歷,也不會導致代碼量的冗長,看似已經比較完美了,但是我們再仔細想想,如果此時狀態更改了,那 turnstile_card 函數和 turnstile_pass 函數都要更改,也就是說事件和狀態存在著耦合,這與「高內聚,低耦合」的思想所違背,也就是說如果我們要繼續優化代碼,那需要對事件和狀態進行解耦。

狀態和事件解耦

將事件與狀態相分離,從而使得各個狀態的事件處理函數非常的單一,因此在這裡需要定義一個狀態類:

我們由淺入深地來思考這個問題,首先我們可以想到把閘機當作一個對象,那麼這個這個對象的職責就是處理 card 事件(刷卡)和 pass 事件(通過閘機),閘機會根據當前的狀態執行不同的動作,也就有了如下的代碼:

在定義了狀態類之後,我們就可以使用狀態類創建 lock 和 unlock 的實例並初始化。

在這裡需要補充一下上述初始化項裡函數裡的具體實現。

這樣,也就實現了狀態與事件的解耦,閘機不再需要判斷當前的狀態,而是直接調用不同狀態提供的 card() 和 pass() 方法。定義了狀態類之後,由於閘機是整個系統的中心,我們還需要定義閘機類,由於 turnstile_state_t 中只存在方法,並不存在屬性,那麼我們可以這樣來定義閘機類:

到這裡,我們已經定義了閘機類,閘機狀態類,以及閘機狀態類實例,他們之間的關係如下圖所示:

通過圖中我們也可以看到閘機類是繼承於閘機狀態類的,locked_state 和 unlocked_state 實例是由閘機狀態類派生而來的,那最底下的那個箭頭是為什麼呢?這是在後面需要講到的對於閘機狀態轉換的處理,在獲取輸入事件調用具體的方法進行處理後,我們需要修改閘機類的p_state,所以也就有了這個箭頭。

相比於最開始定義的閘機類,這個顯得更加簡潔了,同時 p_state 可以指向相應的狀態對象,從而調用相應的事件處理函數。

在定義了一個閘機類之後,就可以通過閘機類定義一個閘機實例:

turnstile_t turnstile;

然後通過函數進行初始化:

整個系統閘機作為中心,進而需要定義閘機類的事件處理方法,定義方法如下:

到這裡,我們回顧前文所述,我們已經能夠對閘機進行初始化並使得閘機根據不同的狀態執行不同的處理函數了,再回顧整個閘機的工作流程,我們發現閘機在工作的時候會涉及到從 locked 狀態到 unlocked 狀態的相互變化,也就是狀態的轉移,因此狀態轉移函數可以這樣實現:

而狀態的轉移是在事件處理之後進行變化的。那麼我們可以這樣修改處理函數,這裡用輸出語句替代閘機動作執行函數:

既然處理函數都發生了變化,那麼閘機狀態類也應該發生更改,更改如下:

但是回顧之前我們給出的閘機類和閘機狀態類的關係,閘機類是繼承於閘機狀態類的,也就是說先有的閘機狀態類後有的閘機類,但是這裡卻在閘機狀態類的方法中使用了閘機類的參數,其實這樣也是可行的,需要提前對閘機類進行處理,總的閘機類狀態類定義如下:

上述就是所有的關於狀態機的相關定義了,下面通過上述的定義實現狀態機的實現:

上述代碼運行結果如下:

結論

以上便是筆者關於狀態機的全部總結,講述了面向過程和面向對象兩種實現方法,雖然從篇幅上看面向對象的方法要更為複雜,但是代碼的執行效率以及長度都要優於面向過程的方法,所以了解面向對象的程序設計方法是很有必要的。

相關焦點

  • 用STATECAD快速設計有限狀態機
    控制單元的實現方式有: 有限狀態機、控制寄存器和微代碼控制器等。有限狀態機在時間尺度上對其控制信號進行離散化控制, 利用狀態轉移使控制信號在有限狀態機的狀態節拍控制下變化, 以實現對被控對象的控制。有限狀態機設計的關鍵是如何把一個實際的時序邏輯關係抽象成一個時序邏輯函數,傳統的電路圖輸入法通過直接設計寄存器組來實現各個狀態之間的轉換, 而用硬體描述語言來描述有限狀態機, 往往是通過充分發揮硬體描述語言的抽象建模能力,通過對系統在系統級或寄存器傳輸級進行描述來建立有限狀態機。EDA 工具的快速發展,使通過CAD快速設計有限狀態機自動化成為可能。
  • 有限狀態機的FPGA設計
    有限狀態機是一種常見的電路,由於時序電路和組合電路組成,設計有限狀態機的第一步是確定採用Moore狀態機還是採用Mealy狀態機。Mealy狀態機的狀態轉變不僅和當前狀態有關,而且和各輸入信號有關;Moore狀態機的轉變只和當前狀態有關。
  • 利用有限狀態機的交通燈控制系統設計與仿真
    摘要:基於硬體電路設計軟體化的思想,根據路口交通燈控制功能要求,以可編程邏輯器件(FPGA)為硬體基礎,以有限狀態機為設計基礎,通過對系統狀態及其轉移關係的定義,運用多進程方式描述硬體模塊的邏輯關係,用VHDL語言編程實現了交通燈控制系統,經仿真,並在實驗箱上進行功能測試
  • 初學者對有限狀態機(FSM)的設計的認識
    有限狀態機(FSM)是一種常見的電路,由時序電路和組合電路組成。設計有限狀態機的第一步是確定採用Moore狀態機還是採用Mealy狀態機。(Mealy型:狀態的轉變不僅和當前狀態有關,而且跟各輸入信號有關;Moore型:狀態的轉變只和當前狀態有關)。
  • 用VHDL設計有限狀態機的方法
    受控部分通常是設計者們所熟悉的各種功能電路,設計較為容易。主要任務是設計控制器,而其控制功能可以用有限狀態機來實現。因而有必要深入探討有限狀態機的設計方法。本文引用地址:http://www.eepw.com.cn/article/150667.htm1 狀態機設計的一般方法  傳統的設計方法是首先繪製出控制器的狀態圖,並由此列出狀態表,再合併消除狀態表中的等價狀態項。在完成狀態寄存器的分配之後,根據狀態表求出次態及輸出方程,最後畫出設計原理圖。
  • 基於VHDL的MTM總線主模塊有限狀態機設計
    摘要:為了能夠更簡潔嚴謹地描述MTM總線的主模塊有限狀態機的狀態轉換,同時減少FPGA晶片功耗,提高系統穩定性,文中在分析MTM總線結構和主模塊有限狀態機模型的基礎上,基於VHDL語言採用「單進程」式對該有限狀態機進行了設計,並在QuartusⅡ開發軟體中實現了對語言代碼的編譯及程序的時序仿真和功能仿真
  • Verilog HDL設計進階:有限狀態機的設計原理及其代
    由於寄存器傳輸級(RTL)描述是以時序邏輯抽象所得到的有限狀態機為依據的,所以把一個時序邏輯抽象成一個同步有限狀態機是設計可綜合風格的Verilog HDL模塊的關鍵。在本章中我們將通過各種實例由淺入深地來介紹各種可綜合風格的Verilog HDL模塊,並把重點放在時序邏輯的可綜合有限狀態機的Verilog HDL設計要點。
  • 基於有限狀態機的飛行器自毀系統時序控制設計
    分析飛行器自毀系統工作原理,採用複雜可編程邏輯器件(CPLD)實現了飛行器自毀系統設計,結合CPLD的特點,提出一種基於改進型有限狀態機的飛行器自毀系統時序控制的設計方法,並在CPLD中予以實現。仿真及實驗表明,基於有限狀態機的飛行器自毀系統定時精度達到納秒級,可以有效地控制自毀信號輸出並消除毛刺現象,很好地滿足系統性能要求。該方法具有結構簡單緊湊、成本低、可靠性高、精度高等優點。
  • Verilog HDL設計進階:有限狀態機的設計原理及其代碼風格
    由於寄存器傳輸級(RTL)描述是以時序邏輯抽象所得到的有限狀態機為依據的,所以把一個時序邏輯抽象成一個同步有限狀態機是設計可綜合風格的Verilog HDL模塊的關鍵。在本章中我們將通過各種實例由淺入深地來介紹各種可綜合風格的Verilog HDL模塊,並把重點放在時序邏輯的可綜合有限狀態機的Verilog HDL設計要點。
  • 基於FPGA與有限狀態機的高精度測角系統的設計與實
    本文介紹的有限狀態機方法,在FPGA上可以有效消除抖動引起的計數幹擾,提高計數的精度[1]。1 方案設計1.1 系統組成雷射跟蹤測量系統的核心處理模塊主要由ARM處理器,FPGA組成。2 理論分析與算法2.1 有限狀態機原理在編碼器的一個輸出周期內
  • 產品經理不得不知的——有限狀態機
    直到某天我看到有限狀態機的原理,才知道我太天真了。比如騰訊公司在深圳的地址,有如下的各種寫法:廣東省深圳市騰訊大廈廣東省518075深圳市南山區科技園騰訊大廈深圳市510875科技園騰訊大廈深圳市南山區科技園騰訊大廈廣東省深圳市科技園中一路騰訊公司…這些地址寫得都有點不清楚,那麼程序是如何準確地識別這些信息呢?下圖所示的是一個識別中國地址有限狀態機的例子。
  • 跟我學java編程—深入理解面向對象的繼承思想
    繼承是面向對象設計的重要思想,其核心是代碼的復用和程序功能高度的擴展性。繼承在詞典中的解釋是把前人的知識、文化、思想、財產、知識等接受過來。在面向對象中,繼承是對類而言的,新類可以繼承已有類的屬性和方法,這樣做的好處是新類可以復用原有類所有的代碼,復用的同時又可以定義新的方法和屬性來擴展原有類的功能。為了理解繼承思想,下面看一個案例。某出版機構準備要通過微信小程序實現產品在微信媒體的推廣和銷售,出版機構的產品包括圖書、音頻、視頻,圖書又分為紙書和電子書。
  • 如何繪畫狀態機來描述業務的變化
    因為技術會反覆的問,有幾種狀態啊,怎麼轉移啊,啥時候轉移啊,什麼時候截止狀態啊,系統根據什麼條件判斷狀態啊……一、為什麼需要使用狀態機?講個親身的例子,去年我設計電商系統的訂單模塊,就犯過類似的問題。一開始參照淘寶的訂單系統,將訂單設計為待付款、已付款、已發貨、已完成,已關閉等5個狀態。上線後很快就發現有問題。
  • 面向對象技術在單片機系統設計中的應用
    面向對象的設計方法和技術與單片機系統設計相結合就產生了面向對象的單片機系統設計,其主要思路是把單片機系統的每個接口電路都看成了一個一個的對象。單片機系統設計的任務也就變成了各接口模塊對象的組合,這樣單片機系統開發者就可以把精力更多地用在系統設計上,特別是軟體的設計。
  • 面向對象編程的災難:是時候考慮更新換代了!
    獨立程序的狀態永遠不會與外部世界共享(封裝)。面向對象編程從來沒有打算擁有諸如繼承、多態性、「new」關鍵字和無數設計模式之類的東西。最純粹的面向對象編程設計Erlang是面向對象編程最純粹的形式。與更主流的語言不同,它關注面向對象的核心思想——消息傳遞。
  • 基於有限狀態機的嵌入式系統模型校驗技術
    答案當然不是唯一的,因為不同應用領域的需求(或設計)差異很大。例如,銀行系統和空間系統在系統規模、結構、復 雜度、系統數據的屬性及執行操作上的需求差異就很明顯。相反,大多數實時嵌入式或安全臨界系統都面向控制,而不是數據,這意味著這些系統的動態特性遠比業 務邏輯(由系統維護的內部數據的結構及操作)重要。
  • 簡單的狀態機入門!
    大家晚上好,今天給大家分享一個篇關於狀態機的學習。1、有限狀態機:常說的狀態機是有限狀態機FSM(Finite State Machine)。FSM指的是有有限個狀態(一般是一個狀態變量的值),這個機器同時能夠從外部接收信號和信息輸入,機器在接收到外部輸入的信號後會綜合考慮當前自己的狀態和用戶輸入的信息,然後機器做出動作:跳轉到另一個狀態。
  • 面向對象六大原則
    這篇文章主要講的是面向對象設計中,應該遵循的六大原則。只有掌握了這些原則,才能更好的理解設計模式。 我們接下來要介紹以下6個內容。開閉原則的定義是軟體中的對象(類,模塊,函數等)應該對於擴展是開放的,但是對於修改是關閉的。 當需求發生改變的時候,我們需要對代碼進行修改,這個時候我們應該儘量去擴展原來的代碼,而不是去修改原來的代碼,因為這樣可能會引起更多的問題。 這個準則和單一職責原則一樣,是一個大家都這樣去認為但是又沒規定具體該如何去做的一種原則。
  • 詳解Java面向對象開發方法,看清華大牛帶你深入淺出剖析
    結構化設計屬於自頂向下的設計,在設計階段就不得不考慮如何實現系統的功能,因為分解功能的過程其實就是實現功能的過程。結構化設計的局限性在於不能靈活地適應用戶不斷變化的需求。當用戶需求發生變化,比如要求修改現有軟體功能的實現方式或者要求追加新的功能時,就需要自頂向下地修改模塊的結構,有時候甚至整個軟體系統的設計被完全推翻。
  • FPGA工程師:如何在FPGA中實現狀態機?
    對於設計人員來說,滿足這些行動和序列要求的最佳方法則是使用狀態機。狀態機是在數量有限的狀態之間進行轉換的邏輯結構。一個狀態機在某個特定的時間點只處於一種狀態。但在一系列觸發器的觸發下,將在不同狀態間進行轉換。