括號里面是本文的副標(biāo)題,跟著作者本來是想搞明白QP是如何實現(xiàn)有限狀態(tài)機的,結(jié)果萬萬沒想到,其實作者的目的不純,他不光想讓搞明白QP的前世,還想讓你的內(nèi)功心法(C語言)在實戰(zhàn)中進階(好文不多,建議大家讀一下原著,或許才能體會到作者的良苦用心,大部分書籍只是教你招式,讓你感悟心法的并不多,感謝作者)。
下面還是由一個書中提到的定時炸彈的例子開啟我們的侃大山模式,話說一個定時炸彈都有哪些功能,你能想到的:
1.他得能倒計時,時間到,一聲 bomb~?。?!
2.要倒計時 ,得有個定時時間 ,timer。
3.有了定時時間,得支持定時功能吧 ,得有UP/DOWM按鍵進行timer調(diào)整。
4.有了定時模式,你得切換到倒計時模式吧,這里有ARM按鍵,按下開啟倒計時。
5.我還想讓這個炸彈有意思一點,給人一定的機會,提供一個炸彈解除密碼吧(友情提示,剪線直接炸給你看~?。?/span>
6.設(shè)置一個初始密碼,你可以通過上下按鍵進行移位輸入密碼,輸入完成按ARM鍵,這個時候如果輸入正確,那么炸彈解除倒計時,進入定時模式(危險解除),如果輸入錯誤,那么也不會立即爆炸 ,可以重新嘗試,畢竟猜對密碼難嘛。
7.有了這一些內(nèi)容,你還需要以一塊顯示屏,管理倒計時和輸入密碼的顯示,基本就這些了,炸彈的原型就出來了:
如何構(gòu)建狀態(tài)機,一千個人有一千種狀態(tài)機設(shè)計思路(可能),并沒有標(biāo)準答案,我總結(jié)了一個通用的黃金規(guī)則,簡單的情況可以copy規(guī)則,直接生成狀態(tài)機,復(fù)雜的情況還是需要認真的思考,不斷地改進模型,打磨出更完美的設(shè)計,如下:
1. 一個項目中需要創(chuàng)建多個狀態(tài)機,狀態(tài)機的本質(zhì)是管理資源,什么是資源,內(nèi)存 IO 鍵盤、顯示器等等,也可以是虛擬的資源,例如狀態(tài)機擴展變量,當(dāng)然一個狀態(tài)機不一定只能管理一種資源,但當(dāng)你不知道該怎么組合的時候,那么就一個狀態(tài)機,一個資源,狀態(tài)機會多一些,但沒什么問題。
2.一個狀態(tài)機會在多種狀態(tài)之間進行切換,那么如何定義到底有幾個狀態(tài),很簡單,這取決于對事件的反應(yīng)不同,有幾種不同的動作就構(gòu)造幾個狀態(tài),相同的動作就放在一個父狀態(tài)里(HSM中)。
這個狀態(tài)機很簡單,只需要創(chuàng)建一個狀態(tài)機(bomb),兩個狀態(tài)setting和timming狀態(tài),再加一個偽狀態(tài)(炸了狀態(tài))就實現(xiàn)了,狀態(tài)圖如下:
基于這個項目,我們提供三個版本的實現(xiàn)讓我們的C徹底進階:
初級版:
1. 先定義狀態(tài)機主體和狀態(tài)定義:
2. 定義事件主體和事件定義:
3.一個狀態(tài)機還需要三個函數(shù)來幫助他實現(xiàn)自身的功能,構(gòu)造函數(shù),初始化函數(shù)和事件分發(fā)處理函數(shù),聲明和定義如下:
4.接下來我們來寫一下main.c的核心部分,如下:
到這里整個方案的代碼的核心部分就全了,這里你可以思考一下這么寫的優(yōu)點和缺點,想想如何改進,想10秒再往下看(答案固然重要,獨立思考更重要),倒計時:
10
想一想優(yōu)點
9
想一想缺點,別急
8
假如是你,你該如何改進它
7
好了,揭曉我的答案(不一定準確)
654321~!
總結(jié),先說說這么寫的優(yōu)點:
1.符合我們對于事物的思考方式,你可以很快掌握他是符合運行,沒有太大的難度。
2.他并沒有用太多的復(fù)雜的設(shè)計方法,例如使用指針,復(fù)雜的類型變化,算法優(yōu)化,不需要工程師太深的基礎(chǔ)就能掌握。
3.基于他的設(shè)計方式,你可以很快上手,來改進自己的項目,讓他看起來更加簡潔明了。
接下來我們來說說他的缺點:
1.首先我們看到void Bomb1_dispatch(Bomb1 *me, Event const *e)這個函數(shù)的實現(xiàn)有點長,我們知道軟件的設(shè)計的規(guī)則中,有一條讓你寫的函數(shù)盡可能的短小,簡單明了,這里只有兩個狀態(tài),三個事件就已經(jīng)占了這么大的篇幅了,再加幾個還得了~!
2.第二點,這并不是一個很好的構(gòu)架,因為不論以后你怎么擴展這個狀態(tài)機,你的主戰(zhàn)場都是盯著這個dispatch函數(shù),哪怕你可以把處理封裝成函數(shù),讓它調(diào)用,但是它就好像是樹根一樣,每長一片葉子,這個樹干都要做一些處理,這并不是一個很好的構(gòu)架,從事多年程序設(shè)計的人員應(yīng)該深有體會,慢慢的他會隨著需求變得不再簡單明了,遠離我們的初衷。
片花:我們的初衷,dispatch不應(yīng)該是這樣,他只是需要傳入一個狀態(tài)機和事件,然后把控制權(quán)轉(zhuǎn)交給這個狀態(tài)機,等待處理完以后,控制權(quán)再回到dispatch函數(shù)中,這里可能會涉及一個課題,如何把狀態(tài)機從dispatch中剝離出來,所謂的剝離并不是完全獨立,而是讓所有的狀態(tài)機可以擁有一個dispatch,而不需要植入任何和自己相關(guān)的狀態(tài)機代碼,思想上升,如何設(shè)計一個通用的dispatch(事件處理器,簡稱QEP,先引出核心課題),下一篇我們開啟進階之路2。