2010-10-18 11 views
1

Nous utilisons StructureMap comme structure d'injection de dépendances (DI) pendant la création d'une bibliothèque en couches. Nos objectifs sont les suivants:Questions à propos de StructureMap dans une bibliothèque (en couches) concernant l'extensibilité et la testabilité

  • Facilitez-vous pour les classes de tests unitaires (et en utilisant des classes fictives au lieu des dépendances réelles) pendant que nous développons et maintenons la bibliothèque.
  • le rendre facile pour l'utilisateur de bibliothèque:
    • Personnaliser le comportement bibliothèque en mettant en œuvre une interface les selfs et la configuration de la bibliothèque d'utiliser leur mise en œuvre à la place de notre propre. Unité Tester leurs classes implémentées (c'est le même objectif que nous avons en interne, mais nous voulons qu'il soit également rempli pour l'utilisateur de la bibliothèque).

Par bibliothèque en couches, nous entendons que nous construisons deux DLL`s:

  1. La bibliothèque de base. Il devrait être utilisable à partir de n'importe quelle application .NET, que ce soit une application console ou MVC.
  2. Une bibliothèque WebForms. Il inclut la bibliothèque de base mais fournit également le contrôle de WebForms pour faciliter la vie des utilisateurs de WebForms.

Nos questions:

  1. Où devrions-nous appeler le code pour la carte des interfaces avec des classes concrètes pour la bibliothèque de base? Il n'a pas de point d'entrée unique, il existe de nombreuses classes qui peuvent être instanciées en premier.

    Actuellement, nous obligeons l'utilisateur à appeler DependencyHandler.Init() avant de faire un autre appel de bibliothèque. Existe-t-il un moyen plus agréable, comme un morceau de code qui est exécuté après avoir chargé la DLL afin que nous n'obligions pas l'utilisateur à écrire une ligne de code de plaque de chaudière?

  2. Quelle est la méthode recommandée pour permettre aux utilisateurs de bibliothèque de modifier une implémentation d'interface dans leur propre type? Actuellement, l'utilisateur peut récupérer le registre par StructureMap.Configuration.DSL.Registry theRegistry = DependencyHandler.Instance().GetConfiguration() puis modifier les choses par exemple theRegistry.For<IFoo>().Use<MyOwnFooImplementation>(); Serait-il plus agréable d'utiliser XML? Et si oui, où devrions-nous mettre ledit XML? Nous choisissons l'approche programmatique car Visual Studio pourra donner de l'aide à l'utilisateur. Si l'approche actuelle dans ci-dessus est utilisée, l'utilisateur de la bibliothèque est nécessaire (au moins maintenant) pour ajouter une référence à StructureMap.DLL ainsi qu'à notre DLL. Pouvons-nous supprimer ce fardeau de l'utilisateur, peut-être en utilisant XML pour l'installation de dépendances à la place?

  3. Existe-t-il un bon emplacement central dans WebForms que nous devrions utiliser, ce qui résout le problème pour la DLL de la bibliothèque WebForms?

  4. Comment recommandez-vous de structurer les choses pour la production et les essais? L'idée actuelle est d'utiliser DI dans tous les endroits nécessaires pour permettre le test unitaire où nous et nos utilisateurs le souhaitons, ainsi que de permettre aux utilisateurs de changer le comportement de la bibliothèque en fournissant leurs propres implémentations.

    Pour tester les choses, nous et les utilisateurs de la bibliothèque devront créer des classes fictives pour remplacer les dépendances que nous voulons dissocier. L'idée est que nous utilisons la configuration normale mais surcharger les classes que nous voulons mocker au lieu d'utiliser leur implémentation normale. Est-ce une bonne façon de procéder?

Répondre

2

Il est important de noter que pour fournir une API à partir de laquelle d'autres peuvent isoler leur code que vous devez vous assurer qu'ils peuvent compter sur des abstractions plutôt que des applications concrètes. C'est-à-dire, tant que les classes que vous exposez que "faire des choses" implémente une interface (habituellement meilleure), hérite d'une classe abstraite qui définit l'interface ou a tous ses membres publics comme virtuels (généralement pire). consommateur de votre API, est capable d'isoler mon code de vos implémentations concrètes. En d'autres termes, l'utilisation d'un conteneur IoC en soi n'est pas la clé pour fournir une API testable. Mais rendre vos classes non statiques et comme implémentations d'abstractions est. L'utilisation d'un conteneur présente de nombreux autres avantages. Par exemple, vous aurez probablement un temps beaucoup plus facile à tester les différentes classes de votre API vous :)

  1. Il n'y a pas de point d'entrée unique, mais d'utiliser votre API j'instancier une ou plusieurs classes non? Chacune de ces classes pourrait avoir des constructeurs qui demandent au client de fournir des implémentations concrètes des abstractions dont dépend la classe. Pour éviter de forcer les clients à spécifier les implémentations à utiliser, vous pouvez également fournir des constructeurs sans paramètre qui utilisent les implémentations par défaut.

  2. Cela dépend, mais n'importe quoi de votre approche à un simple DSL pour le câblage de l'API aux fichiers de configuration peut être bien. En général, cependant, comme je l'ai déjà dit, les clients ne seront probablement pas très intéressés à changer les implémentations dont dépendent vos classes tant qu'elles peuvent elles-mêmes isoler leur propre code de vos classes "de premier niveau".

  3. Le démarrage de l'application est probablement un bon endroit pour cela. La bibliothèque de formulaires Web peut être un HttpModule ou vous pouvez fournir un alignement de démarrage par défaut et indiquer aux clients d'apporter les modifications qu'ils doivent apporter dans l'événement de démarrage de l'application dans global.asax.

  4. Sonne bien. Comme je l'ai déjà dit, je pense que le plus important est que vos classes et leurs méthodes ne soient pas statiques (et de préférence pas singletons) et que leurs interfaces soient définies de façon abstraite, permettant ainsi aux utilisateurs d'utiliser d'autres implémentations concrètes. .

je pourrais avoir mal compris quelques petites choses, mais j'espère que mes réponses pourraient aider au moins un peu :)