[英]How to enforce coupling polymorphic type and enum value?
我有一台狀態機,該狀態機將某種消息(例如文本)發送到外部接收器。 在轉換到此狀態之前(將其稱為Dispatching ),先前的狀態需要存儲該消息的位置,以便Dispatching可以稍后再獲取它。 當消息在一個上下文中創建並在另一個上下文中使用時,它將在堆上創建,並且State Manager對象(用於管理狀態,轉換和事件循環)將保留對它的引用/指針。 狀態對象在狀態機穿越狀態時創建和銷毀。 每個狀態都繼承抽象基類State
:
enum StateID
{
STATE_A,
STATE_B,
...
};
class State
{
public:
State(StateID stateID, StateManager& sm) :
stateID_(stateID), sm(sm_){}
virtual ~State(){};
virtual StateID HandleEvent(const Event& e) = 0;
StateID id() const {return stateID_;}
protected:
StateID stateID_;
StateManager& sm_;
};
為了使將數據傳遞給下一個狀態通用,我想到了StateData的概念-從一個狀態傳遞到下一個狀態的一條信息。 它存儲在動態內存中,狀態管理器保留對其的引用,以便每個狀態都可以訪問它。 由於可能會將不同類型的數據傳遞給不同的狀態,因此可以將StateData做成抽象基類,專門用於每個特定狀態:
struct StateData
{
virtual ~StateData() = 0;
};
struct StateAData : public StateData
{
int n_;
StateAData(int n) : n_(n){}
};
struct StateBData : public StateData
{
std::string str_;
StateBData(const std::string& str) : str_(str){}
};
...
class StateManager
{
boost::scoped_ptr<State> pCurrState_;
boost::scoped_ptr<StateData> pStateData_;
...
public:
void runEventLoop()
{
while(true)
{
...
//get event from a queue
...
StateID nextStateID = pCurrState_->HandleEvent(e);
if(nextStateID == pCurrState_->id())
continue;
pCurrState_.reset(0);
switch(nextStateID)
{
case STATE_A:
pCurrState_.reset(new StateA(*this));
break;
case STATE_B:
pCurrState_.reset(new StateB(*this));
break;
case STATE_C:
pCurrState_.reset(new StateC(*this));
break;
...
}
}
}
...
};
class StateA
{
public:
StateA(StateManager& sm) : State(STATE_A, sm){}
StateID HandleEvent(const Event& e)
{
switch(e.ID)
{
case EVENT_1:
{
StateAData* pData = reinterpret_cast<StateAData*>(stateMachine_.pStateData_.get());
// do something with data, e.g. use it for transition logic
if(pData->n_ % 2)
{
stateMachine_.pStateData_.reset(new StateBData("Hello from StateA"));
return STATE_B;
}
else
{
...
}
break;
}
...
}
}
...
}
以下幾行有一個陷阱:
stateMachine_.pStateData_.reset(new StateBData("Hello from StateA"));
return STATE_B;
如果轉換邏輯發生變化,那么從此處開始,我們需要轉到STATE_C
,開發人員可能會忘記將StateBData
的類型StateBData
為StateCData
:
stateMachine_.pStateData_.reset(new StateBData("Hello from StateA"));
return STATE_C;
...這會在StateC
嘗試將StateData
StateCData
為StateCData
時導致不良行為。
如何避免這種情況? 如何強制匹配所創建對象的類型和返回的枚舉值?
是的,這段代碼很臭,這是使用enum
信息並使用enum
來區分狀態類型而不是類型本身的結果。 HandleEvent
可以返回StateXData
並根據此返回的類型(因為它包含有關下一個狀態的信息),狀態管理器將確定(通過使用RTTI)下一個要轉換為( X
)的狀態,但是我不喜歡這種解決方案。
另一個想法是創建下一個狀態的實例並將其數據傳遞到其構造函數中,但是這種方法會污染狀態機設計,因為在銷毀前一個狀態之前會創建一個狀態。
將enum
作為基類的一部分,或在基類中提供純virtual
函數以返回此enum
。 這樣,狀態本身將通告其類型。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.