精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

c語言設計模式--狀態模式(狀態機)

冬至子 ? 來源:embed linux share ? 作者:亞索老哥 ? 2023-06-14 15:28 ? 次閱讀

模式動機

狀態模式(狀態機)是嵌入式開發中最重要、最核心的設計模式之一,毫不夸張的說,是否熟練掌握狀態模式,很大程度上直接決定了嵌入式工程師的代碼掌控能力。在嵌入式開發里面,幾乎80%以上的程序都有狀態模式(狀態機)的影子。在一個思路清晰而且高效的程序中,必然有狀態模式(狀態機)身影浮現。但是很多嵌入式開發者只是掌握一些很基礎的狀態機編程,對狀態機編程如果提高程序的可維護性和可拓展性并沒有一個深刻的理解。

這里我通過一個簡單易懂的MP3播放器案例,把自己獨家總結的狀態機六步法分享給大家,幫助大家在啃下狀態機這塊硬骨頭。相信你深度掌握狀態機編程以后,你優雅美觀的代碼會讓同事朋友們眼前一亮,嘖嘖稱贊。

生活中的狀態模式(狀態機)

幾乎在所有的復雜項目里面,都充斥著各種事物狀態的變化。這是因為我們身處的物理世界本來就是一個動態多變的環境,自然我們開發的程序也要根據事物不同時刻不同場景的狀態,不斷調整自身的行為屬性。

比如電影《分裂》里面,詹姆斯·麥卡沃伊飾演的男主Kevin患有精神分裂,有著多重人格疾病,他被精神病醫生Dr. Fletcher診斷出有23重人格,可以隨時間或境遇切換,一會變成精明聰穎的律師,一會是懦弱的失敗者總是要自殺,一個境遇觸發又是憤怒的殺人暴徒,這人格切換速度,喪心病狂到令人發指。

想象一下,假如我們要在程序中實現這樣一個角色,就必須要有一個良好的狀態變化設計,才能保證主人公在快速切換狀態的情況下,都能擁有與之匹配的精神狀態和行為舉止。

場景案例

場景:設計一個簡單的MP3播放器,要求兩個按鍵(播放/暫停、停止)分別控制MP3的播放/停止功能。

如下表所示:

1.jpg

狀態遷移圖

在狀態模式的設計開發中,我們通常借助狀態遷移圖來進行多個狀態的分析。本案例中的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);      //停止
}

代碼已經在c在線工具|菜鳥工具中運行驗證,讀者也可以自行驗證。運行結果如下:

開始播放音樂
暫停播放音樂
恢復播放音樂
停止播放音樂

在上面的代碼實現中,主要是在onEvent函數中,以MP3的當前狀態作為判斷條件進行相應的分支改動,簡單地按照狀態遷移圖,實現了功能。

但是我們觀察onEvent函數,不難發現其中有大量的swith...case這樣的判斷(if...else也是一樣).對于MP3播放器這樣簡單的例子,這樣的代碼還是不難閱讀和維護的。但是當狀態和事件增加后,onEvent函數就會變得非常龐大,這是因為該函數的代碼行數與狀態和事件數量的乘積成正比,直接導致代碼行數爆炸增長,代碼會越發變得難以閱讀和維護。

其次,程序的擴展性非常差,無論是我們新增一種狀態,還是新增一種按鍵動作,onEvent函數都要大改特改,極難保障程序的穩定性。

解決方案

核心思路 :我們可以利用C語言的多態特性來分解復雜的條件分支。這樣一來可以就避免大量的swith...case和 if...else等條件分支語句,提高程序的可維護性和可擴展性。

下面我將使用獨家總結的六步法,幫助大家輕松掌握狀態模式(狀態機)的編程訣竅。

#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();//停止
}

代碼已經在c在線工具|菜鳥工具中運行驗證,讀者也可以自行驗證。運行結果如下:

開始播放音樂
暫停播放音樂
恢復播放音樂
停止播放音樂

對比前后兩份代碼,六步法實現的狀態機比簡單狀態機明顯有以下幾方面的優點:

  • 代碼結構要更加清晰,避免了過多的switch...case或者if...else語句 的使用。
  • 很好地體現了開閉原則和單一職責原則,每個狀態都是一個子結構體,你要增加狀態就要增加子結構體,你要修改狀態,你只修改一個子結構體就可以了。
  • 封裝性非常好,狀態變換放置到子結構體的內部來實現,外部的調用不用知道子結構體的內部如何實現狀態和行為的變換。

最后跟大家總計一下狀態機六步法:

(1)、定義狀態接口。

(2)、定義系統當前狀態指針。

(3)、定義具體狀態,根據狀態遷移圖來實現具體功能和狀態切換。

(4)、定義主程序上下文操作接口。

(5)、初始化系統當前狀態指針。

(6)、主程序通過上下文操作接口來控制系統當前狀態的變化。

一般來說,熟練使用狀態機六步法的嵌入式開發者,大都是兩年軟件開發經驗以上的老鳥了。所以,如果你還是個嵌入式新手,請在實際開發中多多運用它,以后你的代碼才能越來越優雅美觀。而且掌握狀態機編程對理解其他更復雜的設計模式也是大有裨益的。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 嵌入式系統
    +關注

    關注

    41

    文章

    3570

    瀏覽量

    129253
  • C語言
    +關注

    關注

    180

    文章

    7601

    瀏覽量

    136251
  • 狀態機
    +關注

    關注

    2

    文章

    492

    瀏覽量

    27486
收藏 人收藏

    評論

    相關推薦

    C語言實現狀態機設計模式

    狀態機模式是一種行為模式,在《設計模式》這本書中對其有詳細的描述,通過多態實現不同狀態的調轉行為的確是一種很好的方法,只可惜在嵌入式環境下,
    發表于 12-14 13:38 ?2803次閱讀

    狀態機編程實例-面向對象的狀態設計模式

    本編介紹了狀態機編程的第3種方法——面向對象的狀態設計模式,通過C++的繼承特性,以及類指針,實現炸彈拆除小游戲中的狀態機功能。
    的頭像 發表于 06-28 09:04 ?1424次閱讀
    <b class='flag-5'>狀態機</b>編程實例-面向對象的<b class='flag-5'>狀態</b>設計<b class='flag-5'>模式</b>

    基于C語言狀態機實現方案

    關于狀態機,基礎的知識點可以自行理解。本文主要講解的是一個有限狀態機FSM通用的寫法,目的在于更好理解,移植,節省代碼閱讀與調試時間,體現出編程之美。
    發表于 09-13 09:28 ?790次閱讀
    基于<b class='flag-5'>C</b><b class='flag-5'>語言</b>的<b class='flag-5'>狀態機</b>實現方案

    Spring狀態機的實現原理和使用方法

    說起 Spring 狀態機,大家很容易聯想到這個狀態機和設計模式狀態模式的區別是啥呢?沒錯,Spring
    的頭像 發表于 12-26 09:39 ?1879次閱讀
    Spring<b class='flag-5'>狀態機</b>的實現原理和使用方法

    玩轉Spring狀態機

    說起Spring狀態機,大家很容易聯想到這個狀態機和設計模式狀態模式的區別是啥呢?沒錯,Spring
    的頭像 發表于 06-25 14:21 ?889次閱讀
    玩轉Spring<b class='flag-5'>狀態機</b>

    【設計技巧】LabVIEW程序設計模式(二)—基本狀態機模式

    時需要使得三個狀態按照B→A→C的順序執行,當單擊B3時需要使得三個狀態按照C→A→B的順序執行。這種情況是無法使用基本狀態機
    發表于 08-06 08:30

    狀態機的相關資料下載

    以前寫狀態機,比較常用的方式是用 if-else 或 switch-case,高級的一點是函數指針列表。最近,看了一文章《c語言設計模式狀態
    發表于 02-15 06:01

    狀態機原理及用法

    狀態機原理及用法狀態機原理及用法狀態機原理及用法
    發表于 03-15 15:25 ?0次下載

    CAN控制器狀態機的分析與實現

    CAN 狀態機包含:總線脫離、總線啟動、總線空閑、模式選擇、發送模式、接收模式、錯誤模式、間歇模式
    發表于 03-22 16:03 ?12次下載

    狀態機概述 如何理解狀態機

    本篇文章包括狀態機的基本概述以及通過簡單的實例理解狀態機
    的頭像 發表于 01-02 18:03 ?1w次閱讀
    <b class='flag-5'>狀態機</b>概述  如何理解<b class='flag-5'>狀態機</b>

    FPGA:狀態機簡述

    本文目錄 前言 狀態機簡介 狀態機分類 Mealy 型狀態機 Moore 型狀態機 狀態機描述 一段式
    的頭像 發表于 11-05 17:58 ?7316次閱讀
    FPGA:<b class='flag-5'>狀態機</b>簡述

    單片C語言 -- 基于條件選擇的狀態機編程技巧

    單片C語言 -- 基于條件選擇的狀態機編程技巧
    發表于 11-23 17:51 ?16次下載
    單片<b class='flag-5'>機</b><b class='flag-5'>C</b><b class='flag-5'>語言</b> -- 基于條件選擇的<b class='flag-5'>狀態機</b>編程技巧

    狀態模式(狀態機)

    以前寫狀態機,比較常用的方式是用 if-else 或 switch-case,高級的一點是函數指針列表。最近,看了一文章《c語言設計模式狀態
    發表于 12-16 16:53 ?9次下載
    <b class='flag-5'>狀態</b><b class='flag-5'>模式</b>(<b class='flag-5'>狀態機</b>)

    C語言狀態機編程思想

    關注、星標公眾號,直達精彩內容文章來源:頭條-嵌入式在左C語言在右鏈接:https://www.toutiao.com/i6843028812112855564/有限狀態機概念有限狀態機
    發表于 01-13 13:32 ?15次下載
    <b class='flag-5'>C</b><b class='flag-5'>語言狀態機</b>編程思想

    什么是狀態機狀態機的種類與實現

    狀態機,又稱有限狀態機(Finite State Machine,FSM)或米利狀態機(Mealy Machine),是一種描述系統狀態變化的模型。在芯片設計中,
    的頭像 發表于 10-19 10:27 ?9054次閱讀