有限狀態機(使用狀態模式C++實現)

   最近在研究怪物的AI,由於怪物的AI是使用有限狀態機來實現的。所以就查看關於有限狀態機的東西。根據這幾天的查看資料,知道了有限狀態機是計算機科學中一個很重要的概念。而且有限狀態機是一個抽象的概念,具體實現是多種多樣的。根據維基百科的介紹,它是這樣的一個概念:有限狀態機英語finite-state machine縮寫FSM)又稱有限狀態自動機,簡稱狀態機,是表示有限個狀態以及在這些狀態之間的轉移和動作等行爲的數學模型

  就像上面說的,既然是數學模型當然有很多的實現方法。其實最簡單容易想到的方法就是使用switch case的寫法:

swtich(case)
{
case STATE1:
        do something;
        break;
case STATE2:
        do something;
        break;
.......
}
  但是這種寫法會帶來很多的問題,首先是當狀態多的時候,switch case的語句會寫的很長,使得可讀性非常差。其次,可維護性也很差,每當要添加一個狀態轉換時,就要在Switch case添加相應的狀態轉換函數。會使得代碼變得冗長,難以擴展。
  當使用狀態模式寫狀態機的時候,每一個狀態就是一個類,添加狀態只需要添加一個狀態類就行了。在每一個類中實現相應的狀態轉換函數。
  使用狀態模式的好處:
  1 每一個狀態就是一個類,便於擴展,可讀性高
  2 每一個類有一個狀態轉換函數,使得整個代碼的層次結構,很清晰
  當然,沒有一個東西是完美的。使用狀態模式實現的時候,會使得類可能會變得很龐大。當總的來說,還是好處大於壞處的。畢竟代碼不僅是出產品,還要考慮這個代碼的擴展性還有可讀性。一個只實現了功能,但擴展性很差,或者是除了你沒有人看得懂,或者是很難看懂的代碼,絕不是好代碼。簡單高效纔是我們程序員追求的目標。
  一個很簡單的例子,仿照電梯的例子:
<pre style="color: rgb(37, 37, 37); margin-top: 0px; margin-bottom: 0px;"><pre style="margin-top: 0px; margin-bottom: 0px;"><span style=" color:#008000;">/************************************************************************</span>
        Des: 狀態機,負責狀態的裝換
************************************************************************/
#include "statemachine.h"
#include "StateDefine.h"

using namespace std;

StateMachine::StateMachine()
{
//起始的狀態爲停止狀態
    m_currentState = m_lastState = STOP_STATE;
}

StateMachine::~StateMachine()
{

}
/**
 * @brief 狀態機的初始化函數,負責把每一個狀態的類放入Map中進行管理
 */
void StateMachine::init()
{

    m_stateManager[0] = new CloseState();
    m_stateManager[1] = new OpenState();
    m_stateManager[2] = new UpState();
    m_stateManager[3] = new DownState();
    m_stateManager[4] = new StopState();
    (m_stateManager[4])->handle(this);
}
/**
 * @brief 狀態裝換函數,當需要切換狀態的時候,只需要調用此函數
 * @param stateId :每一個狀態都有一個相應的ID,定義在StateDefine的頭文件中
 */
void StateMachine::changeState(int stateId)
{
    if( stateId == m_currentState )
    {
        cout<<"curent state is "<<stateId<<"doesn't transfrom state"<<endl;
        return;
    }
    else
    {
        int temp = m_lastState;
        m_lastState = m_currentState;
        m_currentState = stateId;
       std::map<int,State*>::iterator it = m_stateManager.find(stateId);
       if( it != m_stateManager.end() )
       {
          if( !(it->second)->handle(this))
          {
             m_currentState = m_lastState;
             m_lastState = temp;
          }
       }
    }
}
/**
 * @brief 獲得上次的狀態
 * @return 返回狀態ID
 */
int StateMachine::getLastState()
{
    return m_lastState;
}
/**
 * @brief 獲得當前的狀態
 * @return 返回狀態ID
 */
int StateMachine::getState()
{
    return m_currentState;
}

然後是每一個狀態的類:

/************************************************************************
     Des: 下落的狀態
/************************************************************************/
#include "downstate.h"
#include "StateDefine.h"
#include "statemachine.h"
using namespace std;
DownState::DownState()
{
}
DownState::~DownState()
{
    cout<<"destructor downstate"<<endl;
}
/**
 * @brief 處理相應的狀態裝換
 * @param 傳入狀態機
 * @return 
 */
bool DownState::handle(StateMachine* p_machine)
{
    //判斷上次的狀態是否能正確切換到現在的狀態
    if( (p_machine->getLastState() != CLOSE_STATE) || (p_machine->getLastState() != STOP_STATE))
    {
        if(p_machine->getLastState() != CLOSE_STATE)
            cout<<"the lift must be close!"<<endl;
        else
            cout<<"the lift must be stoped"<<endl;
        return false;
    }
    cout<<"the lift is downing "<<endl;
    return true;
}
/************************************************************************
        Des:關閉的狀態
/************************************************************************/
#include "closestate.h"
#include "StateDefine.h"
#include "statemachine.h"
using namespace std;
CloseState::CloseState()
{
}
CloseState::~CloseState()
{
    cout<<"destructor closestate"<<endl;
}
bool CloseState::handle(StateMachine* p_machine)
{
    if( (p_machine->getLastState() != OPEN_STATE))
    {
        cout<<"the lift must be closed!"<<endl;
        return false;
    }
    cout<<"the lift is closing "<<endl;
    return true;
}
/************************************************************************
 *     Des: 開啓的狀態
/************************************************************************/
#include "openstate.h"
#include "StateDefine.h"
#include "statemachine.h"
using namespace std;
OpenState::OpenState()
{
}
OpenState::~OpenState()
{
}
bool OpenState::handle(StateMachine* p_machine)
{
    if( p_machine->getLastState() != STOP_STATE )
    {
        cout<<"the lift must be stop!"<<endl;
        return false;
    }
    cout<<"the lift is opening"<<endl;
    return true;
}
/************************************************************************
 *     Des: 停止的狀態
/************************************************************************/
#include "stopstate.h"
#include "statemachine.h"
#include "StateDefine.h"
using namespace std;
StopState::StopState()
{
}
StopState::~StopState()
{
}
bool StopState::handle(StateMachine* p_machine)
{
    if(p_machine->getLastState() == p_machine->getState())
    {
        cout<<"the lift is stop "<<endl;
        return true;
    }
    if( p_machine->getLastState() == STOP_STATE )
    {
        cout<<"the lift have already be stopping!"<<endl;
        return false;
    }
    cout<<"the lift is stop "<<endl;
    return true;
}
/************************************************************************
 *   Des: 上升的狀態 
/************************************************************************/
#include "upstate.h"
#include "StateDefine.h"
#include "statemachine.h"
using namespace std;
UpState::UpState()
{
}
UpState::~UpState()
{
    cout<<"destrucotor update"<<endl;
}
bool UpState::handle(StateMachine* p_machine)
{
    if( p_machine->getLastState() != CLOSE_STATE )
    {
        cout<<"the lift must be close!"<<endl;
        return false;
    }
    cout<<"the lift is uping"<<endl;
    return true;
}
 下面是公共的頭文件,定義了每一個狀態對應的枚舉值,方便管理
/************************************************************************
 *  Des: 定義一個枚舉,每一個狀態對於一個值
/************************************************************************/
#ifndef STATEDEFINE_H
#define STATEDEFINE_H
enum{
    CLOSE_STATE,
    OPEN_STATE,
    UP_STATE,
    DOWN_STATE,
    STOP_STATE,
};
#endif // STATEDEFINE_H
下面是每一個狀態的父類,使用了虛函數,使得對外的切換狀態的接口是統一的
/************************************************************************
 *  Des: 每一個狀態的父類,定義了虛函數(handle),每一個狀態都實現了這個函數,
 *       實現狀態的切換具體實現,多態的體現
/************************************************************************/
#ifndef STATE_H
#define STATE_H
class StateMachine;
class State
{
public:
    State();
    virtual ~State();
    virtual bool handle(StateMachine* p_machine) = 0; 
};
#endif // STATE_H
使用次狀態模式的具體類:
/************************************************************************
 *   Des: 電梯的具體類,負責實現電梯的具體邏輯
/************************************************************************/
#include "lift.h"
#include <iostream>
#include "StateDefine.h"
using namespace std;
Lift::Lift()
{
}
Lift::~Lift()
{
    delete this->m_stateMachine;
    this->m_stateMachine = nullptr;
}
/**
 * @brief 電梯的初始化,使用按鍵模擬電梯的行爲
 */
void Lift::init()
{
    this->m_stateMachine = new StateMachine();
    this->m_stateMachine->init();
    while (true) {
        cout<<"enter the value"<<endl;
        char str[8] = {0};
        cin>>str;
        if( !strcmp(str,"c"))
        {
            this->m_stateMachine->changeState(CLOSE_STATE);
        }
        else if( !strcmp(str,"o"))
        {
            this->m_stateMachine->changeState(OPEN_STATE);
        }
        else if( !strcmp(str,"u"))
        {
            this->m_stateMachine->changeState(UP_STATE);
        }
        else if( !strcmp(str,"d"))
        {
            this->m_stateMachine->changeState(DOWN_STATE);
        }
        else if( !strcmp(str,"s"))
        {
            this->m_stateMachine->changeState(STOP_STATE);
        }
    }
}
使用狀態模式確實使得使用避免過度的使用了IF ELSE 或者是Switch case,也使得整個的邏輯變得清晰可見。初探狀態模式,還有些不足,以後改進!!(_~ ~_)大笑


<pre style="margin-top: 0px; margin-bottom: 0px;">

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章