Les deux modèles semblent être une mise en œuvre du principe d’inversion de contrôle. C'est-à-dire qu'un objet ne doit pas savoir comment construire ses dépendances.
L'injection de dépendance (ID) semble utiliser un constructeur ou un configurateur pour "injecter" ses dépendances.
Exemple d'utilisation de l'injection de constructeur:
//Foo Needs an IBar
public class Foo
{
private IBar bar;
public Foo(IBar bar)
{
this.bar = bar;
}
//...
}
Service Locator semble utiliser un "conteneur", qui connecte ses dépendances et donne foo it bar.
Exemple d'utilisation d'un service de localisation:
//Foo Needs an IBar
public class Foo
{
private IBar bar;
public Foo()
{
this.bar = Container.Get<IBar>();
}
//...
}
Comme nos dépendances ne sont que des objets eux-mêmes, ces dépendances ont des dépendances, qui ont encore plus de dépendances, et ainsi de suite. Ainsi, l'Inversion of Control Container (ou DI Container) était né. Exemples: Castle Windsor, Ninject, Structure Map, Spring, etc.)
Mais un conteneur IOC/DI ressemble exactement à un localisateur de service. Est-ce appeler un conteneur DI une mauvaise réputation? Un conteneur IOC/DI est-il juste un autre localisateur de type type ? La nuance dans le fait que nous utilisons des conteneurs DI principalement lorsque nous avons de nombreuses dépendances?
La différence peut sembler légère, mais même avec ServiceLocator, la classe est toujours responsable de la création de ses dépendances. Il utilise simplement le localisateur de service pour le faire. Avec DI, la classe reçoit ses dépendances. Il ne sait pas, ni se soucie d'où ils viennent. Un résultat important est que l'exemple de DI est beaucoup plus facile à tester à l'unité, car vous pouvez lui passer des implémentations factices de ses objets dépendants. Vous pouvez combiner les deux et injecter le localisateur de service (ou une usine), si vous le souhaitez.
Lorsque vous utilisez un localisateur de services, chaque classe dépendra de votre localisateur de services. Ce n'est pas le cas avec l'injection de dépendance. L'injecteur de dépendance n'est généralement appelé qu'une fois au démarrage pour injecter des dépendances dans une classe principale. Les classes dont dépend cette classe principale auront leurs dépendances injectées de manière récursive, jusqu'à obtenir un graphe d'objet complet.
Une bonne comparaison: http://martinfowler.com/articles/injection.html
Si votre injecteur de dépendance ressemble à un localisateur de service, où les classes appellent directement l'injecteur, il ne s'agit probablement pas d'un injecteur de dépendance, mais d'un localisateur de service.
Les localisateurs de service cachent des dépendances - vous ne pouvez pas savoir en regardant un objet s'il frappe ou non une base de données (par exemple) lorsqu'il obtient des connexions à partir d'un localisateur. Avec l'injection de dépendance (au moins l'injection du constructeur), les dépendances sont explicites.
De plus, les localisateurs de services interrompent l’encapsulation car ils fournissent un point d’accès global aux dépendances d’autres objets. Avec service locator, comme avec n'importe quel singleton :
il devient difficile de spécifier le pré et post conditions pour l'objet client interface, parce que le fonctionnement de son la mise en œuvre peut être gênée par de dehors.
Avec l'injection de dépendance, une fois que les dépendances d'un objet sont spécifiées, elles sont sous le contrôle de l'objet lui-même.
Martin Fowler déclare:
Avec le service de localisation, la classe d’application le demande explicitement par un message au localisateur. Avec l'injection, il n'y a pas de demande explicite, le service apparaît dans la classe d'application - d'où l'inversion de contrôle.
En bref: Service Locator et Dependency Injection ne sont que des implémentations du principe d'inversion de dépendance.
Le principe important est «dépendez des abstractions, pas des concrétions». Cela rendra la conception de votre logiciel «faiblement couplée», «extensible», «flexible».
Vous pouvez utiliser celui qui correspond le mieux à vos besoins. Pour une grosse application ayant une base de code énorme, vous feriez mieux d'utiliser un localisateur de service, car l'injection de dépendance nécessiterait davantage de modifications de votre base de code.
Vous pouvez consulter ce message: Inversion de dépendance: localisateur de service ou injection de dépendance
Aussi le classique: Inversion des conteneurs de contrôle et le modèle d’injection de dépendance de Martin Fowler
Designing Reusable Classes par Ralph E. Johnson & Brian Foote
Cependant, celui qui m'a ouvert les yeux était: ASP.NET MVC: Résoudre ou injecter? C’est le problème… de Dino Esposito
Une classe utilisant le constructeur DI indique au code consommateur qu'il existe des dépendances à satisfaire. Si la classe utilise la SL en interne pour extraire de telles dépendances, le code consommateur n'est pas au courant de ces dépendances. Cela peut sembler meilleur en apparence, mais il est utile de connaître les dépendances explicites. C'est mieux d'un point de vue architectural. Et lors des tests, vous devez savoir si une classe a besoin de certaines dépendances et configurer le SL pour fournir de fausses versions appropriées de ces dépendances. Avec DI, il suffit de passer dans les faux. Pas une énorme différence, mais c'est là.
DI et SL peuvent cependant travailler ensemble. Il est utile d’avoir un emplacement central pour les dépendances communes (paramètres, enregistreur, etc.). Avec une classe utilisant de tels dépôts, vous pouvez créer un constructeur "réel" qui reçoit les dépôts et un constructeur par défaut (sans paramètre) qui extrait du SL et les transmet au constructeur "réel".
EDIT: et, bien sûr, lorsque vous utilisez le SL, vous introduisez un couplage à ce composant. Ce qui est ironique, car l’idée d’une telle fonctionnalité est d’encourager les abstractions et de réduire le couplage. Les préoccupations peuvent être équilibrées et cela dépend du nombre d'emplacements nécessaires pour utiliser la SL. Si cela est fait comme suggéré ci-dessus, juste dans le constructeur de classe par défaut.
Dans mon dernier projet, j'utilise les deux. J'utilise l'injection de dépendance pour la testabilité à l'unité. J'utilise service locator pour masquer l'implémentation et être dépendant de mon conteneur IoC. et oui! Une fois que vous utilisez l'un des conteneurs IoC (Unity, Ninject, Windsor Castle), vous en dépendez. Et une fois qu'il est obsolète ou pour une raison quelconque, si vous souhaitez l'échanger, vous devrez/devrez peut-être modifier votre implémentation - au moins la racine de la composition. Mais le localisateur de services fait abstraction de cette phase.
Comment vous ne dépendez pas de votre conteneur IoC? Vous devrez soit envelopper le vôtre (ce qui est une mauvaise idée), soit utiliser Service Locator pour configurer votre conteneur IoC. Donc, vous direz à service de localisation de quelle interface vous avez besoin, et il appellera le conteneur IoC configuré pour extraire cette interface.
Dans mon cas, j'utilise ServiceLocator qui est un composant du framework. Et utilisez Unity pour le conteneur IoC. Si, à l'avenir, j'ai besoin de permuter mon conteneur IoC en Ninject tout ce que j'ai à faire est de configurer mon localisateur de services de manière à utiliser Ninject au lieu de Unity. Migration facile.
Voici un excellent article qui explique ce scénario: http://www.johandekoning.nl/index.php/2013/03/03/dont-wrap-your-ioc-container/
Je pense que les deux travaillent ensemble.
L'injection de dépendance signifie que vous insérez une classe/interface dépendante dans une classe consommatrice (généralement son constructeur). Cela permet de découpler les deux classes via une interface et signifie que la classe consommatrice peut travailler avec de nombreux types d'implémentations de "dépendance injectée".
Le rôle du localisateur de service est de rassembler votre implémentation. Vous configurez un service de localisation via un cerclage de démarrage au début de votre programme. L'amorçage est le processus consistant à associer un type d'implémentation à un résumé/interface particulier. Ce qui est créé pour vous au moment de l'exécution. (basé sur vous config ou bootstrap). Si vous n'aviez pas implémenté l'injection de dépendance, il serait très difficile d'utiliser un localisateur de service ou un conteneur IOC.
Une raison d’ajouter, inspirée d’une mise à jour de la documentation que nous avons écrite pour le projet MEF la semaine dernière (j’aide à construire MEF).
Une fois qu'une application est potentiellement composée de milliers de composants, il peut être difficile de déterminer si un composant particulier peut être instancié correctement. Par "instancié correctement", je veux dire que dans cet exemple basé sur le composant Foo
, une instance de IBar
et sera disponible, et que le composant qui le fournit va:
Dans le deuxième exemple que vous avez donné, où le constructeur va au conteneur IoC pour récupérer ses dépendances, le seul moyen de tester qu'une instance de Foo
pourra être instanciée correctement avec la configuration d'exécution réelle de votre application consiste à réellement le construire.
Cela a toutes sortes d'effets secondaires gênants au moment du test, car un code qui fonctionnera au moment de l'exécution ne fonctionnera pas nécessairement avec un faisceau de test. Les simulacres ne suffiront pas, car la configuration réelle est la chose à tester, pas une configuration de test.
La racine de ce problème est la différence déjà soulignée par @Jon: l'injection de dépendances via le constructeur est déclarative, tandis que la deuxième version utilise le modèle impératif Service Locator.
Un conteneur IoC, utilisé avec précaution, peut analyser de manière statique la configuration d'exécution de votre application sans créer réellement d'instance des composants impliqués. De nombreux conteneurs populaires fournissent une certaine variation de ceci; Microsoft.Composition, qui est la version de MEF ciblant les applications de style Web et Metro .NET 4.5, fournit un exemple CompositionAssert
dans la documentation du wiki. En l'utilisant, vous pouvez écrire du code comme:
// Whatever you use at runtime to configure the container
var container = CreateContainer();
CompositionAssert.CanExportSingle<Foo>(container);
(Voir cet exemple ).
En vérifiant les Composition Roots de votre application au moment du test, vous pouvez potentiellement détecter certaines erreurs qui risquent sinon de passer à travers les tests plus tard dans le processus.
J'espère que c'est un ajout intéressant à cet ensemble de réponses complet sur le sujet!
Les deux sont des techniques de mise en œuvre d'IoC. Il existe également d'autres modèles qui implémentent Inversion of Control:
Le localisateur de services et le DI semblent plus similaires, les deux utilisent un conteneur pour définir les dépendances, ce qui mappe l'abstraction à la mise en œuvre concrète.
La principale différence est la manière dont les dépendances sont localisées. Dans Service Location, le code client demande les dépendances. Dans DI, nous utilisons un conteneur pour créer tous les objets et injecte la dépendance en tant que paramètre (ou propriété) du constructeur.
Dans ce cas trop simpliste, il n'y a pas de différence et ils peuvent être utilisés de manière interchangeable. Cependant, les problèmes du monde réel ne sont pas aussi simples. Supposons simplement que la classe Bar a elle-même une autre dépendance nommée D. Dans ce cas, votre localisateur de service ne serait pas en mesure de résoudre cette dépendance et vous auriez à l'instancier dans la classe D; car il est de la responsabilité de vos classes d’instancier leurs dépendances. Cela empirerait même si la classe D elle-même avait d'autres dépendances et dans des situations réelles, cela devenait généralement encore plus compliqué que cela. Dans de tels scénarios, DI est une meilleure solution que ServiceLocator.
Remarque: je ne réponds pas exactement à la question. Mais j’estime que cela peut être utile aux nouveaux apprenants du modèle d’injection de dépendance qui sont déroutés par le modèle de localisateur de service (anti) qui se produit trébucher sur cette page.
Je connais la différence entre le modèle de localisateur de services (il semble être considéré maintenant comme un anti-modèle) et les modèles d'injection de dépendance et je peux comprendre des exemples concrets pour chaque modèle. Pourtant, des exemples montrant un localisateur de services à l'intérieur du constructeur me déroutaient (supposons que nous faire l'injection du constructeur).
"Service Locator" est souvent utilisé à la fois comme nom d'un motif et comme nom pour désigner l'objet (également supposer) utilisé dans ce motif pour obtenir des objets sans utiliser l'opérateur new. Maintenant, ce même type d'objet peut également être utilisé à la racine de la composition pour effectuer une injection de dépendance, et c'est là que la confusion entre en scène.
Il est à noter que vous pouvez utiliser un objet de localisateur de services dans un constructeur DI, mais que vous n'utilisez pas le "modèle de localisateur de services". Il est moins déroutant de le considérer comme un objet conteneur IoC, car vous avez peut-être deviné qu’il fait essentiellement la même chose (corrigez-moi si je me trompe).
Qu'il s'agisse d'un localisateur de service (ou simplement d'un localisateur), ou d'un conteneur IoC (ou simplement d'un conteneur), cela ne fait aucune différence, vous vous en doutez, car ils font probablement référence à la même abstraction (corrigez-moi si je me trompe) ). C’est simplement qu’appeler cela un localisateur de service suggère que l’on utilise l’anti-motif Service Locator avec le motif d’injection de dépendance.
IMHO, le nommant un "localisateur" au lieu de "emplacement" ou "localisation", peut également amener à penser parfois que le localisateur de service dans un article fait référence au conteneur Service Locator, et non au modèle (anti) de localisateur de service , en particulier lorsqu'il existe un modèle associé appelé Injection de dépendance et non Injecteur de dépendance.
Quelle est la différence (le cas échéant) entre l’injection de dépendance et le localisateur de service? Les deux modèles sont efficaces pour la mise en œuvre du principe d'inversion de dépendance. Le modèle Service Locator est plus facile à utiliser dans une base de code existante car il rend la conception globale plus souple sans imposer de modifications à l'interface publique. Pour cette même raison, le code basé sur le modèle Service Locator est moins lisible que le code équivalent basé sur Dependency Injection.
Le modèle d’injection de dépendance indique clairement depuis la signature quelles dépendances une classe (ou une méthode) va avoir. Pour cette raison, le code résultant est plus propre et plus lisible.