State Design Pattern – Best Implementation for C#

cdesigndesign-patternsnetstate

Me and a mate have a discussion about what would be the best way to implement the pattern state for managing several screens for an application we are developing.

We have a ScreenManager (I guess the Context class according some books) which looks like this (some irrelevant code has been removed to ease understanding) :

public class ScreenManager : DrawableGameComponent
{

    #region Fields

    List<GameScreen> screens = new List<GameScreen> ();
    List<GameScreen> screensToUpdate = new List<GameScreen> ();

    InputState input = new InputState ();

    #endregion

    /// <summary>
    /// Constructs a new screen manager component.
    /// </summary>
    public ScreenManager (Game game)
        : base(game)
    {
        content = new ContentManager (game.Services, "Content");

        graphicsDeviceService = (IGraphicsDeviceService)game.Services.GetService (
                                                    typeof(IGraphicsDeviceService));

        if (graphicsDeviceService == null)
            throw new InvalidOperationException ("No graphics device service.");
    }

    public override void Update (GameTime gameTime)
    {
        // Read the keyboard and mouse.
        input.Update ();

        // Make a copy of the master screen list, to avoid confusion if
        // the process of updating one screen adds or removes others
        // (or it happens on another thread)
        screensToUpdate.Clear ();

        foreach (GameScreen screen in screens)
            screensToUpdate.Add (screen);

        while (screensToUpdate.Count > 0) {
            // Pop the topmost screen off the waiting list.
            GameScreen screen = screensToUpdate [screensToUpdate.Count - 1];

            screensToUpdate.RemoveAt (screensToUpdate.Count - 1);

            // Update the screen.
            screen.Update (gameTime);
            screen.HandleInput (input);
        }
    }

    public void AddScreen (GameScreen screen)
    {
        screen.ScreenManager = this;

        if ((graphicsDeviceService != null) &&
            (graphicsDeviceService.GraphicsDevice != null)) {
            screen.LoadContent ();
        }

        screens.Add (screen);
    }

    public void RemoveScreen (GameScreen screen)
    {
        screens.Remove (screen);
        screensToUpdate.Remove (screen);
    }
}

As you can see, the screenmanager has a list of GameScreens which are updated every time the Update method is called.

Then here we have a couple of screens + base Screen class. At the base class, we create a coupling between GameScreen and ScreenManager, while there also exist a coupling between ScreenManager and GameScreen:

public abstract class GameScreen
{
    ScreenManager screenManager;

    public ScreenManager ScreenManager {
        get { return screenManager; }
        internal set { screenManager = value; }
    }

    public virtual void HandleInput (InputState input)
    {
    }

    public virtual void LoadContent ()
    {
    }

    public virtual void UnloadContent ()
    {
    }

    public virtual void Update (GameTime gameTime)
    {
    }
}

public class TitleScreen : GameScreen
{
    public Texture2D titleScreen;

    public TitleScreen ()
    {
    }

    public override void HandleInput (InputState input)
    {
        if (input.MenuSelect) {
            ScreenManager.AddScreen (new PlayingScreen ());
    }

}


public class PlayingScreen : GameScreen
{
//...... Irrelevant implementation details for my question
}

As you can see there, TitleScreen knows about PlayingScreen, creates it and adds it to ScreenManager (creating a dependency between TitleScreen and PlayingScreen). Although this kind of implementation has been seen in books (as far as I've read), my companion proposes me another kind of implementation: He proposes to create an Observer approach, replacing in TitleScreen the whole PlayingScreen management with an event, which would then be suscribed by the ScreenManager, and then the ScreenManager would orchestrate all the management of screens.

With this approach, he says TitleScreen could be reused in other applications and decouple screens among them, but create a big ScreenManager class who would be coupled with everyone.

I'm not sure whether the State approach or the Observer approach is better for my specific case. Which would be the best implementation?

Thank you

Best Answer

I agree with Hans that observer can make the maintenance more nightmare. What I can suggest is separating the concern (delegating the responsibility) of opening playing screen into another class, to follow Single Responsibility Principle.

Currently, your title class has at least 2 responsibility, that is:

  • rendering the title page and menus
  • add screen related to menu selected
  • (handle input such as arrow keys, etc)?

The add screen responsibility can be delegated to another class. Let's say it TitleAddScreenHandler.

public class TitleAddScreenHandler{
    public TitleAddScreenHandler(ScreenManager screenManager){
        // property assignments
    }
    private ScreenManager screenManager;

    public void AddScreen(InputState input){
        if (input.MenuSelect) {
            screenManager.AddScreen (new PlayingScreen ());
        }
    }
}

Then in your TitleScreen, you can has one instance of TitleAddScreenHandler or injected in constructor. It will limit the dependency only to one class.