狀態模式(狀態機)

以前寫狀態機,比較常用的方式是用 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語句 的使用。
  • 很好地體現了開閉原則和單一職責原則,每個狀態都是一個子結構體,你要增加狀態就要增加子結構體,你要修改狀態,你只修改一個子結構體就可以了。
  • 封裝性非常好,狀態變換放置到子結構體的內部來實現,外部的調用不用知道子結構體的內部如何實現狀態和行爲的變換。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章