以前寫狀態機,比較常用的方式是用 if-else 或 switch-case,高級的一點是函數指針列表。最近,看了一文章《c語言設計模式–狀態模式(狀態機)》(來源:embed linux share,作者:亞索老哥)),原來狀態機還可以這麼簡單地玩~~
亞索老哥提出的狀態機六步法
(1)、定義狀態接口
(2)、定義系統當前狀態指針
(3)、定義具體狀態,根據狀態遷移圖來實現具體功能和狀態切換
(4)、定義主程序上下文操作接口
(6)、主程序通過上下文操作接口來控制系統當前狀態的變化
亞索老哥的狀態機例程
場景:
- 設計一個簡單的MP3播放器,要求兩個按鍵(播放/暫停、停止)分別控制MP3的播放/停止功能
按鍵功能:
- [play/pause] 播放/暫停
- [stop] 停止
狀態遷移圖:
#include <stdio.h>
/***********************************************
1、定義狀態接口,以MP3的狀態接口爲例,每種狀態下都可能發生
兩種按鍵動作。
************************************************/
typedef struct State{
void (* stop)();
void (* palyOrPause)();
}State;
/***********************************************
2、定義系統當前狀態指針,保存系統的當前狀態
************************************************/
State * pCurrentState;
/***********************************************
3、定義具體狀態,根據狀態遷移圖來實現具體功能和狀態切換。
************************************************/
void ignore();
void startPlay();
void stopPlay();
void pausePlay();
void resumePlay();
//空閒狀態時,stop鍵操作無效,play/pause會開始播放音樂
State IDLE = {
ignore,
startPlay
};
//播放狀態時,stop鍵會停止播放音樂,play/pause會暫停播放音樂
State PLAY = {
stopPlay,
pausePlay
};
//暫停狀態時,stop鍵會停止播放音樂,play/pause會恢復播放音樂
State PAUSE = {
stopPlay,
resumePlay
};
void ignore()
{
//空函數,不進行操作
}
void startPlay()
{
//實現具體功能
printf("開始播放音樂\n");
//進入播放狀態
pCurrentState = &PLAY;
}
void stopPlay()
{
//實現具體功能
printf("停止播放音樂\n");
//進入空閒狀態
pCurrentState = &IDLE;
}
void pausePlay()
{
//實現具體功能
printf("暫停播放音樂\n");
//進入暫停狀態
pCurrentState = &PAUSE;
}
void resumePlay()
{
//實現具體功能
printf("恢復播放音樂\n");
//進入播放狀態
pCurrentState = &PLAY;
}
/***********************************************
4、定義主程序上下文操作接口,主程序只關心當前狀態,不關心狀態之間
是怎麼變化的。
************************************************/
void onStop();
void onPlayOrPause();
State context = {
onStop,
onPlayOrPause
};
void onStop(State *pThis)
{
pCurrentState->stop(pThis);
}
void onPlayOrPause(State *pThis)
{
pCurrentState->palyOrPause(pThis);
}
/***********************************************
5、初始化系統當前狀態指針,其實就是指定系統的起始狀態
************************************************/
void init()
{
pCurrentState = &IDLE;
}
/***********************************************
6、主程序通過上下文操作接口來控制系統當前狀態的變化
************************************************/
void main()
{
init();
context.palyOrPause();//播放
context.palyOrPause();//暫停
context.palyOrPause();//播放
context.stop();//停止
}
模擬一個狀態機
TODO
小結
亞索老哥的狀態機六步法,有許多優點,還是借用原話:
- 代碼結構要更加清晰,避免了過多的switch…case或者if…else語句 的使用。
- 很好地體現了開閉原則和單一職責原則,每個狀態都是一個子結構體,你要增加狀態就要增加子結構體,你要修改狀態,你只修改一個子結構體就可以了。
- 封裝性非常好,狀態變換放置到子結構體的內部來實現,外部的調用不用知道子結構體的內部如何實現狀態和行爲的變換。