程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 使用C++實現一套簡單的狀態機模型——原理解析

使用C++實現一套簡單的狀態機模型——原理解析

編輯:C++入門知識

使用C++實現一套簡單的狀態機模型——原理解析


在上一文中,我們介紹了該狀態機模型的使用方法。通過例子,我們發現可以使用該模型快速構建滿足基本業務需求的狀態機。本文我們將解析該模型的基礎代碼,以便大家可以根據自己狀態機特點進行修改。(轉載請指明出於breaksoftware的csdn博客)

該模板庫的基礎方法實現在之後給出的工程的AutoStateChart.h中,該文件一共215行,其中有16行是輔助調試代碼。以上一文中狀態機類為例:

class CMachine_Download_Run_App :
    public AutoStateChart::CAutoStateChartMachine
CMachine_Download_Run_App類繼承於模板類CAutoStateChartMachine,該模板類有兩個參數:繼承類自身和CStoreofMachine。CStoreofMachine類顧名思義,其是狀態機中用於存儲數據的類。為什麼要設計這樣的類?因為在我們的狀態機模型中,每個基礎狀態都是割裂的。這樣設計的一個好處便是我們可以讓每個基礎狀態的邏輯代碼獨立,和其他模塊沒有任何耦合。當我們要刪除某個狀態時,我們只要將它從狀態機的跳轉聲明中摘除即可。當我們要新增某個狀態時,我們也只要在狀態機跳轉中做相應聲明即可。但是往往優點也伴隨著缺點:它使得每個基礎狀態類的數據交互產生了障礙。特別是沒有上下文關系的基礎狀態,跳躍性的傳遞信息將變得非常困難。於是我們就需要一個存活於整個狀態機聲明周期的“數據庫”,它可以被每個基礎狀態類訪問和修改。於是CStoreofMachine就應運而生。因為該類比較獨立,所以我們先從該類開始解析。首先我們看下該類的聲明:

#pragma once
#include "AutoStateChart.h"

#define PROPERTY(type,name)													\
public:																		\
	void Set##name(const type& n) {										    \
		m_##name = n;														\
	}																		\
	type Get##name() {														\
		return m_##name;													\
	}																		\
	__declspec(property(get = Get##name, put = Set##name)) type Prop##name;	\
private:																	\
	type m_##name;															\


class CStoreofMachine{
	PROPERTY(std::string, ValueString);
	PROPERTY(std::wstring, ValueWString);
    PROPERTY(int, ValueInt);
};
該類的寫法可能只適合於windows的vs平台,其他平台沒論證過。其實它的內容是非常簡單的,就是暴露成員變量的set和get方法。只是我覺得這種寫法比較有意思,才在這兒羅列下。
我們再看下該類在模板中的使用,我們先從最基礎的類開始解析

	class CEmpytLocalStore{};

	template
	class CLocalStoreAccess{
	public:
		typedef boost::function< Store& () > func;
		Store& GetStore(){return m_pFunc();};
		void SetStore(func& pCallback){m_pFunc = pCallback;};
	public:
		func m_pFunc;
	};
我們先定義了一個空類——CEmptyLocalStore,它相當於一個默認的“數據庫”。當模板的使用者不需要“數據庫”時,就可以在模板中不聲明“數據庫”類,此時我們的CEmptyLocalStore就生效了。比如我們上例的狀態機可以改成:

class CMachine_Download_Run_App :
    public AutoStateChart::CAutoStateChartMachine
CLocalStoreAccess類主要提供如下作用:

設置訪問“數據庫”類對象的方法——SetStore 獲取“數據庫”類對象——GetStore 成員變量m_pFunc是一個函數指針,用於獲取“數據庫”類對象。該變量將由CLoaclStoreAccess繼承類設置,相當於CLocalStoreAccess暴露了設置訪問“數據庫”類對象的能力。而它並不保存“數據庫”類對象——它只提供“訪問”能力,而不提供“存儲”能力。
	template
	class CLocalStoreBase:
		public boost::enable_shared_from_this>,
		public CLocalStoreAccess {
	public:
		void Init(){ func pfunc = boost::bind(&CLocalStoreBase::_GetStore, shared_from_this()); SetStore(pfunc);};
	private:
		Store& _GetStore(){return m_Store;};
	private:
		Store m_Store;
	};
CLoaclStoreBase類的私有成員變量m_Store就是“數據庫”類對象,即該類提供了“存儲”功能。它繼承於CLoaclStoreAccess類,使得該類具備了訪問數據庫的能力——雖然它的私有方法可以訪問“數據庫”類對象,但是我還是希望將這些能力分開。因為之後介紹的基礎狀態類要有“訪問”的能力,而不應該具備“存儲”的能力。如果不將這些能力進行拆分,將會導致層次結構混亂。
CLoaclStoreBase類的init方法,打通了和ClocalStoreAccess的關系——設置函數指針。
介紹完用於存儲上下文的模板類後,我們現在可以關注下狀態機相關的類了。我們先看上一文中一個基礎狀態類的例子
class CSimpleState_Download_From_A :
    public AutoStateChart::CAutoStateChartBase
CSimpleState_Download_From_A類繼承於CAutoStateChartBase模板類。第一個模板參數是繼承類自身,第二個是它所屬的狀態機,第三個是“數據庫”類。我們在看下CAutoStateChartBase類的聲明

	template
	class CAutoStateChartBase:
		public boost::enable_shared_from_this>,
		public CLocalStoreAccess
	{
		BOOST_TYPEOF_REGISTER_TYPE(T)
	public:
		std::string GetCurrentState(){ return typeid(T).name();};
		bool IsCompositeStates(){return false;};
		void SetInitState( const std::string& strState ){};

	public:
		virtual void Entry(){}; 
		virtual std::string Exit(){return "";};
	};
該模板類使用第一個模板參數類的類名作為其繼承類的狀態,並使用GetCurrentState方法提供獲取功能。比如上例中的狀態名為class CSimpleState_Download_From_A。這個模板類繼承於CLocalStoreAccess模板類,使得繼承類具有可以“訪問”第三個模板參數類——“數據庫”類的能力——不具備“存儲”能力。同時該類還暴露了兩個方法——Entry和Exit,他們分別用於在進出該狀態時,讓狀態機調用。
狀態和存儲類都介紹完了,我們就剩下調度狀態變化的狀態機類和復合狀態類。其實從某種程度上說,復合狀態是一種簡單的狀態機,它們在很多地方存在共性。我們從狀態機類入口,進行講解。首先看下上一文中的例子
class CMachine_Download_Run_App :
    public AutoStateChart::CAutoStateChartMachine
狀態機類需要繼承於CAutoStateChartMachine模板類,該類聲明如下:
	template
	class CAutoStateChartMachine:
		public boost::enable_shared_from_this>,
		public CLocalStoreAccess
	{
	public:
		typedef LocalStore SelfStore;
		typedef T Self;
	public:
		CAutoStateChartMachine(){m_spStore.reset();};
		virtual ~CAutoStateChartMachine(){};
	private:
		virtual bool Transition(){return false;};
	public:
		void StartMachine() {
			if ( !m_spStore ) {
				m_spStore = boost::make_shared>();
				m_spStore->Init();
				SetStore( m_spStore->m_pFunc );
			}
			while( Transition()){};
		};
	private:
		void Init(){};
	public:
		bool IsCompositeStates(){return false;};
	protected:
		std::string m_strCurrentState;
		std::string m_strCondition;
		MapString m_MapCompositeStatesSubState;
		boost::shared_ptr> m_spStore;
	};
我們先看下這個類的成員變量。m_strCurrentState保存了狀態機在跳轉中的當前狀態,m_strCondition保存了狀態機中當前狀態之前的狀態的輸出,它用於決定狀態跳轉方向。m_MapCompositeStatesSubState用於保存狀態機中離開復合狀態時的最後狀態,即它記錄復合狀態機的淺歷史。m_spStore指向“數據庫”類對象。
該模板類最重要的函數就是StartMachine,它在第一次被調用時創建了“數據庫”類對象。然後死循環調用Transition方法。Tansition方法是個虛方法,它是整個模型的核心。狀態機類需要實現自己的Transition方法,以使得狀態機可以運轉起來。

我們再看下復合狀態類的基礎模板

	template
	class CCompositeStates:
		public CAutoStateChartBase{
		BOOST_TYPEOF_REGISTER_TYPE(T)
	public:
		CCompositeStates(){};
		~CCompositeStates(){};
	private:
		virtual bool Transition(){return false;};
	public:
		virtual void Entry(){while(Transition());};
		virtual std::string Exit(){return m_strCondition;};
	public:
		std::string GetCurrentState(){return m_strCurrentState;};
		bool IsCompositeStates(){return true;};
		void SetInitState( const std::string& strState ){ m_strCurrentState = strState; };
	protected:
		std::string m_strCurrentState;
		std::string m_strCondition;
		MapString m_MapCompositeStatesSubState;
	};
因為復合狀態也是一種狀態,所以它也要有Entry和Exit兩種方法。而其Entry方法就是調用Transition方法。該模板類的Transition方法也是虛方法,這意味著繼承於該模板類的方法也要去實現Transition。
於是所有的重心都集中於Transition方法的實現。
為了讓代碼美觀,我參考了MFC中使用宏簡潔代碼的思路,設計了如下的宏:
#define STARTSTATE(state)														\
	do {																		\
		boost::shared_ptr sp = boost::make_shared();				\
		sp->SetStore( m_pFunc );												\
		if ( sp->IsCompositeStates() ) {										\
			std::string strState = typeid(state).name();						\
			BOOST_AUTO(it, m_MapCompositeStatesSubState.find(strState));		\
			if ( m_MapCompositeStatesSubState.end() != it ) {					\
				sp->SetInitState(it->second);									\
				if ( DEBUGFRAMEFLAG ) {											\
					std::string strInitState = it->second;						\
					std::cout<<"CompositeStates SetInitState:"<Entry();															\
		m_strCondition = sp->Exit();											\
		if ( sp->IsCompositeStates() ) {										\
			std::string strState = typeid(state).name();						\
			std::string strInnerState = sp->GetCurrentState();					\
			m_MapCompositeStatesSubState[strState] = strInnerState;				\
			if ( DEBUGFRAMEFLAG ) {												\
				std::cout<<"CompositeStates SaveState:"<        然後復合狀態類和狀態機類只要使用這些宏去組織狀態跳轉,就可以清晰的描述過程了。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved