Was ist der Unterschied zwischen den Dependency Injection- und Service Locator-Mustern?

Beide Muster scheinen eine Umsetzung des Prinzips der Inversion der Kontrolle zu sein. Das heißt, ein Objekt sollte nicht wissen, wie es seine Abhängigkeiten konstruiert.

Dependency Injection (DI) scheint einen Konstruktor oder Setter zu verwenden, um seine Abhängigkeiten zu “injizieren”.

Beispiel für die Verwendung der Konstruktorinjektion:

//Foo Needs an IBar public class Foo { private IBar bar; public Foo(IBar bar) { this.bar = bar; } //... } 

Service Locator scheint einen “Container” zu verwenden, der seine Abhängigkeiten verdrahtet und foo es als Balken gibt.

Beispiel für die Verwendung eines Service Locator:

 //Foo Needs an IBar public class Foo { private IBar bar; public Foo() { this.bar = Container.Get(); } //... } 

Da unsere Abhängigkeiten nur Objekte selbst sind, haben diese Abhängigkeiten Abhängigkeiten, die noch mehr Abhängigkeiten haben, und so weiter und so fort. So wurde der Inversion of Control Container (oder DI-Container) geboren. Beispiele: Castle Windsor, Ninject, Strukturkarte, Frühling, etc.)

Ein IOC / DI-Container sieht jedoch genauso aus wie ein Service Locator. Nennt es einen DI-Container einen schlechten Namen? Ist ein IOC / DI Container nur eine andere Art von Service Locator? Ist die Nuance in der Tatsache, dass wir DI Container hauptsächlich verwenden, wenn wir viele Abhängigkeiten haben?

   

    Der Unterschied mag gering erscheinen, aber selbst mit dem ServiceLocator ist die class immer noch für die Erstellung ihrer Abhängigkeiten verantwortlich. Es verwendet nur den Service-Locator, um es zu tun. Mit DI erhält die class ihre Abhängigkeiten. Es weiß weder, noch kümmert es sich woher sie kommen. Ein wichtiges Ergebnis davon ist, dass das DI-Beispiel viel einfacher zu testen ist – weil Sie es als Scheinimplementierungen seiner abhängigen Objekte übergeben können. Sie könnten die beiden kombinieren – und den Service Locator (oder eine Fabrik) injizieren, wenn Sie wollten.

    Wenn Sie einen Service-Locator verwenden, hat jede class eine Abhängigkeit von Ihrem Service-Locator. Dies ist bei der Abhängigkeitsinjektion nicht der Fall. Der Abhängigkeitsinjektor wird typischerweise nur einmal beim Start aufgerufen, um Abhängigkeiten in eine Hauptklasse zu injizieren. Die classn, von denen diese Hauptklasse abhängt, werden rekursiv ihre Abhängigkeiten einbeziehen, bis Sie ein vollständiges Objektdiagramm haben.

    Ein guter Vergleich: http://martinfowler.com/articles/injection.html

    Wenn Ihr Dependency Injector wie ein Service Locator aussieht, bei dem die classn den Injektor direkt aufrufen, handelt es sich wahrscheinlich nicht um einen Dependency Injector, sondern um einen Service Locator.

    Service-Locators verbergen Abhängigkeiten – Sie können nicht erkennen, ob ein Objekt eine database trifft oder nicht (zum Beispiel), wenn es Verbindungen von einem Locator erhält. Mit Abhängigkeitsinjektion (zumindest Konstruktorinjektion) sind die Abhängigkeiten explizit.

    Darüber hinaus unterbrechen Servicelokatoren die Kapselung, da sie einen globalen Zugriffspunkt für Abhängigkeiten anderer Objekte bereitstellen. Mit Service Locator, wie bei jedem Singleton :

    es wird schwierig, die Pre- und Post-Bedingungen für die Schnittstelle des Client-Objekts zu spezifizieren, da die Abläufe ihrer Implementierung von außen eingemischt werden können.

    Sobald die Abhängigkeiten eines Objekts angegeben sind, unterliegen sie bei der Abhängigkeitsinjektion der Kontrolle des Objekts selbst.

    Martin Fowler sagt :

    Mit Service Locator fragt die Anwendungsklasse explizit nach einer Nachricht an den Locator. Bei der Injektion gibt es keine explizite Anfrage, der Dienst erscheint in der Anwendungsklasse – daher die Inversion der Kontrolle.

    Kurz gesagt: Service Locator und Dependency Injection sind nur Implementierungen des Dependency Inversion Principle.

    Das wichtige Prinzip ist “auf Abstraktionen angewiesen, nicht auf Konkretionen”. Dadurch wird Ihr Softwaredesign “lose gekoppelt”, “erweiterbar” und “flexibel”.

    Sie können diejenige verwenden, die Ihren Bedürfnissen am besten entspricht. Für eine große Anwendung mit einer großen Codebasis sollten Sie besser einen Service Locator verwenden, da Dependency Injection mehr Änderungen an Ihrer Codebasis erfordert.

    Sie können diesen Beitrag überprüfen: Dependency Inversion: Service Locator oder Dependency Injection

    Auch der Klassiker: Inversion von Kontrollbehältern und das Dependency Injection Pattern von Martin Fowler

    Entcasting wiederverwendbarer classn von Ralph E. Johnson & Brian Foote

    Doch der, der meine Augen öffnete, war: ASP.NET MVC: Auflösen oder Injizieren? Das ist das Thema … von Dino Esposito

    Eine class, die den Konstruktor DI verwendet, zeigt dem Code, dass Abhängigkeiten erfüllt sein müssen. Wenn die class das SL intern verwendet, um solche Abhängigkeiten abzurufen, kennt der konsumierende Code die Abhängigkeiten nicht. Dies mag auf der Oberfläche besser erscheinen, aber es ist tatsächlich hilfreich, irgendwelche expliziten Abhängigkeiten zu kennen. Aus architektonischer Sicht ist es besser. Und wenn Sie Tests durchführen, müssen Sie wissen, ob eine class bestimmte Abhängigkeiten benötigt, und den SL konfigurieren, um geeignete gefälschte Versionen dieser Abhängigkeiten bereitzustellen. Mit DI geben Sie einfach die Fälschungen ein. Kein großer Unterschied, aber es ist da.

    DI und SL können jedoch zusammenarbeiten. Es ist nützlich, einen zentralen Ort für allgemeine Abhängigkeiten (zB Einstellungen, Logger usw.) zu haben. Bei einer class, die solche Deps verwendet, können Sie einen “echten” Konstruktor erstellen, der die Deps empfängt, und einen Standardkonstruktor (kein Parameter), der vom SL abruft und an den “echten” Konstruktor weiterleitet.

    BEARBEITEN: und natürlich, wenn Sie den SL verwenden, führen Sie eine Kopplung mit dieser Komponente ein. Das ist ironisch, da die Idee einer solchen functionalität darin besteht, Abstraktionen zu fördern und die Kopplung zu reduzieren. Die Bedenken können ausgeglichen sein, und es hängt davon ab, an wie vielen Stellen Sie den SL benutzen müssen. Wenn wie oben vorgeschlagen, nur im Standardklassenkonstruktor.

    Ich denke, die beiden arbeiten zusammen.

    Abhängigkeitsinjektion bedeutet, dass Sie eine abhängige class / Schnittstelle in eine konsumierende class (normalerweise in den Konstruktor) hineinschieben. Dies entkoppelt die beiden classn über eine Schnittstelle und bedeutet, dass die konsumierende class mit vielen Arten von “injected dependency” -Implementierungen arbeiten kann.

    Die Rolle des Service Locators besteht darin, Ihre Implementierung zusammenzufassen. Sie haben einen Service Locator über einige Bootstrapping zu Beginn Ihres Programms eingerichtet. Bootstrapping ist der process des Zuordnens eines Implementierungstyps zu einem bestimmten Abstract / Interface. Was zur Laufzeit für Sie erstellt wird. (basierend auf dir config oder bootstrap). Wenn Sie die Abhängigkeitsinjektion nicht implementiert hätten, wäre es sehr schwierig, einen Service Locator oder einen IOC-Container zu verwenden.

    Beide sind Implementierungstechniken von IoC. Es gibt auch andere Muster, die Inversion of Control implementieren:

    • Fabrikmuster
    • Servicelokalisierer
    • Abhängigkeitsinjektion (Konstruktorinjektion, Parameterinjektion (falls nicht erforderlich), Setterinjektion der Schnittstelleninjektion) …

    Service-Locator und DI scheinen einander ähnlicher zu sein. Beide verwenden Container, um Abhängigkeiten zu definieren, die die Abstraktion der konkreten Implementierung zuordnen.

    Der Hauptunterschied besteht darin, wie die Abhängigkeiten lokalisiert werden, in Service Location Client-Code die Abhängigkeiten anfordern, in DI wir Container verwenden, um alle Objekte zu erzeugen, und es injiziert Abhängigkeit als Konstruktor Parameter (oder Eigenschaften).

    In meinem letzten Projekt verwende ich beides. Ich verwende die Abhängigkeitsinjektion für die Unit-Testbarkeit. Ich benutze Service Locator, um die Implementierung auszublenden und von meinem IoC-Container abhängig zu sein. und ja! Sobald Sie einen der IoC-Container (Unity, Ninject, Windsor Castle) verwenden, sind Sie darauf angewiesen. Und sobald es veraltet ist oder aus irgendeinem Grund, wenn Sie es tauschen möchten, müssen / müssen Sie Ihre Implementierung ändern – zumindest Zusammensetzung Wurzel. Aber Service Locator abstrahiert diese Phase.

    Wie würden Sie nicht auf Ihren IoC-Container angewiesen sein? Entweder müssen Sie es selbst einpacken (was eine schlechte Idee ist) oder Sie verwenden Service Locator, um Ihren IoC-Container zu konfigurieren. Sie werden dem Service Locator also mitteilen, welche Schnittstelle Sie benötigen, und er ruft den IoC-Container auf, der so konfiguriert ist, dass er diese Schnittstelle abruft.

    In meinem Fall verwende ich ServiceLocator , eine Framework-Komponente. Und verwenden Sie Unity für IoC-Container. Wenn ich in Zukunft meinen IoC-Container gegen Ninject tauschen muss, muss ich meinen Service-Locator so konfigurieren, dass Ninject anstelle von Unity verwendet wird. Einfache Migration

    Hier ist ein großartiger Artikel, der dieses Szenario erklärt; http://www.johandekoning.nl/index.php/2013/03/03/dont-wrap-your-ioc-container/

    Ein Grund zum Hinzufügen, inspiriert durch ein Dokumentations-Update, das wir letzte Woche für das MEF-Projekt geschrieben haben (ich helfe beim Aufbau von MEF).

    Sobald eine App aus potenziell tausenden von Komponenten besteht, kann es schwierig sein festzustellen, ob eine bestimmte Komponente korrekt instanziiert werden kann. Mit “instanziiert korrekt” meine ich, dass in diesem Beispiel basierend auf der Foo Komponente eine Instanz von IBar und verfügbar sein wird und dass die Komponente, die es bereitstellt IBar

    • haben ihre erforderlichen Abhängigkeiten,
    • nicht an ungültigen Abhängigkeitszyklen beteiligt sein, und
    • im Fall von MEF, mit nur einer Instanz geliefert werden.

    Im zweiten Beispiel, in dem der Konstruktor zum IoC-Container geht, um seine Abhängigkeiten abzurufen, können Sie nur testen, ob eine Instanz von Foo tatsächlich mit der tatsächlichen Laufzeitkonfiguration Ihrer App instanziiert werden kann konstruiere es .

    Dies hat alle möglichen unangenehmen Nebenwirkungen zur Testzeit, weil Code, der zur Laufzeit funktioniert, nicht unbedingt unter einer Testumgebung funktionieren wird. Mocks werden nicht funktionieren, weil die reale Konfiguration das ist, was wir testen müssen, und nicht eine Testzeit-Einrichtung.

    Die Wurzel dieses Problems ist der Unterschied, der bereits von @Jon hervorgerufen wurde: Das Eingeben von Abhängigkeiten über den Konstruktor ist deklarativ, während die zweite Version das imperative Service Locator-Muster verwendet.

    Ein IoC-Container kann bei sorgfältiger Verwendung die Laufzeitkonfiguration Ihrer App statisch analysieren, ohne dass tatsächlich Instanzen der beteiligten Komponenten erstellt werden. Viele beliebte Container bieten eine gewisse Variation; Microsoft.Composition , die Version von MEF, die auf .NET 4.5 Web- und Metro-Apps abzielt, stellt ein CompositionAssert Beispiel in der Wiki-Dokumentation bereit. Mit ihr können Sie Code schreiben wie:

      // Whatever you use at runtime to configure the container var container = CreateContainer(); CompositionAssert.CanExportSingle(container); 

    (Siehe dieses Beispiel ).

    Wenn Sie die Composition Roots Ihrer Anwendung zum Testzeitpunkt überprüfen, können Sie möglicherweise einige Fehler erkennen, die andernfalls später im Test durchgehen könnten.

    Ich hoffe, dies ist eine interessante Ergänzung zu dieser ansonsten umfassenden Sammlung von Antworten zum Thema!

    Hinweis: Ich beantworte die Frage nicht genau. Aber ich denke, dass dies für neue Lerner des Dependency Injection-Modells nützlich sein kann, die sich darüber mit dem Service Locator (Anti-) Muster , das zufällig auf diese Seite stolpert, verwirrt fühlen.

    Ich kenne den Unterschied zwischen dem Service Locator (es scheint jetzt als ein Anti-Pattern betrachtet zu werden) und Dependency Injection Patterns und kann konkrete Beispiele für jedes Pattern verstehen, aber ich war verwirrt von Beispielen, die einen Service Locator innerhalb des Konstruktors zeigten re do constructor injection).

    “Service Locator” wird häufig sowohl als Name eines Musters als auch als Name für das Objekt verwendet, das in diesem Muster verwendet wird, um Objekte zu erhalten, ohne den neuen Operator zu verwenden. Nun kann derselbe Objekttyp auch im Kompositionswurzel verwendet werden , um die Abhängigkeitsinjektion durchzuführen, und hier kommt die Verwirrung ins Spiel.

    Beachten Sie, dass Sie möglicherweise ein Service-Locator-Objekt in einem DI-Konstruktor verwenden, das “Service Locator Pattern” jedoch nicht verwenden. Es ist weniger verwirrend, wenn man es stattdessen als ein IoC-Container-Objekt bezeichnet, da Sie vielleicht vermutet haben, dass sie im Wesentlichen dasselbe tun (korrigieren Sie mich, wenn ich falsch liege).

    Ob es als Service Locator (oder einfach Locator) oder als IoC-Container (oder nur Container) bezeichnet wird, macht keinen Unterschied, da Sie vermutlich dieselbe Abstraktion verwenden (korrigieren Sie mich, wenn ich falsch liege) ). Es ist nur so, dass das Aufrufen eines Service-Locators das Service-Locator-Anti-Pattern zusammen mit dem Dependency-Injection-Pattern verwendet.

    IMHO, benennen sie ein “Locator” anstelle von “Ort” oder “Ortung”, kann auch manchmal denken, dass der Service-Locator in einem Artikel bezieht sich auf den Service-Locator-Container und nicht das Service Locator (Anti-) Muster , besonders wenn es ein verwandtes Muster namens Dependency Injection und nicht Dependency Injector gibt.

    In diesem vereinfachten Fall gibt es keinen Unterschied und sie können synonym verwendet werden. Die Probleme der realen Welt sind jedoch nicht so einfach. Nehmen wir an, dass die Bar-class selbst eine andere Abhängigkeit namens D hat. In diesem Fall wäre Ihr Service-Locator nicht in der Lage, diese Abhängigkeit aufzulösen, und Sie müssten sie innerhalb der D-class instanziieren. weil es die Verantwortung Ihrer classn ist, ihre Abhängigkeiten zu instanziieren. Es würde sogar noch schlimmer werden, wenn die D-class selbst andere Abhängigkeiten hätte, und in realen Situationen wird es normalerweise noch komplizierter. In solchen Szenarien ist DI eine bessere Lösung als ServiceLocator.

    Was ist der Unterschied zwischen Dependency Injection und Service Locator? Beide Muster sind gut bei der Implementierung des Dependency Inversion-Prinzips. Das Service Locator-Muster ist einfacher in einer vorhandenen Codebasis zu verwenden, da es das Gesamtdesign lockerer macht, ohne dass Änderungen an der öffentlichen Schnittstelle erzwungen werden müssen. Aus demselben Grund ist Code, der auf dem Service Locator-Muster basiert, weniger lesbar als gleichwertiger Code, der auf Dependency Injection basiert.

    Das Dependency Injection-Muster macht es deutlich, da die Signatur, die eine class (oder eine Methode) abhängig ist, eine Signatur haben wird. Aus diesem Grund ist der resultierende Code sauberer und lesbarer.