假设我们现在有一个设备控制程序,上面有运行
、暂停
和停止
三个按钮,并且我们已实现了对应的逻辑控制代码,如下图:
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);
}
好吧,如果你看到了这里,那你肯定觉得上面的实现不完美!对于上面的场景,我们可以用状态图来直观地表示,那么什么是状态图呢?客官请看,下面这个就是状态图:
其中框框表示状态(小圆圈框框一般用来表示初始状态和终止状态),箭头表示触发条件。上面的状态图就可以很直观地表示了我们上面描述的规则了。直观的表示方法还有状态表,客官们感兴趣请个人自家查询相关资料。
原状态\目标状态 | 初始状态 | 运行 | 暂停 | 停止 |
---|---|---|---|---|
初始状态 | - | 运行 | - | - |
运行 | - | - | 暂停 | 停止 |
暂停 | - | 运行 | - | 停止 |
停止 | - | 运行 | - | - |
上面的代码不完美的首要一点是状态相关代码和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
函数(虽然可以提升到基类中实现,但是基类会变得异常庞大)。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
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 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 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
#include
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 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
** 别翻了,这次真的没有了!!! **