一步一步地深入浅出地实现状态机框架

目标场景

假设我们现在有一个设备控制程序,上面有运行暂停停止三个按钮,并且我们已实现了对应的逻辑控制代码,如下图:

	void on_pushButton_Run_clicked() 
	{
		setState("启动运行");
		run();
		setState("运行中");
	}
	void on_pushButton_Pause_clicked()
	{
		setState("正在暂停...");
		pause();
		setState("已暂停");
	}
	void on_pushButton_Stop_clicked()
	{
		setState("正在停止...");
		stop();
		setState("已停止");
	}

一步一步地深入浅出地实现状态机框架_第1张图片
由于我们没有作状态的限制,如在运行状态的时候我们可以再次点击运行按钮,这时候是不合理的
一步一步地深入浅出地实现状态机框架_第2张图片
我们当然可以在按钮点击后将不能点击的按钮禁用来达到我们的目标。假设我们的规则如下:

  • 在初始状态下只能点击运行按钮。
  • 运行状态下可以点击暂停和停止按钮。
  • 暂停状态下可以点击运行和停止按钮。
  • 停止状态可以点运行按钮。
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);
	}

一步一步地深入浅出地实现状态机框架_第3张图片
OK,功能全部完美实现!!!




































状态图

好吧,如果你看到了这里,那你肯定觉得上面的实现不完美!对于上面的场景,我们可以用状态图来直观地表示,那么什么是状态图呢?客官请看,下面这个就是状态图:

一步一步地深入浅出地实现状态机框架_第4张图片
其中框框表示状态(小圆圈框框一般用来表示初始状态和终止状态),箭头表示触发条件。上面的状态图就可以很直观地表示了我们上面描述的规则了。直观的表示方法还有状态表,客官们感兴趣请个人自家查询相关资料。

原状态\目标状态 初始状态 运行 暂停 停止
初始状态 - 运行 - -
运行 - - 暂停 停止
暂停 - 运行 - 停止
停止 - 运行 - -

枚举实现

上面的代码不完美的首要一点是状态相关代码和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个问题(至少我觉得是):

  1. 模式与我们的工程耦合度太高,换一个项目需重新实现一套状态机模式。
  2. 存在重复代码,如PauseStateRunStatestop函数(虽然可以提升到基类中实现,但是基类会变得异常庞大)。
    现在让我们来进一步探究,首先再次回顾一下我的状态图:
    一步一步地深入浅出地实现状态机框架_第5张图片
    根据状态图,我们可以抽象出2个类:状态类和转换条件。为了维护状态之间的切换,我们还需要一个总体的协调类,我们分别命名为XStateXStateTransitionXStateMachine
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;

至此,我们状态机框架已圆满完成!!!





































。。。好吧,看来你还没走,那我们继续。。。

子状态

假设我们的状态图变成如下形式:
一步一步地深入浅出地实现状态机框架_第6张图片
也就是设备启动的时候处于未就绪状态,自检成功后处于就绪状态。就绪、运行和暂停都属于联机状态,在任意联机状态下发生故障回到未就绪状态。其中就绪、运行和暂停就是联机状态的子状态。当然可以不用子状态来表示,为每个联机状态的子状态添加转换条件是可以达到目的的,但是这不是我们风格,对吧?
为了我们的状态机框架能够支持子状态,我们需要调整我们的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





































** 别翻了,这次真的没有了!!! **

你可能感兴趣的:(C&C++,Qt)