电子说
状态模式(状态机)是嵌入式开发中最重要、最核心的设计模式之一,毫不夸张的说,是否熟练掌握状态模式,很大程度上直接决定了嵌入式工程师的代码掌控能力。在嵌入式开发里面,几乎80%以上的程序都有状态模式(状态机)的影子。在一个思路清晰而且高效的程序中,必然有状态模式(状态机)身影浮现。但是很多嵌入式开发者只是掌握一些很基础的状态机编程,对状态机编程如果提高程序的可维护性和可拓展性并没有一个深刻的理解。
这里我通过一个简单易懂的MP3播放器案例,把自己独家总结的状态机六步法分享给大家,帮助大家在啃下状态机这块硬骨头。相信你深度掌握状态机编程以后,你优雅美观的代码会让同事朋友们眼前一亮,啧啧称赞。
几乎在所有的复杂项目里面,都充斥着各种事物状态的变化。这是因为我们身处的物理世界本来就是一个动态多变的环境,自然我们开发的程序也要根据事物不同时刻不同场景的状态,不断调整自身的行为属性。
比如电影《分裂》里面,詹姆斯·麦卡沃伊饰演的男主Kevin患有精神分裂,有着多重人格疾病,他被精神病医生Dr. Fletcher诊断出有23重人格,可以随时间或境遇切换,一会变成精明聪颖的律师,一会是懦弱的失败者总是要自杀,一个境遇触发又是愤怒的杀人暴徒,这人格切换速度,丧心病狂到令人发指。
想象一下,假如我们要在程序中实现这样一个角色,就必须要有一个良好的状态变化设计,才能保证主人公在快速切换状态的情况下,都能拥有与之匹配的精神状态和行为举止。
场景:设计一个简单的MP3播放器,要求两个按键(播放/暂停、停止)分别控制MP3的播放/停止功能。
如下表所示:
在状态模式的设计开发中,我们通常借助状态迁移图来进行多个状态的分析。本案例中的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); //停止
}