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