基于C的面向对象的状态机设计

1、状态机的三要素

状态机存储从开始时刻到现在的状态变化,并根据当前输入,决定下一个状态。因此状态机包含三大要素:

  • 存储状态
  • 获得输入(我们称作跳转调节或者事件)
  • 作出响应

我们以mp3播放器为例实现一个简单的状态机,其状态图入下所示

基于C的面向对象的状态机设计_第1张图片
图1.1 mp3播放器状态图

图中:“停止按钮/停止操作” 中 "停止按钮"代表输入“停止操作”代表响应,箭头代表从播放状态到停止状态的状态切换。

从上图中可以看出。
一共有三种状态: “停止状态”、”播放状态“、“暂停状态”。
一共有两种输入:”播放按钮“、”停止按钮“。
一共有三种响应:“播放处理”、“暂停处理”、“停止处理”。

2、状态机设计

为了便于理解我们对上诉状态图换一种表达方式,我们先创建一个2行3列事件-状态表格,表头的行存储所有的状态,列存储事件(即输入)。表格实体对应的是每种事件和每种状态组合后的4种响应。

事件-状态 停止状态 播放状态 暂停状态
播放按键 播放处理 暂停处理 播放处理
停止按钮 无响应 停止处理 停止处理

要设计状态机我们需要完成状态机的三大要素设计即状态、输入、响应。

  • 状态设计
    设计各个状态之前首先定义一个状态结构体对象,结构体内部用来存储当前状态下每一种输入所对应的响应函数指针;
/*定义状态对象*/
typedef struct State
{
  void (* const KeyPlay)();   //当前状态下对应的”播放按键“事件(输入)响应
  void (* const KeyStop)();   //当前状态下对应的”停止按键“事件(输入)响应
}State_Object;

至此我们只是完成对状态数据结构的抽象即State_Object,下面我们用抽象出来的状态对象定义我们需要的3种状态

/*定义"停止状态"对象实体*/
State_Object STOP = 
{ 
    StartPlay,              //"停止状态"下对应的”播放按键“事件响应
    Ignore,                 //"停止状态"下对应的”停止按键“事件响应
};

/*定义"播放状态“对象实体*/
State_Object PLAY = 
{ 
    PausePlay,            //"播放状态"下对应的”播放按键“事件响应
    StopPlay,              //"播放状态"下对应的”停止按键“事件响应 
};

/*定义"暂停状态“对象实体*/
State_Object PAUSE = 
{ 
    StartPlay,         //"暂停状态"下对应的”播放按键“事件响应
    StopPlay,          //"暂停状态"下对应的”停止按键“事件响应 
};

我们需要定义一个状态指针后面会作为用来实现输入到响应的转换。

/*定义状态指针*/
State_Object * pCurrentState = &STOP;//初始化为“停止状态”
  • 响应设计

接下来我们定义需要4种响应处理函数(实际上只有三种响应函数,无响应也需要为其定义一个函数实体)

/*播放响应函数*/
void StartPlay() //播放
{
    printf("播放!\r\n");
    pCurrentState = &PLAY; //切换到播放状态
}

/*无响应处理函数*/
void Ignore() 
{
	printf("忽略,不进行任何操作\r\n");
}

/*暂停响应函数*/
void PausePlay() 
{
	printf("暂停!\r\n");
	pCurrentState = &PAUSE;//切换到暂停状态
}

/*停止响应函数*/
void StopPlay()
{
	printf("停止\r\n");
	pCurrentState = &STOP;
}
  • 输入设计

然后定义两种输入事件:“播放按键”、“停止按键”

  /*定义“播放按键”输入响应*/
  void Key_PlayOn(void)
  {
        pCurrentState->KeyPlay();
  }
  
  /*定义“停止按键”输入响应*/
  void key_StopOn(void)
  {
        pCurrentState->KeyPlay();
  }
  

3、运行结果

state.h

#ifndef _STATE_H 
#define _STATE_H 

/*定义状态对象*/
typedef struct State
{
  void (* const KeyPlay)();   //当前状态下对应的”播放按键“事件响应
  void (* const KeyStop)();   //当前状态下对应的”停止按键“事件响应
}State_Object;

/*定义响应*/
void StartPlay();
void Ignore(); 
void PausePlay(); 
void StopPlay(); 

/*定义输入*/
void Key_PlayOn(void);
void key_StopOn(void);

#endif

state.c

#include "state.h"

/*定义"停止状态"对象实体*/
State_Object STOP = 
{ 
    StartPlay,              //"停止状态"下对应的”播放按键“事件响应
    Ignore,                 //"停止状态"下对应的”停止按键“事件响应
};

/*定义"播放状态“对象实体*/
State_Object PLAY = 
{ 
    PausePlay,            //"播放状态"下对应的”播放按键“事件响应
    StopPlay,              //"播放状态"下对应的”停止按键“事件响应 
};

/*定义"暂停状态“对象实体*/
State_Object PAUSE = 
{ 
    StartPlay,         //"暂停状态"下对应的”播放按键“事件响应
    StopPlay,          //"暂停状态"下对应的”停止按键“事件响应 
};

/*定义状态指针*/
State_Object * pCurrentState = &STOP;//初始化为“停止状态”

/*播放响应函数*/
void StartPlay() //播放
{
    printf("播放!\r\n");
    pCurrentState = &PLAY; //切换到播放状态
}

/*无响应处理函数*/
void Ignore() 
{
    printf("忽略,不进行任何操作\r\n");
}

/*暂停响应函数*/
void PausePlay() 
{
    printf("暂停!\r\n");
    pCurrentState = &PAUSE;//切换到暂停状态
}

/*停止响应函数*/
void StopPlay()
{
    printf("停止\r\n");
    pCurrentState = &STOP;
}

/*定义“播放按键”输入响应*/
void Key_PlayOn(void)
{
    pCurrentState->KeyPlay();
}

/*定义“停止按键”输入响应*/
void key_StopOn(void)
{
    pCurrentState->KeyStop();
}

main.c

#include "state.h"
int main(void)
{
    Key_PlayOn();
    Key_PlayOn();
    Key_PlayOn();
    key_StopOn();
    key_StopOn();
    while(1);     
}

基于C的面向对象的状态机设计_第2张图片
图3.1 运行结果

总结

上面我们采用面向对象的方法实现了一个简单的状态机,这种采用面向对象方式的设计,把状态机的三大要素进行分离设计,这种分层设计的思想较传统switch(case)方式设计的状态机扩展和维护性要高很多。如果需要增加状态只需要修改“状态”、和“响应”部分代码即可。

参考《C现在方法》4.1状态模式

你可能感兴趣的:(基于C的面向对象的状态机设计)