那曲檬骨新材料有限公司

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

c語言設(shè)計(jì)模式--狀態(tài)模式(狀態(tài)機(jī))

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

模式動(dòng)機(jī)

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

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

生活中的狀態(tài)模式(狀態(tài)機(jī))

幾乎在所有的復(fù)雜項(xiàng)目里面,都充斥著各種事物狀態(tài)的變化。這是因?yàn)槲覀兩硖幍奈锢硎澜绫緛砭褪且粋€(gè)動(dòng)態(tài)多變的環(huán)境,自然我們開發(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è)簡單的MP3播放器,要求兩個(gè)按鍵(播放/暫停、停止)分別控制MP3的播放/停止功能。

如下表所示:

1.jpg

狀態(tài)遷移圖

在狀態(tài)模式的設(shè)計(jì)開發(fā)中,我們通常借助狀態(tài)遷移圖來進(jìn)行多個(gè)狀態(tài)的分析。本案例中的MP3播放器,狀態(tài)遷移圖如下圖所示:

圖片

雖然圖示很簡單,但是非常有用,因?yàn)楦靼存I按下后,MP3播放器的狀態(tài)變化一目了然,根據(jù)狀態(tài)遷移圖,我們就可以著手程序的編寫了。

我們先來看一個(gè)狀態(tài)模式(狀態(tài)機(jī))的入門級(jí)別的實(shí)現(xiàn)--簡單狀態(tài)機(jī)。其實(shí)就是通過大量的switch/case和if/else,在很多項(xiàng)目中經(jīng)常可以看到類似的代碼:

#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的過程變化
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("恢復(fù)播放音樂\\n");
}

void startPlayer()
{
  state = ST_PLAY;
  printf("開始播放音樂\\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);      //停止
}

代碼已經(jīng)在c在線工具|菜鳥工具中運(yùn)行驗(yàn)證,讀者也可以自行驗(yàn)證。運(yùn)行結(jié)果如下:

開始播放音樂
暫停播放音樂
恢復(fù)播放音樂
停止播放音樂

在上面的代碼實(shí)現(xiàn)中,主要是在onEvent函數(shù)中,以MP3的當(dāng)前狀態(tài)作為判斷條件進(jìn)行相應(yīng)的分支改動(dòng),簡單地按照狀態(tài)遷移圖,實(shí)現(xiàn)了功能。

但是我們觀察onEvent函數(shù),不難發(fā)現(xiàn)其中有大量的swith...case這樣的判斷(if...else也是一樣).對(duì)于MP3播放器這樣簡單的例子,這樣的代碼還是不難閱讀和維護(hù)的。但是當(dāng)狀態(tài)和事件增加后,onEvent函數(shù)就會(huì)變得非常龐大,這是因?yàn)樵摵瘮?shù)的代碼行數(shù)與狀態(tài)和事件數(shù)量的乘積成正比,直接導(dǎo)致代碼行數(shù)爆炸增長,代碼會(huì)越發(fā)變得難以閱讀和維護(hù)。

其次,程序的擴(kuò)展性非常差,無論是我們新增一種狀態(tài),還是新增一種按鍵動(dòng)作,onEvent函數(shù)都要大改特改,極難保障程序的穩(wěn)定性。

解決方案

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

下面我將使用獨(dú)家總結(jié)的六步法,幫助大家輕松掌握狀態(tài)模式(狀態(tài)機(jī))的編程訣竅。

#include < stdio.h >

/***********************************************
1、定義狀態(tài)接口,以MP3的狀態(tài)接口為例,每種狀態(tài)下都可能發(fā)生
兩種按鍵動(dòng)作。
************************************************/

typedef struct State{
  void (* stop)();
  void (* palyOrPause)();
}State;


/***********************************************
2、定義系統(tǒng)當(dāng)前狀態(tài)指針,保存系統(tǒng)的當(dāng)前狀態(tài)
************************************************/

State * pCurrentState;


/***********************************************
3、定義具體狀態(tài),根據(jù)狀態(tài)遷移圖來實(shí)現(xiàn)具體功能和狀態(tài)切換。
************************************************/

void ignore();
void startPlay();
void stopPlay();
void pausePlay();
void resumePlay();

//空閑狀態(tài)時(shí),stop鍵操作無效,play/pause會(huì)開始播放音樂
State IDLE = {
  ignore,
  startPlay
};

//播放狀態(tài)時(shí),stop鍵會(huì)停止播放音樂,play/pause會(huì)暫停播放音樂
State PLAY = {
  stopPlay,
  pausePlay
};

//暫停狀態(tài)時(shí),stop鍵會(huì)停止播放音樂,play/pause會(huì)恢復(fù)播放音樂
State PAUSE = {
  stopPlay,
  resumePlay
};

void ignore()
{
  //空函數(shù),不進(jìn)行操作
}

void startPlay()
{
  //實(shí)現(xiàn)具體功能
  printf("開始播放音樂\\n");
  //進(jìn)入播放狀態(tài)
  pCurrentState = &PLAY;
}
void stopPlay()
{
  //實(shí)現(xiàn)具體功能
  printf("停止播放音樂\\n");
  //進(jìn)入空閑狀態(tài)
  pCurrentState = &IDLE;
}

void pausePlay()
{
  //實(shí)現(xiàn)具體功能
  printf("暫停播放音樂\\n");
  //進(jìn)入暫停狀態(tài)
  pCurrentState = &PAUSE;
}

void resumePlay()
{
  //實(shí)現(xiàn)具體功能
  printf("恢復(fù)播放音樂\\n");
  //進(jìn)入播放狀態(tài)
  pCurrentState = &PLAY;
}


/***********************************************
4、定義主程序上下文操作接口,主程序只關(guān)心當(dāng)前狀態(tài),不關(guān)心狀態(tài)之間
是怎么變化的。
************************************************/

void onStop();
void onPlayOrPause();

State context = {
  onStop,
  onPlayOrPause
};

void onStop(State *pThis)
{
  pCurrentState- >stop(pThis);
}

void onPlayOrPause(State *pThis)
{
  pCurrentState- >palyOrPause(pThis);
}


/***********************************************
5、初始化系統(tǒng)當(dāng)前狀態(tài)指針,其實(shí)就是指定系統(tǒng)的起始狀態(tài)
************************************************/

void init()
{
  pCurrentState = &IDLE;
}

/***********************************************
6、主程序通過上下文操作接口來控制系統(tǒng)當(dāng)前狀態(tài)的變化
************************************************/
void main()
{
  init();
  context.palyOrPause();//播放
  context.palyOrPause();//暫停
  context.palyOrPause();//播放
  context.stop();//停止
}

代碼已經(jīng)在c在線工具|菜鳥工具中運(yùn)行驗(yàn)證,讀者也可以自行驗(yàn)證。運(yùn)行結(jié)果如下:

開始播放音樂
暫停播放音樂
恢復(fù)播放音樂
停止播放音樂

對(duì)比前后兩份代碼,六步法實(shí)現(xiàn)的狀態(tài)機(jī)比簡單狀態(tài)機(jī)明顯有以下幾方面的優(yōu)點(diǎn):

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

最后跟大家總計(jì)一下狀態(tài)機(jī)六步法:

(1)、定義狀態(tài)接口。

(2)、定義系統(tǒng)當(dāng)前狀態(tài)指針。

(3)、定義具體狀態(tài),根據(jù)狀態(tài)遷移圖來實(shí)現(xiàn)具體功能和狀態(tài)切換。

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

(5)、初始化系統(tǒng)當(dāng)前狀態(tài)指針。

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

一般來說,熟練使用狀態(tài)機(jī)六步法的嵌入式開發(fā)者,大都是兩年軟件開發(fā)經(jīng)驗(yàn)以上的老鳥了。所以,如果你還是個(gè)嵌入式新手,請(qǐng)?jiān)趯?shí)際開發(fā)中多多運(yùn)用它,以后你的代碼才能越來越優(yōu)雅美觀。而且掌握狀態(tài)機(jī)編程對(duì)理解其他更復(fù)雜的設(shè)計(jì)模式也是大有裨益的。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 嵌入式系統(tǒng)
    +關(guān)注

    關(guān)注

    41

    文章

    3625

    瀏覽量

    129755
  • C語言
    +關(guān)注

    關(guān)注

    180

    文章

    7614

    瀏覽量

    137715
  • 狀態(tài)機(jī)
    +關(guān)注

    關(guān)注

    2

    文章

    492

    瀏覽量

    27648
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    C語言實(shí)現(xiàn)狀態(tài)機(jī)設(shè)計(jì)模式

    狀態(tài)機(jī)模式是一種行為模式,在《設(shè)計(jì)模式》這本書中對(duì)其有詳細(xì)的描述,通過多態(tài)實(shí)現(xiàn)不同狀態(tài)的調(diào)轉(zhuǎn)行為的確是一種很好的方法,只可惜在嵌入式環(huán)境下,
    發(fā)表于 12-14 13:38 ?2865次閱讀

    狀態(tài)機(jī)編程實(shí)例-面向?qū)ο蟮?b class='flag-5'>狀態(tài)設(shè)計(jì)模式

    本編介紹了狀態(tài)機(jī)編程的第3種方法——面向?qū)ο蟮?b class='flag-5'>狀態(tài)設(shè)計(jì)模式,通過C++的繼承特性,以及類指針,實(shí)現(xiàn)炸彈拆除小游戲中的狀態(tài)機(jī)功能。
    的頭像 發(fā)表于 06-28 09:04 ?1602次閱讀
    <b class='flag-5'>狀態(tài)機(jī)</b>編程實(shí)例-面向?qū)ο蟮?b class='flag-5'>狀態(tài)</b>設(shè)計(jì)<b class='flag-5'>模式</b>

    基于C語言狀態(tài)機(jī)實(shí)現(xiàn)方案

    關(guān)于狀態(tài)機(jī),基礎(chǔ)的知識(shí)點(diǎn)可以自行理解。本文主要講解的是一個(gè)有限狀態(tài)機(jī)FSM通用的寫法,目的在于更好理解,移植,節(jié)省代碼閱讀與調(diào)試時(shí)間,體現(xiàn)出編程之美。
    發(fā)表于 09-13 09:28 ?947次閱讀
    基于<b class='flag-5'>C</b><b class='flag-5'>語言</b>的<b class='flag-5'>狀態(tài)機(jī)</b>實(shí)現(xiàn)方案

    Spring狀態(tài)機(jī)的實(shí)現(xiàn)原理和使用方法

    說起 Spring 狀態(tài)機(jī),大家很容易聯(lián)想到這個(gè)狀態(tài)機(jī)和設(shè)計(jì)模式狀態(tài)模式的區(qū)別是啥呢?沒錯(cuò),Spring
    的頭像 發(fā)表于 12-26 09:39 ?2222次閱讀
    Spring<b class='flag-5'>狀態(tài)機(jī)</b>的實(shí)現(xiàn)原理和使用方法

    玩轉(zhuǎn)Spring狀態(tài)機(jī)

    說起Spring狀態(tài)機(jī),大家很容易聯(lián)想到這個(gè)狀態(tài)機(jī)和設(shè)計(jì)模式狀態(tài)模式的區(qū)別是啥呢?沒錯(cuò),Spring
    的頭像 發(fā)表于 06-25 14:21 ?1030次閱讀
    玩轉(zhuǎn)Spring<b class='flag-5'>狀態(tài)機(jī)</b>

    狀態(tài)機(jī)的相關(guān)資料下載

    以前寫狀態(tài)機(jī),比較常用的方式是用 if-else 或 switch-case,高級(jí)的一點(diǎn)是函數(shù)指針列表。最近,看了一文章《c語言設(shè)計(jì)模式狀態(tài)
    發(fā)表于 02-15 06:01

    狀態(tài)機(jī)原理及用法

    狀態(tài)機(jī)原理及用法狀態(tài)機(jī)原理及用法狀態(tài)機(jī)原理及用法
    發(fā)表于 03-15 15:25 ?0次下載

    CAN控制器狀態(tài)機(jī)的分析與實(shí)現(xiàn)

    CAN 狀態(tài)機(jī)包含:總線脫離、總線啟動(dòng)、總線空閑、模式選擇、發(fā)送模式、接收模式、錯(cuò)誤模式、間歇模式
    發(fā)表于 03-22 16:03 ?12次下載

    有限狀態(tài)機(jī)FSM在PLD中的實(shí)現(xiàn)分析

    本文通過舉例 利用VHDL 語言描述了不同模式的有限狀態(tài)機(jī) 分析了有限狀態(tài)機(jī)在 PLD 中綜合的特點(diǎn) 。
    發(fā)表于 03-22 15:41 ?3次下載

    狀態(tài)機(jī)概述 如何理解狀態(tài)機(jī)

    本篇文章包括狀態(tài)機(jī)的基本概述以及通過簡單的實(shí)例理解狀態(tài)機(jī)
    的頭像 發(fā)表于 01-02 18:03 ?1.1w次閱讀
    <b class='flag-5'>狀態(tài)機(jī)</b>概述  如何理解<b class='flag-5'>狀態(tài)機(jī)</b>

    FPGA:狀態(tài)機(jī)簡述

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

    單片機(jī)C語言 -- 基于條件選擇的狀態(tài)機(jī)編程技巧

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

    狀態(tài)模式(狀態(tài)機(jī))

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

    C語言狀態(tài)機(jī)編程思想

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

    什么是狀態(tài)機(jī)狀態(tài)機(jī)的種類與實(shí)現(xiàn)

    狀態(tài)機(jī),又稱有限狀態(tài)機(jī)(Finite State Machine,F(xiàn)SM)或米利狀態(tài)機(jī)(Mealy Machine),是一種描述系統(tǒng)狀態(tài)變化的模型。在芯片設(shè)計(jì)中,
    的頭像 發(fā)表于 10-19 10:27 ?1w次閱讀
    百家乐官网是否违法| 博必发百家乐的玩法技巧和规则 | 太阳城百家乐官网的破解| 百家乐3式打法微笑心法| e世博百家乐官网攻略| 德州扑克小说| 布加迪百家乐官网的玩法技巧和规则| bet365虚拟运动| 大世界百家乐娱乐城| 澳门百家乐官网赢钱秘诀| 大发888足球开户| 墓地附近做生意风水| 百家乐官网发牌千数| 百家乐园| 百家乐官网扑克牌手机壳| 球讯网| 百家乐入庄闲概率| 正品百家乐官网地址| 澳门赌博经历| 百家乐怎样算大小| 易胜博百家乐官网下载| 大发888安装包| 博彩百家乐网址| 百家乐官网中庄闲比例| 速博国际| 潘多拉百家乐的玩法技巧和规则| 汇丰百家乐官网的玩法技巧和规则| 星河国际娱乐场| 威尼斯人娱乐城佣金| 百家乐金币游戏| 澳门百家乐官网心德| 大发888娱乐在线客服| 澳门百家乐庄闲和| 狮威百家乐官网娱乐网| 最新皇冠网址| 新太阳城工业区| 百家乐现金平台排名| 百家乐官网必胜软件下载| 德州扑克高级教程| 娱乐网百家乐的玩法技巧和规则| 最佳场百家乐官网的玩法技巧和规则|