状态机存储从开始时刻到现在的状态变化,并根据当前输入,决定下一个状态。因此状态机包含三大要素:
我们以mp3播放器为例实现一个简单的状态机,其状态图入下所示
图中:“停止按钮/停止操作” 中 "停止按钮"代表输入“停止操作”代表响应,箭头代表从播放状态到停止状态的状态切换。
从上图中可以看出。
一共有三种状态: “停止状态”、”播放状态“、“暂停状态”。
一共有两种输入:”播放按钮“、”停止按钮“。
一共有三种响应:“播放处理”、“暂停处理”、“停止处理”。
为了便于理解我们对上诉状态图换一种表达方式,我们先创建一个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();
}
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);
}
上面我们采用面向对象的方法实现了一个简单的状态机,这种采用面向对象方式的设计,把状态机的三大要素进行分离设计,这种分层设计的思想较传统switch(case)方式设计的状态机扩展和维护性要高很多。如果需要增加状态只需要修改“状态”、和“响应”部分代码即可。
参考《C现在方法》4.1状态模式