当前位置: 动力学知识库 > 问答 > 编程问答 >

oop - Avoiding spaghetti code (gamestatemanager)

问题描述:

I am writing a StateManager for a game (I'm using C++ and SFML, but I tried to hide language specific elements because this is a question about any OOP language).

I have a setup in which a StateManager updates the current active state. However, a State must be able to change the active state (e.g. pressing "Play" in a menu starts a PlayingState), so I keep a reference to the StateManager in my State class.

Here's an UML diagram to make things more clear.

As you see, StateManager and State both reference eachother.

How can I avoid spaghetticode? Should I make the StateManager a singleton class? While we're at it, should the game class be a singleton class? I could easily do this, but I don't really like other classes in my game being able to access the game class or statemanager class, even if I am the only programmer.

网友答案:

You need to design in contracts also known as interfaces. For example, a state (in principle) does not need access to the state machine. The implementation of a state may need access though. Example in 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)
    {
    }
}

Notice Any implementation of IState is unaware of any IStateMachine implementation, they just work on contracts. Also notice that MenuState is not concerned about where 'IStateMachine' comes from (Inversion of Control), it just uses it.

You could add in another function into IStateMachine for GetStates() if you needed to.

Ultimately, you use these contracts to avoid coupling; which means you can completely replace the implementation of IStateManager and (assuming that adheres to the contract), MenuState will still work fine.

网友答案:

There's nothing wrong with bidirectional references. Look at Qt's parent/child model for QWidgets. If each State can only affect one StateManager, make each State take a pointer to a StateManager "parent" in its construction, or set it later. A StateManager can keep track of its "children" in a collection, and each child knows who its parent is, so it can inform that parent of any changes.

Edit: I think the first thing to look at would be to break your State class up. Currently it represents both a state and an action to change into that state. Actions should have knowledge of a StateMachine, to tell it to change states. States should be internal to the StateMachine.

分享给朋友:
您可能感兴趣的文章:
随机阅读: