参考https://blog.csdn.net/u012841414/article/details/123903539
简单状态机
通过switch(state)完成。
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;
}
}
这样会导致一个问题,当状态和事件增加后,onEvent函数就会变得非常庞大,这是因为该函数的代码行数与状态和事件数量的乘积成正比,直接导致代码行数爆炸增长,代码会越发变得难以阅读和维护。
比如新增一个事件,强制关闭播放器。显然任何状态下都能强制关闭,那么case里面,就要多三个if判断。
比如新增一个状态,高音播放态,那么对应的也要增加一个事件。每个case要多一个if判断,并且新增一个case。高音播放态显然能够从到达暂停、停止、正常播放态。于是新增的case有三个if判断。可以看到,我们新增一个内容,就要改变简单状态机几乎所有的代码。
状态模式(状态机)
我们可以利用c语言的多态特性来分解复杂的条件分支(关于c语言多态的实现,请查看c语言面向对象基础)。这样一来可以就避免大量的swith...case和 if...else等条件分支语句,提高程序的可维护性和可扩展性。
初始化:pCurrentState
四步定义法:定义顶层接口,定义状态,定义状态转移方法,定义顶层按钮方法(播放和暂停按钮往往是同一个)
定义顶层接口
typedef struct State{
void (* stop)();
void (* palyOrPause)();
}State;
总的来说,就是以状态为对象,在状态中定义状态转移的方法。比如播放态
State PLAY = {
stopPlay,
pausePlay
};
State IDLE = {
ignore,//空闲状态时,stop键操作无效,play/pause会开始播放音乐
startPlay
};
我们定义了两个状态转移的方法
void startPlay()
{
//实现具体功能
printf("开始播放音乐\n");
//进入播放状态
pCurrentState = &PLAY;
}
状态转移方法主要是改变当前状态pCurrentState
顶层按钮方法
State context = {
onStop,
onPlayOrPause
};
void onStop(State *pThis)
{
pCurrentState->stop(pThis);
}
void onPlayOrPause(State *pThis)
{
pCurrentState->palyOrPause(pThis);
}
测试
void main()
{
init();
context.palyOrPause();//播放
context.palyOrPause();//暂停
context.palyOrPause();//播放
context.stop();//停止
}