Come implementare una classe NavigationManager senza utilizzare il modello singleton

1

Mi sto esercitando a scrivere codice verificabile. Un problema a cui ho partecipato (anche molto ricercato) è il modello singleton. La sua natura di stato globale lo rende inaffidabile da testare perché le modifiche alle proprietà di singleton vengono mantenute tra l'esecuzione di ciascun caso.

Anche così, Singleton è un modello di progettazione che viene utilizzato per risolvere i problemi che riguardano l'intera applicazione, ad esempio un NavigationManager .

Ecco una semplice implementazione di una classe del gestore di navigazione, la cui responsabilità è di tenere traccia della navigazione dell'utente attraverso l'app.

Per gli esempi mi piacerebbe che usiamo C# o Java .

class NavigationManager {
    private ICollection<INavigable> navigables; // all possible navigables views
    private Stack<INavigable> navigableStack; // the navigation stack
    private INavigable currentNavigable;  // the current view

    private NavigationManager() {
        navigables = new ICollection<INavigable>()
        navigables.Add( // register all posible navigables
    }

    static NavigationManager Instance {
        get {
            if(_instance == null ) { 
                  _instance = new NavigationManager(); 
            }
            return _instance 
        }
    }
    private static NavigationManager _instance;

    void navigateTo(INavigable n) { //.. code to change view
}

È chiaro che la classe dovrebbe essere un singleton in modo che possiamo navigare tra le visualizzazioni con navigateTo() , quale alternativa a Singleton è possibile ottenere?

    
posta Christopher Francisco 04.07.2015 - 05:00
fonte

1 risposta

4

Non è perché una classe dovrebbe avere una sola istanza in una particolare applicazione che devi usare un modello singleton. Questa è la chiave per risolvere questo problema.

Semplicemente istanziare un'istanza della classe una sola volta e poi passarla ad altre classi che ne hanno bisogno mantiene ancora il singleton come un concetto - solo un'istanza - ma non ha lo svantaggio nel reparto testing e porta a un codice più bello in generale perché non tutto dipende da un singleton. Per fare tutto questo, se necessario, puoi anche fare in modo che ogni cosa prenda un'interfaccia invece di eliminare la dipendenza da un'implementazione concreta. Il che a sua volta migliora ulteriormente la testabilità (a seconda del linguaggio e degli strumenti utilizzati, ma la maggior parte funziona meglio con le interfacce). Letture consigliate: Inversione di dipendenza, inversione del controllo.

In una piccola app che passa attorno a quell'istanza può essere eseguita "manualmente" tramite argomenti. Nelle app di grandi dimensioni che diventano noiose ed è qui che Iniezione di dipendenza entra nell'immagine, implementata quella che viene chiamata "Inversione del controllo" ' struttura. È molto utile entrare nei dettagli qui, quindi controlla alcuni tutorial su particolari framework.

Per darti un'idea approssimativa di come risolve il tuo problema, ecco un esempio di C # che usa MEF che apprezzo personalmente perché usa gli attributi. Per prima cosa non hai più bisogno del metodo di istanza statica. Inoltre, lo fai implementare un'interfaccia. Ciò si traduce in un codice come

interface INavigationManager
{
  void NavigateTo( INavigable n );
}

[Export( typeof( INavigationManager) )]
class NavigationManager : INavigationManager
{
  //....current implementation
}

class DoSomeNavigating
{
  private readonly INavigationManager navMan;

  [ImportingConstructor]
  DoSomeNavigating( INavigationManager navMan )
  {
    this.navMan = navMan;
  }

  public void Go( INavigable n )
  {
    navMan.NavigateTo( n );
  }
}

Prima il test: non c'è più dipendenza da NavigationManager e supponiamo che tu usi il framework di derisione Moq per scrivere test come

[Test]
void DoSomeNavigating_NavigatesToSomething()
{
  var navMan = new Mock<INavigationManager>();
  var navigable = new Mock<INavigable>();
  navMan.Setup( m => m.NavigateTo( navigable ) );

  new DoSomeNavigating( navMan.Object ).Go( navigable.Object );

  mock.VerifyAll(); //check that navMan.NavigateTo( navigable ) was called
}

Quando hai bisogno di DoSomeNavigating nell'applicazione stessa, chiedi a MEF di creare un'istanza e recuperare automaticamente un'istanza di NavigationManager. Dato che quest'ultimo è contrassegnato solo da Esporta, MEF sa che dovrebbe creare solo un'istanza di esso in modo che si prenda cura del concetto di singleton per te. Poiché il primo è contrassegnato con ImportingConstructor MEF sa che vuole un'istanza di qualcosa che implementa INavigationManager. Che in questo caso sarà l'istanza di NavigationManager.

var doNav = container.GetExportedValue<DoSomeNavigating>();
    
risposta data 04.07.2015 - 10:45
fonte

Leggi altre domande sui tag