狀態機思路在程式設計中的應用

2021-07-05 06:36:15 字數 4278 閱讀 1194

狀態機的概念

狀態機是軟體程式設計中的乙個重要概念,比這個概念更重要的是對它的靈活應用。在乙個思路清晰而且高效的程式中,必然有狀態機的身影浮現。

比如說乙個按鍵命令解析程式,就可以被看做狀態機:本來在a狀態下,觸發乙個按鍵後切換到了b狀態,再觸發另乙個鍵後切換到c狀態,或者返回到a狀態。這就是最簡單的按鍵狀態機例子。實際的按鍵解析程式會比這更複雜些,但這不影響我們對狀態機的認識。

進一步看,擊鍵動作本身也可以看做乙個狀態機。乙個細小的擊鍵動作包含了:釋放、抖動、閉合、抖動和重新釋放等狀態。

同樣,乙個序列通訊的時序(不管它是遵循何種協議,標準串列埠也好、i2c也好;也不管它是有線的、還是紅外的、無線的)也都可以看做由一系列有限的狀態構成。

顯示掃瞄程式也是狀態機;通訊命令解析程式也是狀態機;甚至連繼電器的吸合/釋放控制、發光管(led)的亮/滅控制又何嘗不是個狀態機。

當我們開啟思路,把狀態機作為一種思想匯入到程式中去時,就會找到解決問題的一條有效的捷徑。有時候用狀態機的思維去思考程式該幹什麼,比用控制流程的思維去思考,可能會更有效。這樣一來狀態機便有了更實際的功用。

程式其實就是狀態機。

也許你還不理解上面這句話。請想想看,計算機的大廈不就是建立在「0」和「1」兩個基本狀態的地基之上麼?

狀態機的要素

狀態機可歸納為4個要素,即現態、條件、動作、次態。這樣的歸納,主要是出於對狀態機的內在因果關係的考慮。「現態」和「條件」是因,「動作」和「次態」是果。詳解如下:

①現態:是指當前所處的狀態。

②條件:又稱為「事件」。當乙個條件被滿足,將會觸發乙個動作,或者執行一次狀態的遷移。

③動作:條件滿足後執行的動作。動作執行完畢後,可以遷移到新的狀態,也可以仍舊保持原狀態。動作不是必需的,當條件滿足後,也可以不執行任何動作,直接遷移到新狀態。

④次態:條件滿足後要遷往的新狀態。「次態」是相對於「現態」而言的,「次態」一旦被啟用,就轉變成新的「現態」了。

如果我們進一步歸納,把「現態」和「次態」統一起來,而把「動作」忽略(降格處理),則只剩下兩個最關鍵的要素,即:狀態、遷移條件。

狀態機的表示方法有許多種,我們可以用文字、圖形或**的形式來表示乙個狀態機。

純粹用文字描述是很低效的,所以就不介紹了。接下來先介紹圖形的方式。

狀態遷移圖(std)

狀態遷移圖(std),是一種描述系統的狀態、以及相互轉化關係的圖形方式。狀態遷移圖的畫法有許多種,不過一般都大同小異。我們結合乙個例子來說明一下它的畫法,如圖1所示。

圖1 狀態遷移圖

①狀態框:用方框表示狀態,包括所謂的「現態」和「次態」。

②條件及遷移箭頭:用箭頭表示狀態遷移的方向,並在該箭頭上標註觸發條件。

③節點圓圈:當多個箭頭指向乙個狀態時,可以用節點符號(小圓圈)連線彙總。

④動作框:用橢圓框表示。

⑤附加條件判斷框:用六角菱形框表示。

狀態遷移圖和我們常見的流程圖相比有著本質的區別,具體體現為:在流程圖中,箭頭代表了程式pc指標的跳轉;而在狀態遷移圖中,箭頭代表的是狀態的改變。

我們會發現,這種狀態遷移圖比普通程式流程圖更簡練、直觀、易懂。這正是我們需要達到的目的。  

狀態遷移表

除了狀態遷移圖,我們還可以用**的形式來表示狀態之間的關係。這種表一般稱為狀態遷移表。

表1就是前面介紹的那張狀態遷移圖的另一種描述形式。

表1 狀態遷移表

①採用**方式來描述狀態機,優點是可容納更多的文字資訊。例如,我們不但可以在狀態遷移表中描述狀態的遷移關係,還可以把每個狀態的特徵描述也包含在內。

②如果**內容較多,過於臃腫不利於閱讀,我們也可以將狀態遷移表進行拆分。經過拆分後的**根據其具體內容,**名稱也有所變化。

③比如,我們可以把狀態特徵和遷移關係分開列表。被單獨拆分出來的描述狀態特徵的**,也可以稱為「狀態真值表」。這其中比較常見的就是把每個狀態的顯示內容單獨列表。這種描述每個狀態顯示內容的表稱之為「顯示真值表」。同樣,我們把單獨表述基於按鍵的狀態遷移表稱為「按鍵功能真值表」。另外,如果每乙個狀態包含的資訊量過多,我們也可以把每個狀態單獨列表。

④由此可見,狀態遷移表作為狀態遷移圖的有益補充,它的表現形式是靈活的。

⑤狀態遷移表優點是資訊涵蓋面大,缺點是視覺上不夠直觀,因此它並不能取代狀態遷移圖。比較理想的是將圖形和**結合應用。用圖形展現巨集觀,用**說明細節。二者互為參照,相得益彰。

用狀態機思路實現乙個時鐘程式

接下來,我將就狀態機的應用,結合流程圖、狀態遷移圖和狀態遷移,舉乙個實際例子。下面這張圖是乙個時鐘程式的狀態遷移圖,如圖2所示。

圖2 時鐘程式狀態遷移圖

把這張圖稍做歸納,就可以得到它的另一種表現形式——狀態遷移表,如表2所示。

表2 時鐘程式狀態遷移表

狀態機應用的注意事項

基於狀態機的程式排程機制,其應用的難點並不在於對狀態機概念的理解,而在於對系統工作狀態的合理劃分。

初學者往往會把某個「程式動作」當作是一種「狀態」來處理,我稱之為「偽態」。那麼如何區分「動作」和「狀態」?本匠人的心得是看二者的本質:「動作」是不穩定的,即使沒有條件的觸發,「動作」一旦執行完畢就結束了;而「狀態」是相對穩定的,如果沒有外部條件的觸發,乙個狀態會一直持續下去。

初學者的另一種比較致命的錯誤,就是在狀態劃分時漏掉一些狀態,我稱之為「漏態」。

「偽態」和「漏態」這兩種錯誤的存在,將會導致程式結構的渙散。因此要特別小心避免。  

更複雜的狀態機

前面介紹的是一種簡單的狀態結構。它只有一級,並且只有一維,如圖3所示。

圖3  線性狀態機結構

如果有必要,我們可以建立更複雜的狀態機模型。

1. 多級狀態結構

狀態機可以是多級的。在分層的多級狀態機系統裡面,乙個「父狀態」下可以劃分多個「子狀態」,這些子狀態共同擁有上級父狀態的某些共性,同時又各自擁有自己的一些個性。

在某些狀態下,還可以進一步劃分子狀態。比如,我們可以把前面的時鐘例子修改如下:

把所有和時鐘功能有關的狀態,合併成1個一級狀態。在這個狀態下,又可以劃分出3個二級子狀態,分別為顯示時間、設定小時、設定分鐘;

同樣,我們也可以把所有和鬧鐘功能有關的狀態,合併成1個一級狀態。在這個狀態下,再劃分出4個二級子狀態,分別為顯示鬧鐘、設定「時」、設定「分」、設定鳴叫時間。

我們需要用另乙個狀態變數(暫存器)來表示這些子狀態。

子狀態下面當然還可以有更低一級的孫狀態(子子孫孫無窮盡也),從而將整個狀態體系變成了樹狀多級狀態結構,如圖4所示。

圖4 樹狀多級狀態結構

2. 多維狀態結構

狀態結構也可以是多維的。從不同的角度對系統進行狀態的劃分,這些狀態的某些特性是交叉的。比如,在按照按鍵和顯示劃分狀態的同時,又按照系統的工作程序做出另一種狀態劃分。這兩種狀態劃分同時存在,相互交叉,從而構成了二維的狀態結構空間。

舉乙個這方面的例子,如:空調遙控器,如圖5所示。

圖5 多維狀態機結構

同樣,我們也可以構建三維、四維甚至更多維的狀態結構。每一維的狀態都需要用乙個狀態變數(暫存器)來表示。

無論多級狀態結構和多維狀態結構看上去多麼迷人,匠人的忠告是:我們依然要盡可能地簡化狀態結構,能用單級、單維的結構,就不要給自己找事,去玩那噩夢般的複雜結構。

簡單的才是最有效的。

結束語

對狀態機的理解需要乙個由淺入深的過程。這個過程應該是與實踐應用和具體案例思考相結合的。當一種良好的思路成為設計的習慣,它就能給設計者帶來回報。願這篇手記裡介紹的基於狀態機的程式設計思路能給新手們帶來一些啟迪,幫助大家找到「程式設計」的感覺。

狀態機思路在程式設計中的應用

狀態機思路在微控制器程式設計中的應用 狀態機的概念 狀態機是軟體程式設計中的乙個重要概念。比這個概念更重要的是對它的靈活應用。在乙個思路清晰而且高效的程式中,必然有狀態機的身影浮現。比如說乙個按鍵命令解析程式,就可以被看做狀態機 本來在a狀態下,觸發乙個按鍵後切換到了b狀態 再觸發另乙個鍵後切換到c...

狀態機思路在程式設計中的應用

狀態機思路在微控制器程式設計中的應用 狀態機的概念 狀態機是軟體程式設計中的乙個重要概念。比這個概念更重要的是對它的靈活應用。在乙個思路清晰而且高效的程式中,必然有狀態機的身影浮現。比如說乙個按鍵命令解析程式,就可以被看做狀態機 本來在a狀態下,觸發乙個按鍵後切換到了b狀態 再觸發另乙個鍵後切換到c...

狀態機思路在程式設計中的應用

狀態機思路在微控制器程式設計中的應用 狀態機的概念 狀態機是軟體程式設計中的乙個重要概念。比這個概念更重要的是對它的靈活應用。在乙個思路清晰而且高效的程式中,必然有狀態機的身影浮現。比如說乙個按鍵命令解析程式,就可以被看做狀態機 本來在a狀態下,觸發乙個按鍵後切換到了b狀態 再觸發另乙個鍵後切換到c...