C'est en fait simple à faire une fois que vous comprenez que l'ID concerne les modèles et les principes , pas la technologie.
Pour concevoir l'API de manière agnostique au conteneur DI, suivez ces principes généraux:
Programmer vers une interface, pas une implémentation
Ce principe est en fait une citation (de mémoire cependant) de Design Patterns , mais il devrait toujours être votre véritable objectif . L'ID n'est qu'un moyen d'atteindre cet objectif .
Appliquer le principe d'Hollywood
Le principe d'Hollywood en termes de DI dit: N'appelez pas le conteneur DI, il vous appellera .
Ne demandez jamais directement une dépendance en appelant un conteneur depuis votre code. Demandez-le implicitement en utilisant l' injection de constructeur .
Utiliser l'injection de constructeur
Lorsque vous avez besoin d'une dépendance, demandez-la de manière statique via le constructeur:
public class Service : IService
{
private readonly ISomeDependency dep;
public Service(ISomeDependency dep)
{
if (dep == null)
{
throw new ArgumentNullException("dep");
}
this.dep = dep;
}
public ISomeDependency Dependency
{
get { return this.dep; }
}
}
Remarquez comment la classe Service garantit ses invariants. Une fois qu'une instance est créée, la dépendance est garantie d'être disponible en raison de la combinaison de la clause Guard et du readonly
mot clé.
Utilisez Abstract Factory si vous avez besoin d'un objet éphémère
Les dépendances injectées avec Constructor Injection ont généralement une longue durée de vie, mais parfois vous avez besoin d'un objet à courte durée de vie, ou pour construire la dépendance en fonction d'une valeur connue uniquement au moment de l'exécution.
Voir ceci pour plus d'informations.
Composez uniquement au dernier moment responsable
Gardez les objets découplés jusqu'à la fin. Normalement, vous pouvez attendre et tout câbler dans le point d'entrée de l'application. C'est ce qu'on appelle la racine de composition .
Plus de détails ici:
Simplifiez l'utilisation d'une façade
Si vous pensez que l'API résultante devient trop complexe pour les utilisateurs novices, vous pouvez toujours fournir quelques classes Facade qui encapsulent les combinaisons de dépendances courantes.
Pour fournir une façade flexible avec un haut degré de découverte, vous pouvez envisager de fournir des constructeurs fluides. Quelque chose comme ça:
public class MyFacade
{
private IMyDependency dep;
public MyFacade()
{
this.dep = new DefaultDependency();
}
public MyFacade WithDependency(IMyDependency dependency)
{
this.dep = dependency;
return this;
}
public Foo CreateFoo()
{
return new Foo(this.dep);
}
}
Cela permettrait à un utilisateur de créer un Foo par défaut en écrivant
var foo = new MyFacade().CreateFoo();
Il serait cependant très découvrable qu'il est possible de fournir une dépendance personnalisée, et vous pourriez écrire
var foo = new MyFacade().WithDependency(new CustomDependency()).CreateFoo();
Si vous imaginez que la classe MyFacade encapsule de nombreuses dépendances différentes, j'espère qu'il est clair comment elle fournirait les valeurs par défaut appropriées tout en rendant l'extensibilité détectable.
FWIW, longtemps après avoir écrit cette réponse, j'ai développé les concepts ici et écrit un article de blog plus long sur les bibliothèques DI-Friendly , et un article compagnon sur les cadres DI-Friendly .