一步一步地深入浅出地实现状态机框架
Posted 一只蚂蚁2
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一步一步地深入浅出地实现状态机框架相关的知识,希望对你有一定的参考价值。
目标场景
假设我们现在有一个设备控制程序,上面有运行
、暂停
和停止
三个按钮,并且我们已实现了对应的逻辑控制代码,如下图:
void on_pushButton_Run_clicked()
setState("启动运行");
run();
setState("运行中");
void on_pushButton_Pause_clicked()
setState("正在暂停...");
pause();
setState("已暂停");
void on_pushButton_Stop_clicked()
setState("正在停止...");
stop();
setState("已停止");
由于我们没有作状态的限制,如在运行状态的时候我们可以再次点击运行按钮,这时候是不合理的
我们当然可以在按钮点击后将不能点击的按钮禁用来达到我们的目标。假设我们的规则如下:
- 在初始状态下只能点击运行按钮。
- 运行状态下可以点击暂停和停止按钮。
- 暂停状态下可以点击运行和停止按钮。
- 停止状态可以点运行按钮。
public:
MainWindow(): QMainWindow(nullptr)
ui.setupUi(this);
ui.pushButton_Run->setEnabled(true);
ui.pushButton_Pause->setEnabled(false);
ui.pushButton_Stop->setEnabled(false);
private slots:
void on_pushButton_Run_clicked()
setState("启动运行");
run();
setState("运行中");
ui.pushButton_Run->setEnabled(false);
ui.pushButton_Pause->setEnabled(true);
ui.pushButton_Stop->setEnabled(true);
void on_pushButton_Pause_clicked()
setState("正在暂停...");
pause();
setState("已暂停");
ui.pushButton_Run->setEnabled(true);
ui.pushButton_Pause->setEnabled(false);
ui.pushButton_Stop->setEnabled(true);
void on_pushButton_Stop_clicked()
setState("正在停止...");
stop();
setState("已停止");
ui.pushButton_Run->setEnabled(true);
ui.pushButton_Pause->setEnabled(false);
ui.pushButton_Stop->setEnabled(false);
OK,功能全部完美实现!!!
状态图
好吧,如果你看到了这里,那你肯定觉得上面的实现不完美!对于上面的场景,我们可以用状态图来直观地表示,那么什么是状态图呢?客官请看,下面这个就是状态图:
其中框框表示状态(小圆圈框框一般用来表示初始状态和终止状态),箭头表示触发条件。上面的状态图就可以很直观地表示了我们上面描述的规则了。直观的表示方法还有状态表,客官们感兴趣请个人自家查询相关资料。
原状态\\目标状态 | 初始状态 | 运行 | 暂停 | 停止 |
---|---|---|---|---|
初始状态 | - | 运行 | - | - |
运行 | - | - | 暂停 | 停止 |
暂停 | - | 运行 | - | 停止 |
停止 | - | 运行 | - | - |
枚举实现
上面的代码不完美的首要一点是状态相关代码和UI的代码耦合度太高,并且状态没有任何内部的数据表示。我们改用枚举的方式实现:
enum State
NoneState,
RunState,
PauseState,
StopState
;
public:
MainWindow(): QMainWindow(nullptr)
ui.setupUi(this);
setState(NoneState);
private slots:
void on_pushButton_Run_clicked()
setStateText("启动运行");
run();
setState(RunState);
void on_pushButton_Pause_clicked()
setStateText("正在暂停...");
pause();
setState(PauseState);
void on_pushButton_Stop_clicked()
setStateText("正在停止...");
stop();
setState(StopState);
void setState(State state)
m_state = state;
switch (m_state)
case NoneState:
ui.pushButton_Run->setEnabled(true);
ui.pushButton_Pause->setEnabled(false);
ui.pushButton_Stop->setEnabled(false);
setStateText("初始状态");
break;
case RunState:
ui.pushButton_Run->setEnabled(false);
ui.pushButton_Pause->setEnabled(true);
ui.pushButton_Stop->setEnabled(true);
setStateText("运行中");
break;
case PauseState:
ui.pushButton_Run->setEnabled(true);
ui.pushButton_Pause->setEnabled(false);
ui.pushButton_Stop->setEnabled(true);
setStateText("已暂停");
break;
case StopState:
ui.pushButton_Run->setEnabled(true);
ui.pushButton_Pause->setEnabled(false);
ui.pushButton_Stop->setEnabled(false);
setStateText("已停止");
break;
private:
State m_state;
这样调整后我们的界面更新代码全部在setState
函数中,对于状态比较少且简单情况,此方案是最简单和高效的,但是一旦状态数量和转换条件增加,setState
函数将会变得很大且复杂。
状态机模式
我们可以将状态枚举抽象成状态类,然后在各自的状态类中实现逻辑,这就是状态机模式:
class StateBase;
class Core
public:
Core()
// UI
virtual QPushButton* runButton() = 0;
virtual QPushButton* pauseButton() = 0;
virtual QPushButton* stopButton() = 0;
virtual void setText(const QString& text) = 0;
// Device
virtual void run() = 0;
virtual void pause() = 0;
virtual void stop() = 0;
void setState(StateBase* state) m_currentState = state;
StateBase* noneState;
StateBase* runState;
StateBase* pauseState;
StateBase* stopState;
StateBase* m_currentState;
;
class StateBase : public QObject
Q_OBJECT
public:
StateBase(Core* core,QObject* parent):QObject(parent), m_core(core)
virtual void run()
virtual void pause()
virtual void stop()
protected:
Core* m_core;
;
class NoneState : public StateBase
public:
NoneState(Core* core, QObject* parent):StateBase(core,parent)
virtual void run() override
m_core->setText("启动运行");
m_core->run();
m_core->runButton()->setEnabled(false);
m_core->pauseButton()->setEnabled(true);
m_core->stopButton()->setEnabled(true);
m_core->setText("运行中");
m_core->setState(m_core->runState);
;
class RunState : public StateBase
public:
RunState(Core* core, QObject* parent) :StateBase(core, parent)
virtual void pause() override
m_core->setText("准备暂停");
m_core->pause();
m_core->runButton()->setEnabled(true);
m_core->pauseButton()->setEnabled(false);
m_core->stopButton()->setEnabled(true);
m_core->setText("已暂停");
m_core->setState(m_core->pauseState);
virtual void stop() override
m_core->setText("准备停止");
m_core->stop();
m_core->runButton()->setEnabled(false);
m_core->pauseButton()->setEnabled(false);
m_core->stopButton()->setEnabled(true);
m_core->setText("已停止");
m_core->setState(m_core->stopState);
;
class PauseState : public StateBase
public:
PauseState(Core* core, QObject* parent) :StateBase(core, parent)
virtual void run() override
m_core->setText("启动运行");
m_core->run();
m_core->runButton()->setEnabled(false);
m_core->pauseButton()->setEnabled(true);
m_core->stopButton()->setEnabled(true);
m_core->setText("运行中");
m_core->setState(m_core->runState);
virtual void stop() override
m_core->setText("准备停止");
m_core->stop();
m_core->runButton()->setEnabled(false);
m_core->pauseButton()->setEnabled(false);
m_core->stopButton()->setEnabled(true);
m_core->setText("已停止");
m_core->setState(m_core->stopState);
;
class StopState : public StateBase
public:
StopState(Core* core, QObject* parent) :StateBase(core, parent)
virtual void run() override
m_core->setText("启动运行");
m_core->run();
m_core->runButton()->setEnabled(false);
m_core->pauseButton()->setEnabled(true);
m_core->stopButton()->setEnabled(true);
m_core->setText("运行中");
m_core->setState(m_core->runState);
virtual void pause() override
m_core->setText("准备暂停");
m_core->pause();
m_core->runButton()->setEnabled(true);
m_core->pauseButton()->setEnabled(false);
m_core->stopButton()->setEnabled(true);
m_core->setText("已暂停");
m_core->setState(m_core->pauseState);
;
class MainWindow : public QMainWindow, public Core
Q_OBJECT
public:
MainWindow(): QMainWindow(nullptr)
ui.setupUi(this);
noneState = new NoneState(this, this);
runState = new RunState(this, this);
pauseState = new PauseState(this, this);
stopState = new StopState(this, this);
setState(noneState);
ui.pushButton_Pause->setEnabled(false);
ui.pushButton_Stop->setEnabled(false);
virtual QPushButton* runButton() override return ui.pushButton_Run;
virtual QPushButton* pauseButton() override return ui.pushButton_Pause;
virtual QPushButton* stopButton() override return ui.pushButton_Stop;
virtual void setText(const QString& text) override setStateText(text);
virtual void run() override _run();
virtual void pause() override _pause();
virtual void stop() override _stop();
private slots:
void on_pushButton_Run_clicked()
m_currentState->run();
void on_pushButton_Pause_clicked()
m_currentState->pause();
void on_pushButton_Stop_clicked()
m_currentState->stop();
注1:工程项目中一定要分离界面和业务逻辑,此处为了方便演示,将界面和业务逻辑全部放入了Core
类中。
注2:正统的状态机还有一个状态机类StateMachine
,此处也偷懒了,相关的功能也放在了Core
类。对状态机模式感兴趣的同学请查阅相关资料。
采用状态机模式后,得到的好处有:
- 状态相互之间不再是网状结构: 状态只需要实现相关的条件切换即可,如
NoneState
只需实现run
条件。 - 增加修改状态很方便: 增加状态只需要新子类化一个状态类或修改状态类,然后编写相关的条件逻辑,不影响现有状态逻辑。
- 代码整洁干净: 将转换逻辑交给了状态类,逻辑清洗且更加合理,状态类的逻辑和状态图能够很好地对应。
- 代码更加健壮: 即使我们在状态切换时忘记了禁用按钮,用户多次点击也不会出现任何问题。
状态机框架
状态机模式虽然满足了我们的需求(常规的书籍上面也只会讲到这里),但是存在2个问题(至少我觉得是):
- 模式与我们的工程耦合度太高,换一个项目需重新实现一套状态机模式。
- 存在重复代码,如
PauseState
和RunState
的stop
函数(虽然可以提升到基类中实现,但是基类会变得异常庞大)。
现在让我们来进一步探究,首先再次回顾一下我的状态图:
根据状态图,我们可以抽象出2个类:状态类和转换条件。为了维护状态之间的切换,我们还需要一个总体的协调类,我们分别命名为XState
、XStateTransition
和XStateMachine
:
class XState ;
class XStateTransition;
class XStateMachine;
对于上面的状态图,我们期望用户可以像下面这样写代码:
noneState->addTransition(run,runState);
runState->addTransition(pause,pauseState);
runState->addTransition(stop,stopState);
pauseState->addTransition(run,runState);
pauseState->addTransition(stop,stopState);
stopState->addTransition(run,runState);
这样代码是不是超级清晰、简单、直观、好看、美丽、可爱、善良呢?期望还是要有的,万一我们可以实现呢!!!
首先我们需要保存所有转换条件和目标状态,以便在合适的时机来切换:
#include <unordered_map>
class XStateTransition;
class XStateMachine;
class XState
public:
XState(XStateMachine* stateMachine):m_stateMachine(stateMachine)
void addTransition(XStateTransition* transition, XState* targetState)
m_stateMap[transition] = targetState;
bool trigger(XStateTransition* transition);
private:
XStateMachine* m_stateMachine;
std::unordered_map<XStateTransition*, XState*> m_stateMap;
;
class XStateMachine
public:
void changeState(XState* state)
m_currentState = state;
bool trigger(XStateTransition* transition)
return m_currentState->trigger(transition);
private:
XState* m_currentState;
;
inline bool XState::trigger(XStateTransition* transition)
auto itr = m_stateMap.find(transition);
if (itr != m_stateMap.end())
m_stateMachine->changeState(itr->second);
return true;
return false;
class XStateTransition
public:
XStateTransition(XStateMachine* stateMachie):m_stateMachine(stateMachie)
void trigger() m_stateMachine->trigger(this);
private:
XStateMachine* m_stateMachine;
;
调整我们的项目代码,之前的状态机模式代码可以全部删除了:
class MainWindow : public QMainWindow, public Core
Q_OBJECT
public:
MainWindow(): QMainWindow(nullptr)
ui.setupUi(this);
m_stateMachine = new XStateMachine();
m_run = new XStateTransition(m_stateMachine);
m_pause = new XStateTransition(m_stateMachine);
m_stop = new XStateTransition(m_stateMachine);
m_noneState = new XState(m_stateMachine);
m_runState = new XState(m_stateMachine);
m_pauseState = new XState(m_stateMachine);
m_stopState = new XState(m_stateMachine);
m_noneState->addTransition(m_run, m_runState);
m_runState->addTransition(m_pause, m_pauseState);
m_runState->addTransition(m_stop, m_stopState);
m_pauseState->addTransition(m_run, m_runState);
m_pauseState->addTransition(m_stop, m_stopState);
m_stopState->addTransition(m_run, m_runState);
m_stateMachine->changeState(m_noneState);
ui.pushButton_Pause->setEnabled(false);
ui.pushButton_Stop->setEnabled(false);
virtual QPushButton* runButton() override return ui.pushButton_Run;
virtual QPushButton* pauseButton() override return ui.pushButton_Pause;
virtual QPushButton* stopButton() override return ui.pushButton_Stop;
virtual void setText(const QString& text) override setStateText(text);
virtual void run() override _run();
virtual void pause() override _pause();
virtual void stop() override _stop();
private slots:
void on_pushButton_Run_clicked()
m_run->trigger();
void on_pushButton_Pause_clicked()
m_pause->trigger();
void on_pushButton_Stop_clicked()
m_stop->trigger();
private:
XStateMachine* m_stateMachine;
XState* m_noneState ;
XState* m_runState ;
XState* m_pauseState ;
XState* m_stopState ;
XStateTransition* m_run ;
XStateTransition* m_pause ;
XStateTransition* m_stop ;
目前状态机框架应该时正常工作的,但是界面并没有变化,因为我们没有进行任何界面处理。我们在XStateTransition
中定义一个虚函数用于状态切换时更新信息:
class XStateTransition
public:
XStateTransition(XStateMachine* stateMachie):m_stateMachine(stateMachie)
void trigger()
if (m_stateMachine->trigger(this))
triggerEvent();
protected:
virtual void triggerEvent()
private:
XStateMachine* m_stateMachine;
;
子类化状态切换类并实现具体的功能的功能:
class RunTransition : public XStateTransition
public:
RunTransition(XStateMachine* stateMachine, Core* core):XStateTransition(stateMachine),m_core(core)
protected:
virtual void triggerEvent() override
m_core->setText("启动运行");
m_core->run();
m_core->runButton()->setEnabled(false);
m_core->pauseButton()->setEnabled(true);
m_core->stopButton()->setEnabled(true);
m_core->setText("运行中");
private:
Core* m_core;
;
class PauseTransition : public XStateTransition
public:
PauseTransition(XStateMachine* stateMachine, Core* core) :XStateTransition(stateMachine), m_core(core)
protected:
virtual void triggerEvent() override
m_core->setText("准备暂停");
m_core->pause();
m_core->runButton()->setEnabled(true);
m_core->pauseButton()->setEnabled(false);
m_core->stopButton()->setEnabled(true);
m_core->setText("已暂停");
private:
Core* m_core;
;
class StopTransition : public XStateTransition
public:
StopTransition(XStateMachine* stateMachine, Core* core) :XStateTransition(stateMachine), m_core(core)
protected:
virtual void triggerEvent() override
m_core->setText("准备停止");
m_core->stop();
m_core->runButton()->setEnabled(true);
m_core->pauseButton()->setEnabled(false);
m_core->stopButton()->setEnabled(false);
m_core->setText("已停止");
private:
Core* m_core;
;
修改初始化代码:
m_run = new RunTransition(m_stateMachine,this);
m_pause = new PauseTransition(m_stateMachine,this);
m_stop = new StopTransition(m_stateMachine,this);
到目前为止,我们成功解决状态机模式遗留的几个问题,想想都有些激动呢!
等等!!! 我们的按钮是否可用是否跟转换条件有关,例如,如果可以切换到运行状态才可以点击运行按钮,跟条件切换的思路一样,我们同样可以通过虚函数来达到此目的:
class XState
public:
...
bool haveTransition(XStateTransition* transition)const
return m_stateMap.find(transition) != m_stateMap.end();
...
;
class XStateMachine
public:
void changeState(XState* state)
if(m_currentState!=state)
m_currentState = state;
changeStateEvent(m_currentState);
bool trigger(XStateTransition* transition)
return m_currentState->trigger(transition);
protected:
virtual void changeStateEvent(XState* state)
private:
XState* m_currentState;
;
实现对于的子类并修改工程代码,删除状态转换子类的按钮相关代码:
class StateMachine : public XStateMachine
public:
StateMachine(Core* core) :m_core(core)
m_run = new RunTransition(this, m_core);
m_pause = new PauseTransition(this, m_core);
m_stop = new StopTransition(this, m_core);
m_noneState = new XState(this);
m_runState = new XState(this);
m_pauseState = new XState(this);
m_stopState = new XState(this);
m_noneState->addTransition(m_run, m_runState);
m_runState->addTransition(m_pause, m_pauseState);
m_runState->addTransition(m_stop, m_stopState);
m_pauseState->addTransition(m_run, m_runState);
m_pauseState->addTransition(m_stop, m_stopState);
m_stopState->addTransition(m_run, m_runState);
changeState(m_noneState);
void run() m_run->trigger();
void pause() m_pause->trigger();
void stop() m_stop->trigger();
protected:
virtual void changeStateEvent(XState* state)override
m_core->runButton()->setEnabled(state->haveTransition(m_run));
m_core->pauseButton()->setEnabled(state->haveTransition(m_pause));
m_core->stopButton()->setEnabled(state->haveTransition(m_stop));
private:
Core* m_core;
XState* m_noneState;
XState* m_runState;
XState* m_pauseState;
XState* m_stopState;
XStateTransition* m_run;
XStateTransition* m_pause;
XStateTransition* m_stop;
;
class MainWindow : public QMainWindow, public Core
Q_OBJECT
public:
MainWindow(): QMainWindow(nullptr)
ui.setupUi(this);
m_stateMachine = new StateMachine(this);
virtual QPushButton* runButton() override return ui.pushButton_Run;
virtual QPushButton* pauseButton() override return ui.pushButton_Pause;
virtual QPushButton* stopButton() override return ui.pushButton_Stop;
virtual void setText(const QString& text) override setStateText(text);
virtual void run() override _run();
virtual void pause() override _pause();
virtual void stop() override _stop();
private slots:
void on_pushButton_Run_clicked()
m_stateMachine->run();
void on_pushButton_Pause_clicked()
m_stateMachine->pause();
void on_pushButton_Stop_clicked()
m_stateMachine->stop();
private:
StateMachine* m_stateMachine;
至此,我们状态机框架已圆满完成!!!
。。。好吧,看来你还没走,那我们继续。。。
子状态
假设我们的状态图变成如下形式:
也就是设备启动的时候处于未就绪状态,自检成功后处于就绪状态。就绪、运行和暂停都属于联机状态,在任意联机状态下发生故障回到未就绪状态。其中就绪、运行和暂停就是联机状态的子状态。当然可以不用子状态来表示,为每个联机状态的子状态添加转换条件是可以达到目的的,但是这不是我们风格,对吧?
为了我们的状态机框架能够支持子状态,我们需要调整我们的XState
,实现上面我们可以选择记录所有的孩子状态、和可以保存父亲状态。此处我选择保存父亲状态,另外XState
还需要保存当前的子状态,XStateMachine
感觉很像一个有孩子状态的状态,所以我们就让XStateMachine
直接继承自XState
,调整代码如下:
class XStateTransition;
class XState
public:
XState(XState* parent):m_parent(parent),m_currentState(nullptr)
virtual ~XState()
void addTransition(XStateTransition* transition, XState* targetState)
m_stateMap[transition] = targetState;
bool haveTransition(XStateTransition* transition)const
auto itr = m_stateMap.find(transition);
if (itr != m_stateMap.end())
return true;
if (m_currentState)
return m_currentState->haveTransition(transition);
return false;
XState* currentState(bool recursively=false)const
if (recursively)
if (m_currentState)
if (m_currentState->m_currentState)
return m_currentState->currentState(true);
else
return m_currentState;
else
return nullptr;
else
return m_currentState;
protected:
virtual void changeStateEvent()
virtual void enterEvent()
virtual void exitEvent()
void changeState(XState* state)
if (m_currentState != state)
if (m_currentState)
m_currentState->exit();
m_currentState = findChild(state);
if (m_currentState)
m_currentState->enter();
// 子状态继续切换状态
if (m_currentState != state)
m_currentState->changeState(state);
changeStateEvent();
if (m_parent)
m_parent->changeStateEvent();
void trigger(XStateTransition* transition)
// Trigger current;
auto itr = m_stateMap.find(transition);
if (itr != m_stateMap.end())
m_parent->changeState(itr->second);
return;
// Trigger child
if (m_currentState)
m_currentState->trigger(transition);
return;
Q_ASSERT(false);
void enter() enterEvent();
void exit() exitEvent();
XState* findChild(XState* descendant)const
if (descendant == nullptr)
return nullptr;
if (descendant->m_parent == this)
return descendant;
if (descendant->m_parent == nullptr)
return nullptr;
return findChild(descendant->m_parent);
private:
XState* m_parent;
XState* m_currentState;
std::unordered_map<XStateTransition*, XState*> m_stateMap;
;
class XStateMachine : public XState
public:
XStateMachine() :XState(nullptr)
virtual ~XStateMachine()
void trigger(XStateTransition* transition) XState::trigger(transition);
;
class XStateTransition
public:
XStateTransition(XStateMachine* stateMachie):m_stateMachine(stateMachie)
virtual ~XStateTransition()
void trigger()
if (m_stateMachine->haveTransition(this) && triggerEvent())
m_stateMachine->trigger(this);
protected:
virtual bool triggerEvent() return true;
private:
XStateMachine* m_stateMachine;
;
class Core
public:
Core()
// UI
virtual QPushButton* runButton() = 0;
virtual QPushButton* pauseButton() = 0;
virtual QPushButton* stopButton() = 0;
virtual QPushButton* checkButton() = 0;
virtual QPushButton* errButton() = 0;
virtual void setText(const QString& text) = 0;
// Device
virtual void run() = 0;
virtual void pause() = 0;
virtual void stop() = 0;
virtual bool check() = 0;
virtual void err() = 0;
;
class CheckOkTransition : public XStateTransition
public:
CheckOkTransition(XStateMachine* stateMachine, Core* core) :XStateTransition(stateMachine), m_core(core)
protected:
virtual bool triggerEvent() override
m_core->setText("自检中...");
if (m_core->check())
return true;
else
m_core->setText("自检失败");
return false;
private:
Core* m_core;
;
class ErrTransition : public XStateTransition
public:
ErrTransition(XStateMachine* stateMachine, Core* core) :XStateTransition(stateMachine), m_core(core)
protected:
virtual bool triggerEvent() override
m_core->setText("发生故障");
return true;
private:
Core* m_core;
;
class RunTransition : public XStateTransition
public:
RunTransition(XStateMachine* stateMachine, Core* core):XStateTransition(stateMachine),m_core(core)
protected:
virtual bool triggerEvent() override
m_core->setText("启动运行");
m_core->run();
return true;
private:
Core* m_core;
;
class PauseTransition : public XStateTransition
public:
PauseTransition(XStateMachine* stateMachine, Core* core) :XStateTransition(stateMachine), m_core(core)
protected:
virtual bool triggerEvent() override
m_core->setText("准备暂停");
m_core->pause();
return true;
private:
Core* m_core;
;
class StopTransition : public XStateTransition
public:
StopTransition(XStateMachine* stateMachine, Core* core) :XStateTransition(stateMachine), m_core(core)
protected:
virtual bool triggerEvent() override
m_core->setText("准备停止");
m_core->stop();
return true;
private:
Core* m_core;
;
class State : public XState
public:
State(XState* parent, const QString& text, Core* core):XState(parent),m_text(text),m_core(core)
QString text() return m_text;
protected:
virtual void enterEvent()override
m_core->setText(m_text);
private:
Core* m_core;
QString m_text;
;
class StateMachine : public XStateMachine
public:
StateMachine(Core* core) :m_core(core)
m_check = new CheckOkTransition(this, m_core);
m_err = new ErrTransition(this, m_core);
m_run = new RunTransition(this, m_core);
m_pause = new PauseTransition(this, m_core);
m_stop = new StopTransition(this, m_core);
m_offlineState = new State(this,"脱机",m_core);
m_onlineState = new State(this,"联机", m_core);
m_noneState = new State(m_onlineState, "就绪", m_core);
m_runState = new State(m_onlineState, "运行", m_core);
m_pauseState = new State(m_onlineState, "已暂停", m_core);
m_offlineState->addTransition(m_check, m_noneState);
m_noneState->addTransition(m_run, m_runState);
m_runState->addTransition(m_pause, m_pauseState);
m_runState->addTransition(m_stop, m_noneState);
m_pauseState->addTransition(m_run, m_runState);
m_pauseState->addTransition(m_stop, m_noneState);
m_onlineState->addTransition(m_err, m_offlineState);
changeState(m_offlineState);
void check() m_check->trigger();
void run() m_run->trigger();
void pause() m_pause->trigger();
void stop() m_stop->trigger();
void err() m_err->trigger();
protected:
virtual void changeStateEvent()override
auto state = currentState(true);
m_core->checkButton()->setEnabled(state->haveTransition(m_check));
m_core->runButton()->setEnabled(state->haveTransition(m_run));
m_core->pauseButton()->setEnabled(state->haveTransition(m_pause));
m_core->stopButton()->setEnabled(state->haveTransition(m_stop));
private:
Core* m_core;
XState* m_offlineState;
XState* m_onlineState;
XState* m_noneState;
XState* m_runState;
XState* m_pauseState;
XStateTransition* m_check;
XStateTransition* m_err;
XStateTransition* m_run;
XStateTransition* m_pause;
XStateTransition* m_stop;
;
class MainWindow : public QMainWindow, public Core
Q_OBJECT
public:
MainWindow(): QMainWindow(nullptr)
ui.setupUi(this);
m_stateMachine = new StateMachine(this);
virtual QPushButton* runButton() override return ui.pushButton_Run;
virtual QPushButton* pauseButton() override return ui.pushButton_Pause;
virtual QPushButton* stopButton() override return ui.pushButton_Stop;
virtual QPushButton* checkButton()override return ui.pushButton_Check;
virtual QPushButton* errButton() override return ui.pushButton_Err;
virtual void setText(const QString& text) override setStateText(text);
virtual void run() override _run();
virtual void pause() override _pause();
virtual void stop() override _stop();
virtual bool check() override sleep(1000); return QTime::currentTime().second() % 2 == 0;
virtual void err() override
private slots:
void on_pushButton_Run_clicked()
m_stateMachine->run();
void on_pushButton_Pause_clicked()
m_stateMachine->pause();
void on_pushButton_Stop_clicked()
m_stateMachine->stop();
void on_pushButton_Check_clicked()
m_stateMachine->check();
void on_pushButton_Err_clicked()
m_stateMachine->err();
private:
StateMachine* m_stateMachine;
最后的倔强
如果我们有多个条件转换到联机状态,并且我们希望联机状态的默认状态不是就绪状态,那么上面的框架代码有几个条件转换到就绪状态就需要调整几处代码,我们可以增加一个初始状态来解决,并在状态进入时自动切换:
class XState
public:
XState(XState* parent):m_parent(parent),m_currentState(nullptr),m_initialState(nullptr)
...
void enter()
enterEvent();
if (m_initialState)
changeState(m_initialState);
private:
...
XState* m_initialState;
;
浅出
Qt已提供了状态机框架(请参见Qt助手的The State Machine Framework
相关章节),用法基本和上面的一致,功能更全更强大,直接拿来用就好了!
状态机框架完整代码
#ifndef _X_STATE_MACHINE_HPP
#define _X_STATE_MACHINE_HPP
#include <unordered_map>
#include <cassert>
class XStateTransition;
class XState
public:
XState(XState* parent):m_parent(parent),m_currentState(nullptr),m_initialState(nullptr)
virtual ~XState()
void addTransition(XStateTransition* transition, XState* targetState)
m_stateMap[transition] = targetState;
bool haveTransition(XStateTransition* transition)const
auto itr = m_stateMap.find(transition);
if (itr != m_stateMap.end())
return true;
if (m_currentState)
return m_currentState->haveTransition(transition);
return false;
XState* currentState(bool recursively=false)const
if (recursively)
if (m_currentState)
if (m_currentState->m_currentState)
return m_currentState->currentState(true);
else
return m_currentState;
else
return nullptr;
else
return m_currentState;
void setInitialState(XState* state)
assert(m_initialState == nullptr&&state);
m_initialState = state;
protected:
virtual void changeStateEvent()
virtual void enterEvent()
virtual void exitEvent()
void changeState(XState* state)
if (m_currentState != state)
if (m_currentState)
m_currentState->exit();
m_currentState = findChild(state);
if (m_currentState)
m_currentState->enter();
if (m_currentState != state)
m_currentState->changeState(state);
changeStateEvent();
if (m_parent)
m_parent->changeStateEvent();
void trigger(XStateTransition* transition)
// Trigger current;
auto itr = m_stateMap.find(transition);
if (itr != m_stateMap.end())
m_parent->changeState(itr->second);
return;
// Trigger child
if (m_currentState)
m_currentState->trigger(transition);
return;
assert(false);
void enter()
enterEvent();
if (m_initialState)
changeState(m_initialState);
void exit() exitEvent();
XState* findChild(XState* descendant)const
if (descendant == nullptr)
return nullptr;
if (descendant->m_parent == this)
return descendant;
if (descendant->m_parent == nullptr)
return nullptr;
return findChild(descendant->m_parent);
private:
XState* m_parent;
XState* m_currentState;
XState* m_initialState;
std::unordered_map<XStateTransition*, XState*> m_stateMap;
;
class XStateMachine : public XState
public:
XStateMachine() :XState(nullptr)
virtual ~XStateMachine()
void trigger(XStateTransition* transition) XState::trigger(transition);
void setInitialState(XState* state)
XState::setInitialState(state);
changeState(state);
;
class XStateTransition
public:
XStateTransition(XStateMachine* stateMachie):m_stateMachine(stateMachie)
virtual ~XStateTransition()
void trigger()
if (m_stateMachine->haveTransition(this) && triggerEvent())
m_stateMachine->trigger(this);
protected:
virtual bool triggerEvent() return true;
private:
XStateMachine* m_stateMachine;
;
#ifdef X_UNIT_TEST
#include "XUnitTest.hpp"
class State : public XState
public:
State(const std::string& name, XState* state) :XState(state), m_name(name)
protected:
virtual void enterEvent()override
std::cout << "\\t" << m_name << " ENTER" << std::endl;
virtual void exitEvent() override
std::cout << "\\t" << m_name << " EXIT" << std::endl;
private:
std::string m_name;
;
class XStateMachineTest :public XUnitTest
protected:
virtual void testEvent()
XStateMachine stateMachine;
State s1("s1", &stateMachine);
State sq("sq", &stateMachine);
State s11("s11", &s1);
State s12("s12", &s1);
State s13("s13", &s1);
s1.setInitialState(&s11);
stateMachine.setInitialState(&s1);
XStateTransition t1(&stateMachine);
s11.addTransition(&t1, &s12);
s12.addTransition(&t1, &s13);
s13.addTransition(&t1, &s11);
XStateTransition tq(&stateMachine);
s1.addTransition(&tq, &sq);
sq.addTransition(&tq, &s1);
X_OUTPUT(t1.trigger());
X_VERIFY(stateMachine.currentState(true) == &s12);
X_OUTPUT(t1.trigger());
X_VERIFY(stateMachine.currentState(true) == &s13);
X_OUTPUT(tq.trigger());
X_VERIFY(stateMachine.currentState(true) == &sq);
X_OUTPUT(t1.trigger());
X_VERIFY(stateMachine.currentState(true) == &sq);
X_OUTPUT(tq.trigger());
X_VERIFY(stateMachine.currentState(true) == &s11);
X_OUTPUT(t1.trigger());
X_VERIFY(stateMachine.currentState(true) == &s12);
X_OUTPUT(t1.trigger());
X_VERIFY(stateMachine.currentState(true) == &s13);
;
#endif // X_UNIT_TEST
#endif //_X_STATE_MACHINE_HPP
** 别翻了,这次真的没有了!!! **
以上是关于一步一步地深入浅出地实现状态机框架的主要内容,如果未能解决你的问题,请参考以下文章
我们';我们将带您一步一步地使用Python、Django、Bootstrap、Javascript等构建一个现代的、完全开源的电子商务web应用程序。