Gestione / classe contenitore rispetto ai metodi di classe statici

7

Supponiamo di avere una classe Widget che fa parte di un framework utilizzato in modo indipendente da molte applicazioni. Creo istanze di widget in molte situazioni e le loro vite variano. Oltre ai metodi specificati per l'istanza di Widget, mi piacerebbe essere in grado di eseguire le seguenti operazioni di classe:

  • Trova una singola istanza Widget basata su un ID univoco
  • Fai scorrere l'elenco di tutti i widget
  • Rimuovi un widget dall'insieme di tutti i widget

Per supportare queste operazioni, ho preso in considerazione due approcci:

  1. Classe contenitore : crea una classe container o manager, WidgetContainer, che contiene un elenco di tutte le istanze Widget, supporta l'iterazione e fornisce metodi per l'aggiunta, la rimozione e la ricerca di widget. Ad esempio in C #:

    public class WidgetContainer : IEnumerable&ltWidget>  
    {  
      public void AddWidget(Widget);  
      public Widget GetWidget(WidgetId id);  
      public void RemoveWidget(WidgetId id);  
    }  
    
  2. Metodi di classe statica : aggiungi metodi di classi statiche a Widget. Ad esempio:

    public class Widget
    {
      public Widget(WidgetId id);
    
      public static Widget GetWidget(WidgetId id);
      public static void RemoveWidget(WidgetId id);
      public static IEnumerable&ltWidget> AllWidgets();  
    }
    

L'uso di una classe contenitore ha il problema aggiunto di come accedere alla classe contenitore. Rendilo un singleton? .. yuck! Crea un oggetto World che fornisce l'accesso a tutte queste classi contenitore?

Ho visto molti framework che usano l'approccio della classe contenitore, quindi qual è il consenso generale?

    
posta Ben 02.11.2012 - 15:51
fonte

3 risposte

8

Il consenso moderno è che non dovresti farlo affatto.

Avere un progetto "tutte le istanze di questa classe" si è dimostrato problematico sotto molti aspetti, in primo luogo rispetto alla concorrenza. Vuoi tutti i widget o tutti i widget di proprietà del tuo thread? Vuoi davvero rendere tutto questo widget sicuro? Ricorda che i test di unità sono quasi sempre eseguiti in parallelo, quindi "la mia app è single threaded" potrebbe non essere sufficiente.

L'altro problema in cui ti imbatti è nel tuo progetto. Con la "sola e unica" lista di qualsiasi cosa, sia essa globale, statica o singleton ti imbatterai rapidamente in problemi quando ne hai bisogno più di uno. Incontrerai problemi con widget temporanei o widget fuori schermo se vengono aggiunti automaticamente all'elenco. Incontrerai errori in cui hai dimenticato di aggiungerli all'elenco se sono non gestiti automaticamente.

Una volta che non hai il requisito 'tutte le istanze di una classe', allora finisci con una semplice raccolta vecchia. Qualunque sia il modo in cui il codice consumato sta funzionando con i widget (anche se questo è un framework a sua volta consumato) può orchestrare come viene utilizzata la raccolta e come / quando i widget vengono inseriti. Gli stessi widget non dovrebbero preoccuparsene.

    
risposta data 02.11.2012 - 16:44
fonte
1

Questo sembra essere un buon candidato per l'utilizzo di un contenitore IoC (Inversion of Control). Invece di dover creare autonomamente il contenitore, basta fare affidamento su una parte contenitore generica di una libreria IoC che è possibile chiamare.

Ad esempio definiresti un'interfaccia:

public interface IWidgetHandler
{
   void AddWidget(IWidget widget);  
   IWidget GetWidget(int widgetId);  
   void RemoveWidget(int widgetId);
   IEnumerable<Widget> GetAllWidgets();
}

Quindi definisci una classe che implementa quell'interfaccia.

public class WidgetHandler : IWidgetHandler
{
    public void AddWidget(IWidget widget)
    {
        throw new NotImplementedException();
    }

    public IWidget GetWidget(int widgetId)
    {
        throw new NotImplementedException();
    }

    public void RemoveWidget(int widgetId)
    {
        throw new NotImplementedException();
    }


    public IEnumerable<Widget> GetAllWidgets()
    {
        throw new NotImplementedException();
    }
}

Registreresti come dovresti trovare le istanze di WidgetHandlers con un contenitore IoC.

Generalmente, un contenitore IoC ha una sorta di metodo di registrazione che consente di chiamare

MyIoCContainer.Instance.Register<IWidgetHandler, WidgetHandler>();

Ora ogni volta che hai bisogno di un gestore di widget devi dire a IoC di darti un'istanza di uno.

IWidgetHandler widgetHandler = MyIoCContainer.Instance.Resolve<IWidgetHandler>();

Il contenitore IoC può anche controllare la durata dei tuoi oggetti.

Dai un'occhiata alla documentazione di rinnovo se desideri ulteriori dettagli su come iniziare a utilizzare IoC. Ci sono anche alcune opzioni in più ( unity , autofac , Castle solo per nome alcune). Infatti esiste una libreria CommonServiceLocator che fornisce un'interfaccia comune per tutti, con il minimo comune denominatore di funzionalità utile (se si desidera scambiare tra di loro)

Consente al tuo codice di essere più testabile rispetto alle classi di metodi statici, dato che possono essere derisi quando un pezzo di codice dipende da un IWidgetHandler. Invece di servire un vero gestore di widget, IoC (o il framework di test) può servire un'istanza di un FakeWidgetHandler che può essere usato per testare il codice che dipende da questo modulo servendo varie risposte false al codice e affermando quel metodo i parametri vengono passati correttamente alla dipendenza IWidgetHandler.

Buona fortuna!

    
risposta data 02.11.2012 - 16:23
fonte
0

Non c'è assolutamente alcuna differenza tra avere un oggetto Singleton e usare metodi statici nella classe Widget. Widget.GetWidget e WidgetContainer.Instance.GetWidget portano esattamente gli stessi problemi.

Dove e in quale forma sono i tuoi widget quando l'applicazione non è in esecuzione? Ciò ti aiuterà a risolvere il tuo problema.

Se si trovano in un database o sul filesystem, non è necessario un oggetto a istanza singola per accedervi, ad esempio. Qualsiasi numero di oggetti WidgetContainer può ottenerli dal file system o dal database, indipendentemente. È possibile implementare un sistema di memorizzazione nella cache se le prestazioni sono un problema.

    
risposta data 02.11.2012 - 16:19
fonte

Leggi altre domande sui tag