Existe-t-il une raison de ne pas utiliser IoC comme référentiel de paramètres général?

StackOverflow https://stackoverflow.com/questions/115039

  •  02-07-2019
  •  | 
  •  

Question

Supposons que la classe ApplicationSettings soit un référentiel général de paramètres qui s'appliquent à mon application, tels que TimeoutPeriod, DefaultUnitOfMeasure, HistoryWindowSize, etc. ... Et disons que MyClass utilise l'un de ces paramètres - DefaultUnitOfMeasure.

Mon interprétation de l'utilisation correcte de Inversion of Control Containers - et corrigez-moi si je me trompe - est que vous définissez les dépendances d'une classe dans son constructeur:

public class MyClass {
  public MyClass(IDataSource ds, UnitOfMeasure default_uom) {...}
} 

puis appelez instanciez votre classe avec quelque chose comme

var mc = IoC.Container.Resolve<MyClass>();

IDataSource a été affectée à une implémentation concrète et default_uom a été configuré pour instancier à partir de la propriété ApplicationSettings.DefaultUnitOfMeasure . Je me demande cependant si tous ces obstacles sont vraiment nécessaires pour passer à travers. Pour quel problème dois-je me préparer si je devais faire

public class MyClass {
  public MyClass(IDataSource ds) {
    UnitOfMeasure duom = IoC.Container.Resolve<UnitOfMeasure>("default_uom");
  }
} 

Oui, beaucoup de mes classes se retrouvent avec une dépendance sur IoC.Container , mais il s'agit de toute façon d'une dépendance de la plupart de mes classes. Il semble que je devrais peut-être en tirer le meilleur parti tant que les classes sont couplées. S'il vous plaît, gourous agiles, dites-moi où je me trompe.

Était-ce utile?

La solution

  

IoC.Container.Resolve ("default_uom");

Je vois cela comme un anti-motif classique, dans lequel vous utilisez le conteneur IoC en tant que localisateur de services. Les principaux problèmes qui en résultent sont les suivants:

  • Votre application n'échoue plus rapidement si votre conteneur est mal configuré (vous ne le saurez que la première fois qu'il tentera de résoudre ce service particulier dans le code, ce qui pourrait ne pas se produire, sauf pour un ensemble spécifique de logique / circonstances) .
  • Plus difficile à tester - pas impossible bien sûr, mais vous devez soit créer une instance réelle (et semi-configurée) du conteneur windsor pour vos tests, soit injecter au singleton une maquette d'IWindsorContainer - cela ajoute beaucoup de friction à tester, comparé au fait de pouvoir passer les services simulés / stub directement dans votre classe sous test via constructors / properties.
  • Plus difficile de gérer ce type d'application (la configuration n'est pas centralisée à un seul emplacement)
  • Enfreint un certain nombre d'autres principes de développement logiciel (DRY, SOC, etc.)

La partie importante de votre déclaration initiale est l’implication que la plupart de vos classes auront une dépendance sur votre singleton IoC - s’ils obtiennent tous les services injectés via des constructeurs / dépendances, un couplage étroit avec IoC devrait alors être associé. l'exception à la règle - En général, la seule fois où je crée une dépendance sur le conteneur, c'est lorsque je fais quelque chose de délicat, c'est-à-dire que j'essaie d'éviter les problèmes de dépendance circulaire ou que je souhaite créer des composants à l'exécution pour une raison quelconque, et même alors, je peux souvent éviter de dépendre d'une interface autre qu'une interface générique IServiceProvider, ce qui me permet de permuter dans une implémentation IoC ou Service Locater maison si je dois réutiliser les composants dans un environnement en dehors du projet d'origine.

Autres conseils

Je n'ai généralement pas beaucoup de classes en fonction de mon conteneur IoC. J'essaie généralement d'envelopper la substance IoC dans un objet de façade que j'injecte dans d'autres classes, mais la plupart de mes injections IoC ne sont généralement effectuées que dans les couches supérieures de mon application.

Si vous faites les choses à votre façon, vous ne pourrez pas tester MyClass sans créer de configuration IoC pour vos tests. Cela rendra vos tests plus difficiles à maintenir.

Un autre problème est que vous allez avoir des utilisateurs intensifs de votre logiciel qui veulent changer la configuration en modifiant vos fichiers de configuration IoC. C'est quelque chose que je voudrais éviter. Vous pouvez diviser votre configuration IoC en un fichier de configuration normal et les éléments spécifiques à IoC. Mais vous pouvez tout aussi bien utiliser la fonctionnalité normale de configuration .Net pour lire la configuration.

  

Oui, beaucoup de mes classes se retrouvent avec une dépendance sur IoC.Container mais c'est une dépendance que la plupart de mes classes auront de toute façon.

Je pense que c'est le noeud de la question. Si, en réalité, la plupart de vos classes sont couplées au conteneur IoC, vous devrez probablement repenser votre conception.

En règle générale, votre application ne doit faire référence à la classe de conteneur directement qu'une seule fois lors du démarrage. Une fois ce premier crochet accroché au conteneur, le reste du graphique d’objet doit être entièrement géré par le conteneur et tous ces objets doivent être inconscients du fait qu’ils ont été créés par un conteneur IoC.

Pour commenter votre exemple spécifique:

public class MyClass {
    public MyClass(IDataSource ds) {
        UnitOfMeasure duom = IoC.Container.Resolve<UnitOfMeasure>("default_uom");
    }
}

Cela rend plus difficile la réutilisation de votre classe. Plus précisément, il est plus difficile d’instancier votre classe en dehors du modèle d’utilisation étroit auquel vous le limitez. Un des endroits les plus communs que cela se manifeste est le fait d’essayer de tester votre classe. Il est beaucoup plus facile de tester cette classe si UnitOfMeasure peut être passé directement au constructeur.

En outre, le choix du nom de l'instance UOM ("default_uom") implique que la valeur peut être remplacée, en fonction de l'utilisation de la classe. Dans ce cas, vous ne voudriez pas "coder en dur". la valeur dans le constructeur comme ça.

L'utilisation du modèle d'injection du constructeur ne rend pas votre classe dépendante de l'IoC, bien au contraire, elle donne aux clients la possibilité d'utiliser ou non l'IoC.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top