【发布时间】:2013-10-04 18:36:14
【问题描述】:
我曾经实现过这样的状态机:
class Player
{
public:
int Run();
int Jump();
int Stop();
private:
class State
{
public:
virtual int Run() = 0;
virtual int Jump() = 0;
virtual int Stop() = 0;
};
class StandingState : public State
{
virtual int Run() { /*...*/ }
virtual int Jump() { /*...*/ }
virtual int Stop() { /*...*/ }
};
class RunningState : public State
{
virtual int Run() { /*...*/ }
virtual int Jump() { /*...*/ }
virtual int Stop() { /*...*/ }
};
// More states go here!
std::list<State*> states;
State* currentState;
};
int Player::Run()
{
int result = m_currentState->Run();
// do something with result
}
int Player::Jump()
{
int result = m_currentState->Jump();
// do something with result
}
int Player::Stop()
{
int result = m_currentState->Stop();
// do something with result
}
我认为是相当教科书:Player 将来自外部的调用委托给其当前的State 对象,并对结果进行处理(可能转换到另一个状态)。本质上,每个状态都知道给定动作如何影响它,但由状态机将各种状态连接在一起。我发现这是一个很好的关注点分离。
但我在这里看到了抽象的可能性。整个系统由State类的接口定义:
- 状态机和子状态都实现
State - 状态机保存指向所有可能的
States 和当前State的指针 - 无论在状态机上调用
State的任何方法,它都会被无差别地转发到当前状态。
所以,我们完全可以把它变成一个类模板,对吧?看:
template< class StateInterface >
class StateMachine : public StateInterface
{
// public methods already declared in StateInterface
protected:
std::list<StateInterface*> states;
void AddState(StateInterface* state);
StateInterface* currentState;
};
class PlayerStateInterface
{
public:
virtual int Run() = 0;
virtual int Jump() = 0;
virtual int Stop() = 0;
};
class Player : public StateMachine< PlayerStateInterface >
{
public:
virtual int Run() { currentState->Run(); /* do stuff */ }
virtual int Jump() { currentState->Jump(); /* do stuff */ }
virtual int Stop() { currentState->Stop(); /* do stuff */ }
};
在以上几点中,这包括了 1 和 2,但是 3 呢?我仍然必须手动将调用委托给具体状态机实现中的当前状态。有没有办法将该功能移至StateMachine 模板?当我不知道StateInterface 的方法的名称或签名时,我能否以某种方式表示,每当在StateMachine 上调用StateInterface 的方法时,它应该在currentState 上调用相同的方法?
【问题讨论】:
-
您可能有兴趣查看STTCL- 我正是使用该原理设计的。 (抱歉有些缺点:接口方法(事件),需要顶层和子状态机都知道)
-
我猜
StandingState和RunningState派生自第一个代码块中的State? -
@DyP:晕!很对,已编辑。谢谢。
-
目前还不清楚您到底要做什么。假装编译器可以理解任何人类程序员可以理解的任何东西,任何你想要的奇妙的语言结构(显然不是“为我解决这个问题”那种)。你的代码会是什么样子呢?请比“do stuff”更具体,因为不清楚“stuff”应该相同还是不同。
-
@n.m. “做事”部分并不重要。目前,我可以将具体的
StateMachine类转发到其状态对象的唯一方法是让它实现StateInterface的所有方法。我在问是否有办法让StateMachine模板本身进行呼叫转移。基本上,我想告诉编译器将StateMachine的每个方法M定义为M() { currentState->M(); }。或者至少是StateInterface中定义的那些方法。