【问题标题】:Avoiding spaghetti code (gamestatemanager)避免意大利面条式代码(gamestatemanager)
【发布时间】:2013-04-02 09:25:35
【问题描述】:

我正在为游戏编写 StateManager(我正在使用 C++ 和 SFML,但我试图隐藏特定于语言的元素,因为这是关于任何 OOP 语言的问题)。 我有一个设置,其中 StateManager 更新当前的活动状态。但是,State 必须能够更改活动状态(例如,在菜单中按“Play”会启动 PlayingState),因此我在我的 State 类中保留对 StateManager 的引用。

这是一个让事情更清楚的 UML 图。

如您所见,StateManager 和 State 都相互引用。 我怎样才能避免意大利面条代码?我应该将 StateManager 设为单例类吗?当我们这样做时,游戏类应该是单例类吗?我可以轻松做到这一点,但我真的不喜欢我的游戏中的其他类能够访问游戏类或 statemanager 类,即使我是唯一的程序员。

【问题讨论】:

  • 你能把State也需要访问的部分分开吗?即从更简单的合约(接口)组成 StateManager?即具有更新和渲染的 IRenderable。那么你只给State这部分的StateManager。此外,您还想将此 IRenderable 注入状态(即查看控制反转)。
  • 我不确定你的意思。你能举一个这样的例子吗?你的意思是,例如会有另一个类使用 changeState 方法,并且那个 State 有对这个类的引用,但对 StateManager 没有?

标签: oop coding-style


【解决方案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 仍然可以正常工作。

【讨论】:

  • 如果我有一个隐式合同(我是唯一的程序员,可能不会发布我的源代码),说 StateManager 应该有一个 ChangeState 方法,那也可以吗?在这种情况下,使用 contracts 除了简洁的设计还有其他好处吗?
  • 很多人在评论中提到,但一句话:decoupling。基本上,你想要它,这样你就可以替换合同的实施并且它不会破坏任何东西;即MenuStateIStateManager 合约所做的事情仍然有效。简而言之就是这样。
【解决方案2】:

双向引用没有错。查看 Qt 的 QWidgets 的父/子模型。如果每个 State 只能影响一个 StateManager,则让每个 State 在其构造中获取一个指向 StateManager“父级”的指针,或者稍后设置它。 StateManager 可以在集合中跟踪它的“子代”,每个子代都知道它的父代是谁,因此它可以通知该父代任何更改。

编辑: 我认为首先要看的是打破你的州级。目前,它既代表一种状态,也代表一种转变为该状态的动作。 Actions 应该知道 StateMachine,告诉它改变状态。状态应该在 StateMachine 内部。

【讨论】:

  • 我认为它有问题,因为我们必须为大学设计一个应用程序,他们告诉我们这是最糟糕的设计。
  • 嗯,理想情况下,你会使用某种基于事件的系统,这样 State 就可以向世界尖叫“嘿,我改变了一些东西!”,并且 StateManager 知道要听这些事件并做出回应。这样一来,State 就不需要对 StateManager 或外部世界有任何了解。它只是说“嘿,任何关心的人,用户希望你现在玩。只是说'。”并留在那里。如果合适的话,由其他班级来倾听和回应。不过,我认为这远远超出了您的设计范围。
  • 我知道这将是一个很好的系统。但是,它确实超出了我正在设计的范围。我希望 StateManager 尽可能小,因为可能只有几个状态(可能是 3 或 4 个)。如果它只使用一次并且不会做很多事情,我不想让代码过于复杂。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-16
相关资源
最近更新 更多