当前位置:首页 > 知识普及 > 正文内容

c语言设计模式--状态模式(状态机)

admin11小时前知识普及2

c语言设计模式--状态模式(状态机)

模式动机

状态模式(状态机)是嵌入式开发中最重要、最核心的设计模式之一,毫不夸张的说,是否熟练掌握状态模式,很大程度上直接决定了嵌入式工程师的代码掌控能力。在嵌入式开发里面,几乎80%以上的程序都有状态模式(状态机)的影子。在一个思路清晰而且高效的程序中c语言状态机设计模式,必然有状态模式(状态机)身影浮现。但是很多嵌入式开发者只是掌握一些很基础的状态机编程,对状态机编程如果提高程序的可维护性和可拓展性并没有一个深刻的理解。

这里我通过一个简单易懂的MP3播放器案例,把自己独家总结的状态机六步法分享给大家,帮助大家在啃下状态机这块硬骨头。相信你深度掌握状态机编程以后,你优雅美观的代码会让同事朋友们眼前一亮,啧啧称赞。

生活中的状态模式(状态机)

几乎在所有的复杂项目里面c语言设计模式--状态模式(状态机),都充斥着各种事物状态的变化。这是因为我们身处的物理世界本来就是一个动态多变的环境,自然我们开发的程序也要根据事物不同时刻不同场景的状态,不断调整自身的行为属性。

比如电影《分裂》里面c语言状态机设计模式,詹姆斯·麦卡沃伊饰演的男主Kevin患有精神分裂,有着多重人格疾病,他被精神病医生Dr. 诊断出有23重人格,可以随时间或境遇切换,一会变成精明聪颖的律师,一会是懦弱的失败者总是要自杀,一个境遇触发又是愤怒的杀人暴徒,这人格切换速度,丧心病狂到令人发指。

想象一下,假如我们要在程序中实现这样一个角色,就必须要有一个良好的状态变化设计,才能保证主人公在快速切换状态的情况下,都能拥有与之匹配的精神状态和行为举止。

场景案例

场景:设计一个简单的MP3播放器,要求两个按键(播放/暂停、停止)分别控制MP3的播放/停止功能。

如下表所示:

按键功能

play/pause

播放/暂停

停止

状态迁移图

c语言设计模式--状态模式(状态机) 第1张

在状态模式的设计开发中c语言设计模式--状态模式(状态机),我们通常借助状态迁移图来进行多个状态的分析。本案例中的MP3播放器,状态迁移图如下图所示:

虽然图示很简单,但是非常有用,因为各按键按下后,MP3播放器的状态变化一目了然,根据状态迁移图,我们就可以着手程序的编写了。

我们先来看一个状态模式(状态机)的入门级别的实现--简单状态机。其实就是通过大量的/case和if/else,在很多项目中经常可以看到类似的代码:

#include 

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在线工具|菜鸟工具中运行验证,读者也可以自行验证。运行结果如下:

开始播放音乐
暂停播放音乐
恢复播放音乐
停止播放音乐

在上面的代码实现中,主要是在函数中,以MP3的当前状态作为判断条件进行相应的分支改动,简单地按照状态迁移图,实现了功能。

但是我们观察函数,不难发现其中有大量的swith...case这样的判断(if...else也是一样).对于MP3播放器这样简单的例子,这样的代码还是不难阅读和维护的。但是当状态和事件增加后,函数就会变得非常庞大,这是因为该函数的代码行数与状态和事件数量的乘积成正比,直接导致代码行数爆炸增长,代码会越发变得难以阅读和维护。

其次,程序的扩展性非常差,无论是我们新增一种状态c语言状态机设计模式,还是新增一种按键动作,函数都要大改特改,极难保障程序的稳定性。

解决方案

核心思路:我们可以利用C语言的多态特性来分解复杂的条件分支(关于c语言多态的实现,请查看)。这样一来可以就避免大量的swith...case和 if...else等条件分支语句,提高程序的可维护性和可扩展性。

下面我将使用独家总结的六步法,帮助大家轻松掌握状态模式(状态机)的编程诀窍。

#include 

/***********************************************
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语言设计模式--状态模式(状态机) 第2张

代码已经在c在线工具|菜鸟工具中运行验证,读者也可以自行验证。运行结果如下:

开始播放音乐
暂停播放音乐
恢复播放音乐
停止播放音乐

对比前后两份代码,六步法实现的状态机比简单状态机明显有以下几方面的优点:

最后跟大家总计一下状态机六步法:

(1)、定义状态接口。

(2)、定义系统当前状态指针。

(3)、定义具体状态,根据状态迁移图来实现具体功能和状态切换。

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

(5)、初始化系统当前状态指针。

(6)、主程序通过上下文操作接口来控制系统当前状态的变化。

一般来说,熟练使用状态机六步法的嵌入式开发者,大都是两年软件开发经验以上的老鸟了。所以,如果你还是个嵌入式新手,请在实际开发中多多运用它,以后你的代码才能越来越优雅美观。而且掌握状态机编程对理解其他更复杂的设计模式也是大有裨益的。

如果你认为文章不错,欢迎分享给身边的同事好友

加入微信交流群:************ ,请猛戳这里→点击入群

扫描二维码推送至手机访问。

版权声明:本文由智汇百科网发布,如需转载请注明出处。

本文链接:https://www.zhihuibkw.com/post/6396.html

分享给朋友:

“c语言设计模式--状态模式(状态机)” 的相关文章

旅游必备知识:如何规划一场完美旅行

旅游必备知识:如何规划一场完美旅行

旅行,是一种寻找自我、拓宽视野的方式,而规划一场完美的旅行则是开启这段奇妙旅程的关键。以下是一些旅游必备的知识和技巧,帮助你规划出一场令人难忘的完美旅行。一、确定旅行目标和预算1. 明确旅行目的是放松身心、探索历史文化、享受美食还是进行户外运动?了解自己的旅行目的将有助于你选择合适的目的地和活动。2...

葡萄酒品鉴知识:从新手到行家

葡萄酒品鉴知识:从新手到行家

葡萄酒,那是一种流淌在时光与味蕾之间的神奇液体,它承载着历史、文化与自然的馈赠。对于葡萄酒的品鉴,从新手到行家,是一段充满趣味与挑战的旅程,让我们一同踏上这美妙的品鉴之路。新手阶段,仿佛是打开葡萄酒世界大门的初始时刻。我们需要了解葡萄酒的基本分类。从颜色上,有红葡萄酒、白葡萄酒和桃红葡萄酒之分;从酿...

宠物饲养知识:做合格的铲屎官

宠物饲养知识:做合格的铲屎官

在如今的生活中,越来越多的人选择养宠物作为自己的伴侣,它们给我们带来了无尽的欢乐和温暖。养宠物不仅仅是给予它们食物和住所,更重要的是要成为一名合格的“铲屎官”,负责照顾它们的生活起居,保障它们的健康和幸福。了解宠物的基本需求是做合格铲屎官的第一步。不同的宠物有不同的习性和需求,比如猫和狗。猫是独立的...

陶瓷知识认识传统陶瓷工艺

陶瓷知识认识传统陶瓷工艺

陶瓷,作为中国传统文化的瑰宝,承载着千年的历史与智慧。传统陶瓷工艺,更是一门精湛而神秘的艺术,它历经岁月的洗礼,依然散发着独特的魅力。传统陶瓷工艺的起源可以追溯到远古时期。那时,人们开始用泥土制作简单的器皿,用以储存食物和水。随着时间的推移,人们逐渐掌握了烧制技术,使得陶器的质量和美观度都有了极大的...

灯谜知识感受传统民俗魅力

灯谜知识感受传统民俗魅力

灯谜,作为中华民族传统民俗文化的瑰宝之一,以其独特的形式和深厚的内涵,展现出了无尽的魅力。它不仅是一种智力游戏,更是一种传承千年的文化符号,让我们在解谜的过程中,感受到了传统民俗的博大精深。灯谜的起源可以追溯到古代的隐语和廋辞。在古代,人们为了表达自己的情感、思想或传递信息,常常使用隐语和廋辞。这些...

常见蔬菜的营养价值

常见蔬菜的营养价值

蔬菜是我们日常饮食中不可或缺的重要组成部分,它们不仅为我们的味蕾带来丰富的口感,还蕴含着多种对人体健康至关重要的营养成分。一、绿叶蔬菜绿叶蔬菜是蔬菜家族中的佼佼者,其中以菠菜、生菜、西兰花等最为常见。菠菜富含维生素 A、C、K 以及铁、钙等矿物质。维生素 A 有助于维持视力和皮肤健康,维生素 C 具...