模式動(dòng)機(jī)
狀態(tài)模式(狀態(tài)機(jī))是嵌入式開(kāi)發(fā)中最重要、最核心的設(shè)計(jì)模式之一,毫不夸張的說(shuō),是否熟練掌握狀態(tài)模式,很大程度上直接決定了嵌入式工程師的代碼掌控能力。在嵌入式開(kāi)發(fā)里面,幾乎80%以上的程序都有狀態(tài)模式(狀態(tài)機(jī))的影子。在一個(gè)思路清晰而且高效的程序中,必然有狀態(tài)模式(狀態(tài)機(jī))身影浮現(xiàn)。但是很多嵌入式開(kāi)發(fā)者只是掌握一些很基礎(chǔ)的狀態(tài)機(jī)編程,對(duì)狀態(tài)機(jī)編程如果提高程序的可維護(hù)性和可拓展性并沒(méi)有一個(gè)深刻的理解。
這里我通過(guò)一個(gè)簡(jiǎn)單易懂的MP3播放器案例,把自己獨(dú)家總結(jié)的狀態(tài)機(jī)六步法分享給大家,幫助大家在啃下?tīng)顟B(tài)機(jī)這塊硬骨頭。相信你深度掌握狀態(tài)機(jī)編程以后,你優(yōu)雅美觀的代碼會(huì)讓同事朋友們眼前一亮,嘖嘖稱贊。
生活中的狀態(tài)模式(狀態(tài)機(jī))
幾乎在所有的復(fù)雜項(xiàng)目里面,都充斥著各種事物狀態(tài)的變化。這是因?yàn)槲覀兩硖幍奈锢硎澜绫緛?lái)就是一個(gè)動(dòng)態(tài)多變的環(huán)境,自然我們開(kāi)發(fā)的程序也要根據(jù)事物不同時(shí)刻不同場(chǎng)景的狀態(tài),不斷調(diào)整自身的行為屬性。
比如電影《分裂》里面,詹姆斯·麥卡沃伊飾演的男主Kevin患有精神分裂,有著多重人格疾病,他被精神病醫(yī)生Dr. Fletcher診斷出有23重人格,可以隨時(shí)間或境遇切換,一會(huì)變成精明聰穎的律師,一會(huì)是懦弱的失敗者總是要自殺,一個(gè)境遇觸發(fā)又是憤怒的殺人暴徒,這人格切換速度,喪心病狂到令人發(fā)指。
想象一下,假如我們要在程序中實(shí)現(xiàn)這樣一個(gè)角色,就必須要有一個(gè)良好的狀態(tài)變化設(shè)計(jì),才能保證主人公在快速切換狀態(tài)的情況下,都能擁有與之匹配的精神狀態(tài)和行為舉止。
場(chǎng)景案例
場(chǎng)景:設(shè)計(jì)一個(gè)簡(jiǎn)單的MP3播放器,要求兩個(gè)按鍵(播放/暫停、停止)分別控制MP3的播放/停止功能。
如下表所示:
狀態(tài)遷移圖
在狀態(tài)模式的設(shè)計(jì)開(kāi)發(fā)中,我們通常借助狀態(tài)遷移圖來(lái)進(jìn)行多個(gè)狀態(tài)的分析。本案例中的MP3播放器,狀態(tài)遷移圖如下圖所示:
雖然圖示很簡(jiǎn)單,但是非常有用,因?yàn)楦靼存I按下后,MP3播放器的狀態(tài)變化一目了然,根據(jù)狀態(tài)遷移圖,我們就可以著手程序的編寫(xiě)了。
我們先來(lái)看一個(gè)狀態(tài)模式(狀態(tài)機(jī))的入門(mén)級(jí)別的實(shí)現(xiàn)--簡(jiǎn)單狀態(tài)機(jī)。其實(shí)就是通過(guò)大量的switch/case和if/else,在很多項(xiàng)目中經(jīng)??梢钥吹筋愃频拇a:
#include < stdio.h >
void stopPlayer();
void pausePlayer();
void resumePlayer();
void startPlayer();
//按鍵的動(dòng)作類型
typedef enum {
EV_STOP,
EV_PLAY_PAUSE
}EventCode;
//MP3的狀態(tài)
enum{
ST_IDLE,
ST_PLAY,
ST_PAUSE
};
//MP3當(dāng)前狀態(tài)
char state;
//MP3狀態(tài)初始化
void init()
{
state = ST_IDLE;
}
//狀態(tài)機(jī)處理MP3的過(guò)程變化
void onEvent(EventCode ec)
{
switch (state)
{
case ST_IDLE:
if(EV_PLAY_PAUSE == ec)
startPlayer();
break;
case ST_PLAY:
if(EV_STOP == ec)
stopPlayer();
else if(EV_PLAY_PAUSE == ec)
pausePlayer();
break;
case ST_PAUSE:
if(EV_STOP == ec)
stopPlayer();
else if(EV_PLAY_PAUSE == ec)
resumePlayer();
break;
default:
break;
}
}
void stopPlayer()
{
state = ST_IDLE;
printf("停止播放音樂(lè)\\n");
}
void pausePlayer()
{
state = ST_PAUSE;
printf("暫停播放音樂(lè)\\n");
}
void resumePlayer()
{
state = ST_PLAY;
printf("恢復(fù)播放音樂(lè)\\n");
}
void startPlayer()
{
state = ST_PLAY;
printf("開(kāi)始播放音樂(lè)\\n");
}
//主程序?qū)崿F(xiàn)MP3的播放控制
void main()
{
init();
onEvent(EV_PLAY_PAUSE);//播放
onEvent(EV_PLAY_PAUSE);//暫停
onEvent(EV_PLAY_PAUSE);//繼續(xù)播放
onEvent(EV_STOP); //停止
}