Hello,筆者最近工作被領導要求寫了一個狀態機,說實在的,筆者之前從來沒有寫過狀態機(越做工作越發現自己越菜),所以不得已找了一些網上的一些資料,發現其實狀態機也有特定的設計模式的,所以我想針對我這個項目來聊一聊狀態機這個事情。說實在的,這個事情看起來不太大,但是里面要注意的東西真心不少,因此,筆者決定分成兩篇博客來寫,這篇博客主要針對狀態模式以及我的項目來初步的了解狀態機怎么寫,之后的文章就聊一聊項目中應該注意的點以及狀態機寫的時候應該注意些什么。
一、狀態機是什么
二、項目背景大致說明
三、狀態機設計思想和圖
四、狀態模式和策略模式的區別
一、狀態機是什么?
所謂狀態機,就是當一個對象狀態轉換的條件表達式過於復雜的時候,把狀態的判斷邏輯轉換到不同狀態的一系列類當中去。這樣解釋可能有點抽象,我們舉一個簡單的例子,我們以電梯為例,電梯可以分成開門,關門,上升/下落,停止這五個部分。首先我們要明確兩點,就是首先這五種狀態在同一時間只能出現一個,其次,這五種狀態在滿足某種條件后是可以相互轉換的,比如下落到某樓層后就會進入停止狀態,那么這也是狀態機使用的兩個前提,第一,在某段時間內只准許出現一種狀態,第二,這些狀態在滿足某些條件后是可以相互轉換的。
二、項目背景大致說明
這個項目背景大概是這樣的,當前我們有四個狀態,分別為IDLE狀態,PREPARE狀態,PROCESSING狀態,POSTPONE狀態,其中每種狀態的切換依賴於兩種條件(因為這里涉及到工作隱私,所以就不寫那么詳細了),另外,PREPARE狀態,PROCESSING狀態,POSTPONE狀態如果持續一段時間,那么他們就會退回IDLE狀態,大致情況就是這樣,詳情請看圖片。
三、狀態機的設計思想和類圖
我的設計思想是這樣的:首先整體分成六個類,我們姑且把這些類稱為stateManager,IDLE,PREPARE,PROCESSING以及POSTPONE以及FSMInterface類。其中stateManager類作為管理類,分別管理狀態的遷移以及傳輸判斷條件,而FSMInterface類作為接口類,作為IDLE,PREPARE,PROCESSING和POSTPONE的父類。其具體的類圖如圖所示。
我們從做向右看這個類圖,SensorManager中的sensorstate,infraredstate,curIndex,startFlag這四個成員變量都是判斷條件所依賴的全局變量,s則表示當前所處的狀態,一旦有條件改變狀態時,我們只要調用setstate函數就可以改變函數了。另外我們來看SensorManager中的調用函數handle函數,handle函數用來調用state中的handle,利用多態的方法來調用狀態機的處理函數。它的時序圖如圖所示。
其代碼如圖所示:
// 外部條件改變stateManager的全局變量
class stateManager
{
private:
int m_sensorState;
int curIndex;
int startFlag;
// std::vector<uint32_t> m_infraredState;
public:
void setStartFlag(int value);
int getStartFlag();
void setSensorState(int state);
void setInfraredState(uint32_t index, int state);
bool getSensorState();
stateManager();
virtual ~stateManager();
void setState(State *value);
void handle();
void setCurIndex(int value);
int getCurIndex();
std::shared_ptr<State> current; //當前狀態
std::map<uint32_t, int> m_infraredState;
};
void stateManager::handle()
{
current->handle(this);
}
void Idle::handle(stateManager *m)
{
// init
m->setSensorState(false);
m->setCurIndex(-1);
m->m_infraredState.clear();
// init end
LOG_INFO("Idle::handle is {0},signalState is {1}", __LINE__, IDLENAME);
emit signalState(IDLENAME);
// sensor changed
if (m->getSensorState() == 1) {
LOG_INFO("------the sensormanager set up,Idle::handle is {0}", __LINE__);
DefaultClient::getInstance()->getRfidReaderManager()->startInventory((DOORLOGICID));
m->setStartFlag(1);
m->current = std::make_shared<Prepare>();
m->handle();
}
// infrared changed
int flag = 0;
for (auto iter : m->m_infraredState) {
if (iter.second == 1) {
m->setCurIndex(iter.first);
flag = 1;
break;
}
}
if (flag == 1) {
DefaultClient::getInstance()->getRfidReaderManager()->startInventory((DOORLOGICID));
m->setStartFlag(1);
m->current = std::make_shared<Processing>();
m->handle();
}
}
我們重點關注一下stateManager::handle()函數,當每一次有狀態切換時,我們調用這個函數,在這個函數,我們找到當前current指針,調用相應的handle函數,調用的同時我們把this指針傳進去(因為我們要根據里面的變量限定條件)
。之后再IDLE::HANDLE內部中,一旦狀態改變,我們就可以修改current的值了。這也是設計模式中狀態模式的基本用法。
四、策略模式和狀態模式的總結
在設計模式當中,還有一種模式和狀態模式很像,就是策略模式。兩者的共同點在於都是由一個管理類來完成總結,都是以類來傳遞。但是策略模式一般來說是在初始化的時候就決定好了算法,而狀態模式是在運行過程中出現的,兩者
是不同的。
總結:這個算法其實是一個最簡單的狀態機,這個代碼我之后被上司強行改了5遍,至於具體的原因我會在下一篇博客中做說明。