Les deux modèles semblent être une mise en œuvre du principe d'inversion de contrôle. Autrement dit, un objet ne doit pas savoir comment construire ses dépendances.
L'injection de dépendance (DI) semble utiliser un constructeur ou un setter 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;
}
//...
}
Le localisateur de service semble utiliser un "conteneur", qui connecte ses dépendances et donne foo c'est bar.
Exemple d'utilisation d'un localisateur de service:
//Foo Needs an IBar
public class Foo
{
private IBar bar;
public Foo()
{
this.bar = Container.Get<IBar>();
}
//...
}
Parce que 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) est né. Exemples: Castle Windsor, Ninject, Structure Map, Spring, etc.)
Mais un conteneur IOC / DI ressemble exactement à un localisateur de service. L'appeler un conteneur DI est-il un mauvais nom? Un conteneur IOC / DI n'est-il qu'un autre type de localisateur de service? La nuance réside-t-elle dans le fait que nous utilisons les conteneurs DI principalement lorsque nous avons de nombreuses dépendances?
la source
Réponses:
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 services pour le faire. Avec DI, la classe reçoit ses dépendances. Il ne sait ni ne se soucie d'où ils viennent. Un résultat important de cela est que l'exemple DI est beaucoup plus facile à tester unitaire - car vous pouvez lui passer des implémentations simulées de ses objets dépendants. Vous pouvez combiner les deux - et injecter le localisateur de service (ou une usine), si vous le souhaitez.
la source
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épendances sera généralement appelé une seule fois au démarrage pour injecter des dépendances dans une classe principale. Les classes dont dépend cette classe principale verront récursivement leurs dépendances injectées, jusqu'à ce que vous ayez un graphique d'objet complet.
Une bonne comparaison: http://martinfowler.com/articles/injection.html
Si votre injecteur de dépendances ressemble à un localisateur de services, où les classes appellent directement l'injecteur, ce n'est probablement pas un injecteur de dépendances, mais plutôt un localisateur de services.
la source
Les localisateurs de service masquent les dépendances - vous ne pouvez pas dire en regardant un objet s'il atteint une base de données ou non (par exemple) lorsqu'il obtient des connexions à partir d'un localisateur. Avec l'injection de dépendances (au moins l'injection de constructeur), les dépendances sont explicites.
De plus, les localisateurs de service interrompent l'encapsulation car ils fournissent un point d'accès global aux dépendances d'autres objets. Avec le localisateur de service, comme avec tout singleton :
Avec l'injection de dépendances, une fois les dépendances d'un objet spécifiées, elles sont sous le contrôle de l'objet lui-même.
la source
With dependency injection (at least constructor injection) the dependencies are explicit.
. S'il vous plaît, expliquez.Martin Fowler déclare :
En bref: Service Locator et Dependency Injection ne sont que des implémentations du Dependency Inversion Principle.
Le principe important est «Dépendre 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 grande application, ayant une énorme base de code, vous feriez mieux d'utiliser un localisateur de service, car l'injection de dépendances nécessiterait plus de modifications dans votre base de code.
Vous pouvez consulter cet article: Inversion de dépendance: Localisateur de service ou Injection de dépendance
Aussi le classique: l' inversion des conteneurs de contrôle et le modèle d'injection de dépendance par Martin Fowler
Concevoir des classes réutilisables par Ralph E. Johnson & Brian Foote
Cependant, celui qui m'a ouvert les yeux était: ASP.NET MVC: résoudre ou injecter? C'est ça le problème… par Dino Esposito
la source
Une classe utilisant le constructeur DI indique pour consommer du code qu'il y a des dépendances à satisfaire. Si la classe utilise le SL en interne pour récupérer ces dépendances, le code consommateur n'est pas au courant des dépendances. Cela peut sembler mieux en surface, mais il est en fait utile de connaître les dépendances explicites. C'est mieux d'un point de vue architectural. Et lorsque vous effectuez des tests, vous devez savoir si une classe a besoin de certaines dépendances et configurer la SL pour fournir de fausses versions appropriées de ces dépendances. Avec DI, passez simplement les faux. Pas une énorme différence, mais elle est là.
DI et SL peuvent cependant fonctionner ensemble. Il est utile d'avoir un emplacement central pour les dépendances courantes (par exemple les paramètres, l'enregistreur, etc.). Étant donné une classe utilisant de tels deps, vous pouvez créer un "vrai" constructeur qui reçoit les deps, et un constructeur par défaut (sans paramètre) qui récupère de la SL et transmet au "vrai" constructeur.
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 dont vous auriez besoin pour utiliser le SL. Si cela est fait comme suggéré ci-dessus, juste dans le constructeur de classe par défaut.
la source
Les deux sont des techniques de mise en œuvre d'IoC. Il existe également d'autres modèles qui implémentent l'inversion de contrôle:
Le localisateur de services et le conteneur DI semblent plus similaires, les deux utilisent un conteneur pour définir les dépendances, qui mappent l'abstraction à l'implémentation concrète.
La principale différence est la façon dont les dépendances sont situées, dans Service Locator, le code client demande les dépendances, dans DI Container, nous utilisons un conteneur pour créer tous les objets et il injecte des dépendances en tant que paramètres (ou propriétés) du constructeur.
la source
Dans mon dernier projet, j'utilise les deux. J'utilise l'injection de dépendance pour la testabilité unitaire. J'utilise le localisateur de services 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 résume cette phase.
Comment ne dépendriez-vous pas de votre conteneur IoC? Soit vous devrez l'envelopper vous-même (ce qui est une mauvaise idée), soit vous utilisez Service Locator pour configurer votre conteneur IoC. Ainsi, vous direz au localisateur de services d'obtenir l'interface dont vous avez besoin et il appellera le conteneur IoC configuré pour récupérer cette interface.
Dans mon cas, j'utilise ServiceLocator qui est un composant de framework. Et utilisez Unity pour le conteneur IoC. Si à l'avenir je dois échanger mon conteneur IoC contre Ninject tout ce que je dois faire, c'est que je dois configurer mon localisateur de service pour 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/
la source
Je pense que les deux fonctionnent ensemble.
L'injection de dépendance signifie que vous insérez une classe / interface dépendante dans une classe consommatrice (généralement vers son constructeur). Cela dissocie les deux classes via une interface et signifie que la classe consommatrice peut fonctionner avec de nombreux types d'implémentations de "dépendance injectée".
Le rôle du localisateur de services est de regrouper votre implémentation. Vous configurez un localisateur de service via un cerclage de démarrage au début de votre programme. Le bootstrap est le processus d'association d'un type d'implémentation à un résumé / interface particulier. Qui est créé pour vous au moment de l'exécution. (basé sur votre configuration 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.
la source
Une raison d'ajouter, inspirée par 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 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
Foo
composant, une instance deIBar
et sera disponible, et que le composant qui le fournira:Dans le deuxième exemple que vous avez donné, où le constructeur va dans le conteneur IoC pour récupérer ses dépendances, la seule façon de tester qu'une instance de
Foo
pourra être instanciée correctement avec la configuration d'exécution réelle de votre application est de réellement construire il .Cela a toutes sortes d'effets secondaires gênants au moment du test, car le code qui fonctionnera à l'exécution ne fonctionnera pas nécessairement sous un faisceau de test. Les simulations ne suffiront pas, car la configuration réelle est la chose que nous devons tester, pas une configuration de temps de test.
La racine de ce problème est la différence déjà signalée par @Jon: l'injection de dépendances via le constructeur est déclarative, tandis que la deuxième version utilise le modèle de localisateur de service impératif.
Un conteneur IoC, lorsqu'il est utilisé avec précaution, peut analyser statiquement la configuration d'exécution de votre application sans réellement créer d'instances des composants impliqués. De nombreux conteneurs populaires offrent une certaine variation de cela; Microsoft.Composition , qui est la version de MEF ciblant les applications de style Web et Metro .NET 4.5, fournit un
CompositionAssert
exemple dans la documentation wiki. En l'utilisant, vous pouvez écrire du code comme:(Voir cet exemple ).
En vérifiant les racines de composition de de votre application au moment du test, vous pouvez potentiellement détecter certaines erreurs qui pourraient autrement passer à travers les tests plus tard dans le processus.
J'espère que c'est un ajout intéressant à cet ensemble de réponses autrement complet sur le sujet!
la source
Remarque: je ne répond pas exactement à la question. Mais je pense que cela peut être utile pour les nouveaux apprenants du modèle d'injection de dépendance qui sont confondus à ce sujet avec le modèle (anti) Service Locator qui tombent par hasard sur cette page.
Je connais la différence entre le localisateur de service (il semble être considéré comme un anti-modèle maintenant) et les modèles d'injection de dépendance et je peux comprendre des exemples concrets pour chaque modèle, mais j'ai été confus par des exemples montrant un localisateur de service à l'intérieur du constructeur (supposons que nous '' faire l'injection du constructeur).
"Service Locator" est souvent utilisé à la fois comme nom d'un modèle et comme nom pour faire référence à l'objet (supposons aussi) utilisé dans ce modèle pour obtenir des objets sans utiliser le nouvel opérateur. Maintenant, ce même type d'objet peut également être utilisé à la racine de la composition de pour effectuer une injection de dépendance, et c'est là qu'intervient la confusion.
Le fait est que vous utilisez peut-être un objet de localisation de service à l'intérieur d'un constructeur DI, mais vous n'utilisez pas le "modèle de localisation de service". C'est moins déroutant si on le réfère plutôt comme un objet conteneur IoC, car vous avez peut-être deviné qu'ils font essentiellement la même chose (corrigez-moi si je me trompe).
Qu'il soit appelé localisateur de services (ou simplement localisateur) ou conteneur IoC (ou simplement conteneur) ne fait aucune différence, comme vous pouvez le deviner, car il fait probablement référence à la même abstraction (corrigez-moi si je me trompe) ). C'est juste que l'appeler un localisateur de service suggère que l'on utilise l'anti-modèle Service Locator avec le modèle d'injection de dépendance.
À mon humble avis, le nommer un «localisateur» au lieu de «emplacement» ou «localisation», peut aussi parfois faire penser que le localisateur de service dans un article se réfère au conteneur Service Locator, et non au modèle Service Locator (anti-) , en particulier lorsqu'il existe un modèle associé appelé injection de dépendance et non injecteur de dépendance.
la source
Dans ce cas simplifié, 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 elle-même avait une autre dépendance nommée D. Dans ce cas, votre localisateur de services ne pourrait pas résoudre cette dépendance et vous devrez l'instancier au sein de la classe D; car il est de la responsabilité de vos classes d'instancier leurs dépendances. Cela pourrait même empirer si la classe D elle-même avait d'autres dépendances et dans des situations réelles, cela devient généralement encore plus compliqué que cela. Dans de tels scénarios, DI est une meilleure solution que ServiceLocator.
la source
bar
classe elle-même a une dépendance, ellebar
aura également un localisateur de service, c'est tout l'intérêt d'utiliser DI / IoC.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 bons pour implémenter le 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 forcer les modifications de 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 l'injection de dépendance.
Le modèle d'Injection de dépendances l'indique clairement depuis la signature des dépendances qu'une classe (ou une méthode) va avoir. Pour cette raison, le code résultant est plus propre et plus lisible.
la source
Une conception simple m'a permis de mieux comprendre la différence entre Service Locator et DI Container:
Le localisateur de service est utilisé par le consommateur et il extrait les services par ID de certains stockages à la demande directe du consommateur
DI Container est situé quelque part à l'extérieur et prend des services d'un certain stockage et les pousse vers le consommateur (peu importe via le constructeur ou via la méthode)
Cependant, nous ne pouvons parler de différence entre ces derniers que dans le contexte d'une utilisation concrète par les consommateurs. Lorsque Service Locator et DI Container sont utilisés dans la racine de composition, ils sont presque similaires.
la source
Le conteneur DI est un surensemble de localisateur de services. Il peut être utilisé pour localiser un service , avec une capacité supplémentaire d' assemblage (câblage) des injections de dépendance .
la source
Pour mémoire
Sauf si vous avez vraiment besoin d'une interface (l'interface est utilisée par plusieurs classes), vous NE DEVEZ PAS L'UTILISER . Dans ce cas, IBar permet d'utiliser n'importe quelle classe de service, qui l'implémente. Cependant, généralement, cette interface sera utilisée par une seule classe.
Pourquoi est-ce une mauvaise idée d'utiliser une interface?. Parce que c'est vraiment difficile à déboguer.
Par exemple, disons que l'instance "bar" a échoué, question: quelle classe a échoué?. Quel code dois-je corriger? Une vue simple, elle mène à une Interface, et c'est ici que ma route se termine.
Au lieu de cela, si le code utilise une dépendance matérielle, il est facile de déboguer une erreur.
Si "bar" échoue, je devrais vérifier et lancer la classe BarService.
la source
contract
et définit simplement un comportement et non l'action. Au lieu de faire circuler l'objet réel, seule l'interface est partagée afin que le consommateur n'accède pas au reste de votre objet. De plus, pour les tests unitaires, il est utile de tester uniquement la partie qui doit être testée. Je suppose qu'avec le temps, vous comprendrez son utilité.