我认为实现状态机的一个好方法是使用单例模式。例如,它看起来像这样:
class A
{
private:
friend class State;
State* _state;
void change_state(State* state) { _state = state; }
};
class State
{
virtual void action(A *a) = 0;
private:
void change_state(A *a, State *state) { a->change_state(state); }
};
class StateA : public State
{
public:
static State* get_instance()
{
static State *state = new StateA;
return state;
}
virtual void action(A *a) { change_state(a, StateB::get_instance(); }
};
class StateB : public State
{
public:
...
virtual void action(A *a) { change_state(a, StateA::get_instance(); }
};
Run Code Online (Sandbox Code Playgroud)
我的问题是:我读过很多关于单例模式如此邪恶的文章。在没有单例模式的情况下实现这一点,每次更改状态时都必须调用 new,因此对于那些不喜欢单例模式的人,您将如何实现状态机模式?
我不认为单例模式在这里是合适的。单例适用于表示抽象实体或物理对象,它们实际上只有一个副本。从 Java 中窃取一个示例,只有一个运行时环境可以在其中执行程序的特定实例。单例很适合表示这些对象,因为它们使整个程序能够命名和引用它,同时保留封装并允许多个可能的后端。
鉴于此,我不同意单例是状态机的最佳途径。如果您确实将其实现为单例,则您是说它始终是该状态机的一个副本。但是如果我想让两个状态机并行运行呢?或者根本没有状态机?如果我想要我自己的本地状态机,以便我可以对其进行试验以查看它会发生什么情况,该怎么办?如果你的状态机是一个单例,我不能做任何这些事情,因为整个程序实际上只有一个状态机。
现在,取决于您如何使用状态机,也许它是合适的。如果状态机控制程序的整体执行,那么这可能是一个好主意。例如,如果您正在开发一个视频游戏并希望一个状态机来控制您是在菜单中,还是在聊天区中,还是在玩游戏,那么拥有一个单例状态机就完全没问题了,因为任何时候程序只有一种逻辑状态。但是,从您的问题中,我无法推断是否是这种情况。
至于如何在没有单例的情况下实现状态机,您可能希望让状态机对象为每个状态分配自己的副本并建立转换表(如果您需要显式状态对象),或者只需要一个巨大的 switch 语句和一个单一的枚举值控制你所处的状态。如果你有一个状态机的实例,这并不比当前版本低效率,如果你有多个实例,它允许你在每个状态中存储本地信息,而无需污染了程序其他部分可以读取的状态的全局副本。