状态机

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

 

posted @ 2022-08-24 13:09  Miraculous_B  阅读(106)  评论(0)    收藏  举报