我正在为游戏编写StateManager(我正在使用C ++和SFML,但我试图隐藏特定于语言的元素,因为这是关于任何OOP语言的问题)。 我有一个设置,其中StateManager更新当前活动状态。但是,状态必须能够更改活动状态(例如,在菜单中按“播放”启动播放状态),因此我在State类中保留对StateManager的引用。
这是一个使事情更清晰的UML图。
如您所见,StateManager和State都互相参考。 我怎样才能避免spaghetticode?我应该将StateManager设为单例类吗?虽然我们在这里,但游戏类应该是单身类吗?我可以很容易地做到这一点,但我真的不喜欢我的游戏中的其他类能够访问游戏类或州级管理员类,即使我是唯一的程序员。
答案 0 :(得分:1)
双向引用没有任何问题。查看Qt的QWidgets的父/子模型。如果每个State只能影响一个StateManager,则使每个State在其构造中获取指向StateManager“parent”的指针,或稍后进行设置。 StateManager可以跟踪集合中的“子节点”,并且每个子节点都知道其父节点是谁,因此它可以通知父节点有任何更改。
编辑: 我认为首先要看的是打破国家级。目前,它代表状态和改变为该状态的行动。动作应该了解StateMachine,告诉它改变状态。国家应该是StateMachine的内部。
答案 1 :(得分:1)
您需要设计合同,也称为接口。例如,状态(原则上)不需要访问状态机。但是,状态的实现可能需要访问。 C#中的示例:
public interface IState
{
void Render();
void Update();
}
public interface IStateMachine
{
void ChangeState(IState newState);
}
public class MenuState : IState
{
private IStateMachine _stateMachine;
public MenuState(IStateMachine stateMachine)
{
_stateMachine = stateMachine;
}
public void Render()
{
}
public void Update()
{
}
}
public class StateMachineImplementation : IStateMachine
{
public void ChangeState(IState newState)
{
}
}
注意IState
的任何实现都不知道任何IStateMachine
实现,它们只是在合同上工作。另请注意MenuState
并不关心'IStateMachine'来自何处(控制反转),它只是使用它。
如果需要,您可以为IStateMachine
添加另一个功能GetStates()
。
最终,您使用这些合同来避免耦合;这意味着你可以完全取代IStateManager
的实现和(假设遵守合同),MenuState
仍然可以正常工作。