web-dev-qa-db-fra.com

Manager / Classe de conteneur VS Méthodes de classe statique

Supposons que j'ai une classe de widget faisant partie d'un cadre utilisé indépendamment par de nombreuses applications. Je crée des instances de widget dans de nombreuses situations et leur vie varie. En plus des méthodes spécifiées d'instance de Widget, j'aimerais pouvoir effectuer les opérations de suivi de la classe suivantes:

  • Trouvez une instance de widget unique en fonction d'un identifiant unique
  • Itérer sur la liste de tous les widgets
  • Supprimer un widget de l'ensemble de tous les widgets

Pour soutenir ces opérations, j'ai envisagé deux approches:

  1. Classe de conteneurs - Créez une classe de conteneur ou de gestionnaire, WidgetContainer, qui détient une liste de toutes les instances de widget, prenant en charge l'itération et fournit des méthodes d'addition de widget, de retrait et de recherche. Par exemple en C #:

     Classe publique WidgetContainer: iEnumerable & ltwidget> [.____] {
     annulation publique addwidget (widget); 
     widget public getwidget (ID Widgetid); 
     Public Void RemoveWidget (ID Widgetid); 
    } 
    
  2. Méthodes de classe statique - Ajoutez des méthodes de classe statique au widget. Par exemple:

     widget de la classe publique [.____] {
     widget public (ID Widgetid); 
     
     widget statique public getwidget (widgetid id); Public statique Void RemoveWidget (ID WidgetID); 
     Public statique ienumerable & ltwidget> Allwidgets (); 
    } 
    

L'utilisation d'une classe de conteneurs a le problème supplémentaire de l'accès à la classe de conteneurs. Faites-le un singleton? .. yuck! Créez-vous un objet mondial qui donne accès à toutes ces classes de conteneurs?

J'ai vu de nombreux cadres qui utilisent l'approche de la classe de conteneurs, alors quel est le consensus général?

7
Ben

Le consensus moderne est que vous ne devriez pas le faire du tout.

Avoir une "conception de tous les cas de cette classe" s'est avéré gênant dans de nombreux aspects, le principal étant en ce qui concerne la concurrence. Voulez-vous tous les widgets ou tous les widgets appartenant à votre fil? Voulez-vous vraiment faire que toutes les listes de widgets threadsafe? N'oubliez pas que les tests d'unités sont presque toujours faits en parallèle, donc "mon application est un seul fileté" pourrait ne pas être suffisamment bon.

L'autre problème que vous rencontrez est dans votre conception. Avec la liste des "une seule et unique" de quoi que ce soit, que ce soit global, statique ou singleton, vous rencontrerez rapidement des problèmes lorsque vous en avez besoin de plus d'un. Vous rencontrerez des problèmes avec des widgets temporaires ou des widgets hors écran si elles sont automatiquement ajoutées à la liste. Vous rencontrerez des erreurs où vous avez oublié de les ajouter à la liste s'ils sont non traité automatiquement.

Une fois que vous n'avez pas eu les "toutes les cas d'une classe", vous vous retrouvez avec une seule collection unique. Quel que soit le code de consommation qui travaille avec les widgets (même si c'est un cadre qui est consommé à son tour) peut orchestrer comment la collection est utilisée et comment/lorsque les widgets sont placés là-bas. Les widgets eux-mêmes ne devraient pas s'en soucier.

8
Telastyn

Cela semble être un bon candidat à utiliser un conteneur IOC (inversion de contrôle). Au lieu de devoir créer vous-même le conteneur vous-même, vous comptez simplement sur une partie de conteneur générique d'une bibliothèque de CIO que vous pouvez appeler.

Par exemple, vous définissez une interface:

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

Ensuite, vous définissez une classe qui implémente cette interface.

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

Vous allez enregistrer la manière dont vous devriez trouver des instances de widgethandlers avec un conteneur IOC.

Généralement, un conteneur IOC a une sorte de méthode d'enregistrement afin que vous puissiez appeler

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

Maintenant, chaque fois que vous avez besoin d'un gestionnaire de widget, vous devez simplement dire au COO de vous donner une instance d'une.

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

Le conteneur COI peut également contrôler la durée de vie de vos objets.

Jetez un coup d'œil à la Documentation NINJECT Si vous souhaitez plus de détails sur la mise au point de CIO. Il existe également quelques options supplémentaires (- nity , Autofac , châtea juste pour en nommer quelques-uns). En fait, il existe une bibliothèque COMMONServiceLocator fournissant une interface commune pour tous, avec le dénominateur commun le plus bas de fonctionnalité utile (si vous souhaitez échanger entre eux)

Il permet à votre code d'être plus testable par rapport aux classes de méthodes statiques, car elles peuvent être moquées lorsqu'un morceau de code dépend d'un iWidGetHandler. Au lieu de servir un véritable gestionnaire de widget, le CIO (ou le cadre de test) peut servir une instance d'un fauxWidDGetherdler qui peut être utilisée pour tester le code qui dépend de ce module en servant diverses fausses réponses au code et affirmant que cette méthode Les paramètres sont correctement passés à la dépendance IWIDGETHANDLER.

Bonne chance!

1
Anastasiosyal

Il n'y a absolument aucune différence entre avoir un objet singleton et utiliser des méthodes statiques sur la classe widget. Widget.getwidget et widgetContainer.instance.getwidget porter exactement les mêmes problèmes.

Où et sous quelle forme sont vos widgets lorsque l'application n'est pas en cours d'exécution? Cela aidera à résoudre votre problème.

S'ils sont dans une base de données ou sur le système de fichiers, vous n'avez pas besoin d'un objet à instance pour y accéder, par exemple. N'importe quel nombre d'objets WidgetContainer peuvent les obtenir à partir du système de fichiers ou de la base de données, indépendamment. Vous pouvez mettre en œuvre un système de mise en cache si la performance est votre préoccupation.

0
pdr