筆者在職場(chǎng)工作多年,維護(hù)過(guò)屎山級(jí)別的項(xiàng)目代碼,也參與過(guò)大大小小的軟件開(kāi)發(fā)。我逐漸明白了寫(xiě)代碼最重要的并不是炫技,而是讓其他維護(hù)這個(gè)項(xiàng)目的人能夠更快的上手去拓展項(xiàng)目的功能,以便能夠更好的傳承下去。
在實(shí)際的嵌入式應(yīng)用開(kāi)發(fā)過(guò)程中,我們常常能夠聽(tīng)到軟件系統(tǒng)的分層設(shè)計(jì),根據(jù)不同的產(chǎn)品軟件設(shè)計(jì),它們可能擁有應(yīng)用層、系統(tǒng)層、驅(qū)動(dòng)層等等。在我看來(lái),這些層的描述太大,但是它不得不存在;因?yàn)樗軌驈暮暧^上讓每個(gè)了解它的人知道它到底有什么東西,大部分情況下給人感覺(jué)是:哇,好牛逼!?然后,就沒(méi)有然后了。
實(shí)際上,真正能夠體現(xiàn)設(shè)計(jì)的牛逼并不是簡(jiǎn)單的描述幾個(gè)層就可以了,真正的設(shè)計(jì),它至少擁有兩個(gè)面的維度,即數(shù)據(jù)平面、控制平面。當(dāng)然,如果我們分得更細(xì)一點(diǎn),它可以是數(shù)據(jù)平面、控制平面和管理平面。至于怎么定義每個(gè)平面的作用,早在1979年,就已經(jīng)有一位叫做Trygve Reenskaug的大佬為我們?cè)O(shè)計(jì)好了所謂的MVC框架。直到如今,MVC框架廣泛應(yīng)用于現(xiàn)代應(yīng)用軟件設(shè)計(jì)中,也是嵌入式應(yīng)用軟件設(shè)計(jì)中最常用的設(shè)計(jì)模式之一。那么,什么是MVC框架呢?
1、MVC框架
MVC框架,是軟件系統(tǒng)模塊化設(shè)計(jì)的一種方法,它給軟件系統(tǒng)劃分為三個(gè)大的部分,分別是Model(模型)、View(視圖)、Controller(控制器)。
Model模型
模型就是負(fù)責(zé)具體功能、業(yè)務(wù)邏輯實(shí)現(xiàn)的,它通常是一個(gè)產(chǎn)品內(nèi)部的一些業(yè)務(wù)邏輯組成;例如,接下來(lái)我們要做的一個(gè)項(xiàng)目里有一個(gè)MQ-2傳感器,MQ-2傳感器的氣體檢測(cè)流程可以認(rèn)為是一個(gè)模型。
View視圖
視圖就是負(fù)責(zé)展示、響應(yīng)其它模塊處理結(jié)果的。例如,有一款設(shè)備擁有一個(gè)LCD屏幕,然后上面移植了一個(gè)GUI系統(tǒng),它用于顯示當(dāng)前MQ-2傳感器的數(shù)據(jù),那么這個(gè)GUI系統(tǒng)就是一個(gè)視圖。當(dāng)MQ-2傳感器檢測(cè)到的閾值超出我們所設(shè)定的閾值時(shí),蜂鳴器或者LED報(bào)警了,那么蜂鳴器、LED也可以認(rèn)為是一個(gè)視圖。當(dāng)然,視圖不局限于以上這些內(nèi)容,視圖也可以是IOT前端、也可以是一個(gè)Shell終端,甚至可以是一個(gè)進(jìn)程或者線程。
Controller控制器
控制器就是用來(lái)接收用戶輸入的。通常,一個(gè)設(shè)備上可能有按鍵、觸摸屏、鼠標(biāo)等輸入設(shè)備,那么當(dāng)用戶控制輸入設(shè)備時(shí),根據(jù)產(chǎn)品內(nèi)部的業(yè)務(wù)邏輯,界面可能會(huì)發(fā)生跳轉(zhuǎn)(視圖),用戶看不到的另一面會(huì)啟動(dòng)應(yīng)用業(yè)務(wù)邏輯(模型),然后設(shè)備內(nèi)部的業(yè)務(wù)邏輯處理完畢后,又會(huì)通知界面,例如彈窗或者僅僅是界面上控件數(shù)據(jù)更新(視圖)。
有了MVC架構(gòu)以后,我們可以為我們接下來(lái)的項(xiàng)目做以下軟件模塊的劃分了,先預(yù)告一下,我們要做一個(gè)簡(jiǎn)單的氣體檢測(cè)裝置,它會(huì)延用我們之前分享過(guò)的內(nèi)容進(jìn)一步規(guī)范化:
表驅(qū)動(dòng)+狀態(tài)機(jī)法AD傳感器驅(qū)動(dòng)檢測(cè)框架
讓傳感器數(shù)據(jù)更直觀之LCD曲線顯示
基于小熊派氣體傳感器MQ-2綜合實(shí)踐
Model
傳感器數(shù)據(jù)獲取、傳感器流程檢測(cè)。
View
LCD GUI(或者騰訊云、騰訊連連小程序)顯示,顯示傳感器數(shù)據(jù)、檢測(cè)狀態(tài)等
Controller
開(kāi)發(fā)板上的按鍵、IOT終端下發(fā)命令(或騰訊連連小程序下發(fā)命令)
那么,這三個(gè)模塊怎么來(lái)通信呢?一般情況下,可以有兩種方式,一種是通過(guò)消息傳遞,另外一種是通過(guò)回調(diào)函數(shù)傳遞(類(lèi)似事件回調(diào)觸發(fā)機(jī)制)。很顯然,我們的項(xiàng)目會(huì)考慮上RTOS,那么,通過(guò)消息隊(duì)列來(lái)傳輸數(shù)據(jù)就再好不過(guò)了!這樣的話,我們每個(gè)模塊之間的通信一旦設(shè)計(jì)好了,那么未來(lái)需要做的事情僅僅只需要關(guān)心:
消息發(fā)送端:數(shù)據(jù)怎么傳,要傳什么數(shù)據(jù)
接收消息端:數(shù)據(jù)怎么接,接完之后要做什么
解決了發(fā)送、接收數(shù)據(jù)的問(wèn)題以后,第二步就是要設(shè)計(jì)傳感器的檢測(cè)流程了,它也是我們MVC架構(gòu)中模型部分最重要的內(nèi)容了,它是整個(gè)項(xiàng)目的核心業(yè)務(wù)。對(duì)于傳感器設(shè)備檢測(cè)來(lái)說(shuō),無(wú)非就是幾種分析狀態(tài):
傳感器設(shè)備處于空閑狀態(tài)
傳感器設(shè)備處于校準(zhǔn)狀態(tài)
傳感器設(shè)備處于檢測(cè)中狀態(tài)
傳感器設(shè)備處于獲取檢測(cè)結(jié)果狀態(tài)
從一種狀態(tài)切換到另一種狀態(tài),那么一定是由某個(gè)事件觸發(fā)的,進(jìn)而產(chǎn)生一定的動(dòng)作,然后完成狀態(tài)的遷移,我們將這種稱為狀態(tài)機(jī)。在設(shè)計(jì)模式中,狀態(tài)機(jī)稱為狀態(tài)模式,狀態(tài)模式也是嵌入式軟件應(yīng)用設(shè)計(jì)中最常用的模式。
2、狀態(tài)模式
狀態(tài)模式是設(shè)計(jì)模式中行為型模式的一種,它允許對(duì)象在內(nèi)部狀態(tài)發(fā)生改變時(shí)改變它的行為。對(duì)于小白來(lái)說(shuō),狀態(tài)模式的概念講述是比較抽象的,那么,我可以舉一個(gè)接下來(lái)要做的氣體檢測(cè)儀項(xiàng)目來(lái)加深大家對(duì)狀態(tài)模式的理解,以下是這個(gè)項(xiàng)目的簡(jiǎn)單的狀態(tài)遷移圖:
在狀態(tài)機(jī)的基本概念里,它的基本組成要素主要由現(xiàn)態(tài)、條件、動(dòng)作、次態(tài)。我們能看到,檢測(cè)儀主要有四個(gè)狀態(tài),分別是IDLE、CALI、DETECT、RESULT,每個(gè)狀態(tài)其實(shí)都是由相應(yīng)的條件來(lái)進(jìn)行觸發(fā)產(chǎn)生動(dòng)作進(jìn)而產(chǎn)生下一個(gè)狀態(tài)的。從以上的狀態(tài)遷移圖里,我們能很清晰地將每個(gè)狀態(tài)的遷移過(guò)程描述出來(lái):
1.對(duì)于IDLE來(lái)說(shuō),主要有:
IDLE→START→CALI
解析:當(dāng)檢測(cè)儀狀態(tài)為IDLE時(shí),檢測(cè)儀的操作者需要發(fā)起一個(gè)START事件來(lái)讓檢測(cè)儀的狀態(tài)由IDLE進(jìn)入到CALI狀態(tài)中。這樣的操作場(chǎng)景通常是檢測(cè)儀的操作者按下一個(gè)開(kāi)始檢測(cè)的按鈕,檢測(cè)儀由空閑狀態(tài)轉(zhuǎn)為基準(zhǔn)采集狀態(tài),此時(shí)檢測(cè)儀可能會(huì)開(kāi)啟各類(lèi)傳感器,在此期間,各項(xiàng)傳感器指標(biāo)需要與當(dāng)前環(huán)境進(jìn)行融合,使各項(xiàng)傳感器指標(biāo)處于穩(wěn)定狀態(tài),這樣才有利于后續(xù)檢測(cè)結(jié)果精確。
2.對(duì)于CALI來(lái)說(shuō),主要有:
CALI→NEXT→DETECT CALI→STOP→IDLE
解析:當(dāng)檢測(cè)儀狀態(tài)處于CALI時(shí),如果此時(shí)條件滿足,則需要發(fā)起一個(gè)NEXT事件讓檢測(cè)儀狀態(tài)由CALI進(jìn)入DETECT中。這個(gè)NEXT事件可以是CALI過(guò)程穩(wěn)定后自動(dòng)觸發(fā)的,也可以是由用戶手動(dòng)觸發(fā)的,這個(gè)需要根據(jù)產(chǎn)品需求進(jìn)行定義。如果此時(shí)條件不滿足則維持現(xiàn)態(tài)CALI,當(dāng)檢測(cè)儀的操作者發(fā)起STOP事件時(shí),通常是按下了某個(gè)返回或者退出的按鍵,則此時(shí)檢測(cè)的狀態(tài)由CALI遷移到了IDLE態(tài)。
3.對(duì)于DETECT來(lái)說(shuō),主要有:
DETECT→NEXT→RESULT DETECT→PREV→CALI DETECT→STOP→IDLE
解析:當(dāng)檢測(cè)儀狀態(tài)處于DETECT時(shí),此時(shí)內(nèi)部會(huì)進(jìn)行一系列的動(dòng)作,那么它得有結(jié)果呀,結(jié)果就是由NEXT事件來(lái)產(chǎn)生的,這樣的應(yīng)用場(chǎng)景通常是傳感器各項(xiàng)技術(shù)指標(biāo)整合的檢測(cè)算法運(yùn)算結(jié)果與檢測(cè)儀所設(shè)定的閾值進(jìn)行比較后自行觸發(fā)的,最終將由DETECT狀態(tài)遷移到了RESULT。當(dāng)然,如果當(dāng)前處于DETECT狀態(tài),而DETECT狀態(tài)的過(guò)程存在偏差,此時(shí)檢測(cè)儀操作者可以通過(guò)某個(gè)按鍵,觸發(fā)PERV事件,將DETECT狀態(tài)遷移到上個(gè)狀態(tài)CALI;當(dāng)然,檢測(cè)儀操作者也可以直接選擇停止檢測(cè),他只需要發(fā)起一個(gè)STOP事件(通常是一個(gè)返回按鍵)即可讓檢測(cè)儀的狀態(tài)由DETECT遷移到IDLE狀態(tài)。
4.對(duì)于RESULT來(lái)說(shuō),主要有:
RESULT→PREV→DETECT RESULT→STOP→IDLE
解析:當(dāng)檢測(cè)儀狀態(tài)處于RESULT時(shí),一般情況下檢測(cè)儀要么是通過(guò)聲光報(bào)警,要么是直接在LCD上進(jìn)行展示,要么是都有。檢測(cè)儀的操作者可以繼續(xù)檢測(cè),那么他只需要發(fā)起一個(gè)PREV事件就能讓檢測(cè)儀的狀態(tài)由RESULT遷移到DETECT,設(shè)備又能夠繼續(xù)進(jìn)行檢測(cè)了,這就是所謂的快檢模式。當(dāng)然,檢測(cè)儀操作者也可以發(fā)起一個(gè)STOP事件,讓檢測(cè)儀回到IDLE狀態(tài)。
對(duì)以上狀態(tài)遷移過(guò)程熟悉后,我們第二步要做的事情就是設(shè)計(jì)符合我們業(yè)務(wù)場(chǎng)景的枚舉以及結(jié)構(gòu)體。首先是傳感器的狀態(tài),有IDLE、CALI、DETECT、RESULT,用枚舉來(lái)體現(xiàn)就是:
//傳感器狀態(tài) enum?SensorState_t { ????IDLE?=?0, ????CALI, ????DETECT, ????RESULT };
每種狀態(tài)是由特定的事件進(jìn)行觸發(fā),那么傳感器的事件主要有:
//傳感器事件 enum?SensorEvent_t { ????START?=?0, ????STOP, ????NEXT, };
有了狀態(tài)和事件之后,我們需要使用一個(gè)結(jié)構(gòu)體來(lái)抽象從現(xiàn)態(tài)->事件->次態(tài)這個(gè)過(guò)程,那么我們可以這么來(lái)設(shè)計(jì):
struct?SensorStateItem_t { ????enum?SensorState_t?CurState; ????enum?SensorEvent_t?Event; ????enum?SensorState_t?NextSate; };
因此,我們能夠根據(jù)我們所描述的狀態(tài)規(guī)劃出一張表,而這張表就清晰的描述出現(xiàn)態(tài)->事件->次態(tài)的這幾個(gè)過(guò)程了:
struct?SensorStateItem_t?SensorStateTable[]?=? { ????{IDLE,???START,?CALI},??????//IDLE->START->CALI ????{CALI,???NEXT,??DETECT},????//CALI->NEXT->DETECT ????{CALI,???STOP,??IDLE},??????//CALI->STOP->IDLE ????{DETECT,?NEXT,??RESULT},????//DETECT->NEXT->RESULT ????{DETECT,?PREV,??CALI},??????//DETECT->PREV->CALI ????{DETECT,?STOP,??IDLE},??????//DETECT->STOP->IDLE ????{RESULT,?PREV,??DETECT},????//RESULT->PREV->DETECT ????{RESULT,?STOP,??IDLE},??????//RESULT->STOP->IDLE };
有了業(yè)務(wù)模型以后,接下來(lái)就是編寫(xiě)代碼邏輯了!
[1]https://www.runoob.com/design-pattern/mvc-pattern.html
[2]https://refactoringguru.cn/design-patterns/state
編輯:黃飛
?
評(píng)論
查看更多