Pattern di progettazione dello stato: migliore implementazione per il mio caso?

1

Io e il mio compagno abbiamo una discussione su quale sarebbe il modo migliore per implementare lo stato del pattern per la gestione di diversi schermi per un'applicazione che stiamo sviluppando.

Abbiamo uno ScreenManager (immagino la classe Context secondo alcuni libri) che assomiglia a questo (un codice irrilevante è stato rimosso per facilitare la comprensione):

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);
    }
}

Come puoi vedere, lo screenmanager ha un elenco di GameScreens che vengono aggiornati ogni volta che viene chiamato il metodo Update.

Quindi qui abbiamo un paio di schermate + classe Screen base. Alla classe base, creiamo un accoppiamento tra GameScreen e ScreenManager, mentre esiste anche un accoppiamento tra ScreenManager e 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
}

Come puoi vedere, TitleScreen sa su PlayingScreen, lo crea e lo aggiunge a ScreenManager (creando una dipendenza tra TitleScreen e PlayingScreen). Sebbene questo tipo di implementazione sia stato visto nei libri (per quanto ho letto), il mio compagno mi propone un altro tipo di implementazione: propone di creare un approccio Observer, sostituendo in TitleScreen l'intera gestione di PlayingScreen con un evento, che quindi essere recepito da ScreenManager, quindi ScreenManager orchestrerà tutta la gestione degli schermi.

Con questo approccio, dice che TitleScreen può essere riutilizzato in altre applicazioni e disaccoppiare schermate tra di loro, ma creare una grande classe ScreenManager che sia accoppiata con tutti.

Non sono sicuro che l'approccio statale o l'approccio Observer siano migliori per il mio caso specifico. Quale sarebbe la migliore implementazione?

Grazie

    
posta David Jiménez Martínez 02.12.2013 - 15:37
fonte

1 risposta

3

Sono d'accordo con Hans che l'osservatore può rendere la manutenzione più da incubo. Quello che posso suggerire è separare la preoccupazione (delegando la responsabilità) di aprire lo schermo di gioco in un'altra classe, per seguire Single Responsibility Principle .

Attualmente, la tua classe del titolo ha almeno 2 responsabilità, ovvero:

  • rendering della pagina del titolo e dei menu
  • aggiungi schermata relativa al menu selezionato
  • (gestire l'input come i tasti freccia, ecc.)

La responsabilità della schermata di aggiunta può essere delegata a un'altra classe. Diciamo 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 ());
        }
    }
}

Quindi nel tuo TitleScreen , puoi avere un'istanza di TitleAddScreenHandler o iniettata nel costruttore. Limiterà la dipendenza solo a una classe.

    
risposta data 03.12.2013 - 03:16
fonte

Leggi altre domande sui tag