wake-up-neo.com

Was ist der Unterschied zwischen den Abhängigkeitseinspritzungs- 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 aufbauen soll. 

Dependency Injection (DI) scheint einen Konstruktor oder Setter zu verwenden, um seine Abhängigkeiten "einzuspeisen". 

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 die Latte gibt. 

Beispiel für die Verwendung eines Service Locators:

//Foo Needs an IBar
public class Foo
{
  private IBar bar;

  public Foo()
  {
    this.bar = Container.Get<IBar>();
  }

  //...
}

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 Inversionskontrollcontainer (oder DI-Container) geboren. Beispiele: Castle Windsor, Ninject, Structure Map, Spring usw.)

Aber ein IOC/DI-Container sieht genau aus wie ein Service Locator. Ist es ein schlechter Name, DI-Container zu nennen? Ist ein IOC/DI-Container nur ein anderer type von Service Locator? Ist die Nuance in der Tatsache, dass wir DI-Container meistens verwenden, wenn wir viele Abhängigkeiten haben?

250
Charles Graham

Der Unterschied mag geringfügig erscheinen, aber selbst beim ServiceLocator ist die Klasse immer noch dafür verantwortlich, ihre Abhängigkeiten zu erstellen. Dazu wird lediglich der Service Locator verwendet. Mit DI erhält die Klasse ihre Abhängigkeiten. Es weiß weder, noch interessiert es, woher sie kommen. Ein wichtiges Ergebnis davon ist, dass das DI-Beispiel viel einfacher für den Komponententest ist, da Sie Modellimplementierungen der abhängigen Objekte übergeben können. Sie können die beiden kombinieren - und den Service Locator (oder eine Fabrik) injizieren, wenn Sie dies wünschen.

156
tvanfosson

Wenn Sie einen Service Locator verwenden, hängt jede Klasse von Ihrem Service Locator ab. Dies ist bei Abhängigkeitsinjektion nicht der Fall. Der Abhängigkeitsinjektor wird normalerweise beim Start nur einmal aufgerufen, um Abhängigkeiten in eine Hauptklasse einzugeben. Bei den Klassen, von denen diese Hauptklasse abhängt, werden die Abhängigkeiten rekursiv eingefügt, bis ein vollständiger Objektgraph vorliegt.

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

Wenn Ihr Abhängigkeitsinjektor wie ein Service Locator aussieht, bei dem die Klassen den Injektor direkt aufrufen, handelt es sich wahrscheinlich nicht um einen Abhängigkeitsinjektor, sondern um einen Service Locator.

78
Joel

Service Locators blenden Abhängigkeiten aus - Sie können nicht anhand eines Objekts erkennen, ob es auf eine Datenbank trifft oder nicht (zum Beispiel), wenn es Verbindungen von einem Locator abruft. Bei der Abhängigkeitsinjektion (zumindest Konstruktorinjektion) sind die Abhängigkeiten explizit.

Darüber hinaus unterbrechen Service Locators die Kapselung, da sie einen globalen Zugriffspunkt auf Abhängigkeiten anderer Objekte bieten. Mit Service Locator wie bei jedem Singleton :

es wird schwierig, pre und post anzugeben Bedingungen für das Clientobjekt Schnittstelle, weil die Arbeitsweise seiner Implementierung kann mit .__ eingemischt werden. von außen.

Bei der Abhängigkeitsinjektion stehen die einmal festgelegten Abhängigkeiten eines Objekts unter der Kontrolle des Objekts.

41
Jeff Sternal

Martin Fowler erklärt

Mit dem Service Locator fragt die Anwendungsklasse explizit nach einer Nachricht an den Locator. Bei der Injektion gibt es keine explizite Anforderung Der Dienst wird in der Anwendungsklasse angezeigt - daher die Umkehrung von Steuerung.

Kurz gesagt: Service Locator und Dependency Injection sind nur Implementierungen des Prinzips der Abhängigkeitsinversion.

Das wichtige Prinzip lautet: "Hänge von Abstraktionen ab, nicht von 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 Codebase sollten Sie besser einen Service Locator verwenden, da die Abhängigkeitsinjektion weitere Änderungen an Ihrer Codebase erfordert.

Sie können diesen Beitrag überprüfen: Abhängigkeitsinversion: Service Locator oder Abhängigkeitsinjektion

Auch der Klassiker: Inversion von Kontrollcontainern und das Abhängigkeitseinspritzmuster von Martin Fowler

Entwerfen wiederverwendbarer Klassen von Ralph E. Johnson & Brian Foote 

Die eine, die mir die Augen öffnete, war: ASP.NET MVC: Auflösen oder Inject Das ist das Problem… von Dino Esposito

31
Nathan

Eine Klasse, die Konstruktor DI verwendet, zeigt an, dass Code verwendet werden muss, dass Abhängigkeiten zu erfüllen sind. Wenn die Klasse den SL intern verwendet, um solche Abhängigkeiten abzurufen, sind dem Abhängigen Code die Abhängigkeiten nicht bekannt. Dies mag oberflächlich erscheinen, aber es ist tatsächlich hilfreich, explizite Abhängigkeiten zu kennen. Aus architektonischer Sicht ist es besser. Beim Testen müssen Sie wissen, ob eine Klasse bestimmte Abhängigkeiten benötigt, und den SL so konfigurieren, dass er geeignete falsche Versionen dieser Abhängigkeiten bereitstellt. Mit DI übergeben Sie einfach die Fälschungen. Kein großer Unterschied, aber es ist da.

DI und SL können jedoch zusammenarbeiten. Es ist nützlich, einen zentralen Ort für häufige Abhängigkeiten (z. B. Einstellungen, Protokollierung usw.) zu haben. Wenn eine Klasse 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: Wenn Sie den SL verwenden, führen Sie natürlich eine Kopplung mit dieser Komponente ein. Was ironisch ist, da die Idee einer solchen Funktionalität darin besteht, Abstraktionen zu fördern und die Kopplung zu reduzieren. Die Bedenken können ausgewogen sein und hängen davon ab, an wie vielen Stellen Sie den SL benötigen. Wenn dies wie oben beschrieben erfolgt, nur im Standardklassenkonstruktor.

19
Grant Palin

In meinem letzten Projekt verwende ich beides. Ich benutze die Abhängigkeitspritze für die Testbarkeit von Einheiten. Ich verwende den Service Locator, um die Implementierung zu verbergen und von meinem IoC-Container abhängig zu sein. und ja! Sobald Sie einen der IoC-Container (Unity, Ninject, Windsor Castle) verwenden, sind Sie davon abhängig. Und wenn es einmal veraltet ist oder wenn Sie es aus irgendeinem Grund austauschen möchten, müssen/müssen Sie möglicherweise Ihre Implementierung ändern - zumindest die Kompositionswurzel. Service Locator abstrahiert diese Phase.

Wie würden Sie sich nicht auf Ihren IoC-Container verlassen? Entweder müssen Sie es selbst verpacken (was keine gute 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 der IoC-Container wird so konfiguriert, 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 austauschen muss, muss ich meinen Service Locator so konfigurieren, dass er Ninject anstelle von Unity verwendet. 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/

6
Teoman shipahi

Ich denke, die beiden arbeiten zusammen.

Abhängigkeitsinjektion bedeutet, dass Sie abhängige Klassen/Schnittstellen in eine konsumierende Klasse (normalerweise in den Konstruktor) einschieben. Dies entkoppelt die beiden Klassen über eine Schnittstelle und bedeutet, dass die konsumierende Klasse mit vielen Arten von Implementierungen "injizierter Abhängigkeit" arbeiten kann. 

Die Funktion des Service Locators besteht darin, Ihre Implementierung zusammenzuführen. Sie richten einen Service Locator über Start-Strapping beim Start Ihres Programms ein. Beim Bootstrapping wird ein Typ von Implementierung einem bestimmten Abstract/Interface zugeordnet. Welches wird zur Laufzeit für Sie erstellt. (basierend auf deiner config oder bootstrap). Wenn Sie keine Abhängigkeitsinjektion implementiert hätten, wäre es sehr schwierig, einen Service Locator oder einen IOC Container zu verwenden.

5
NoelAdy

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

Sobald eine App aus möglicherweise Tausenden von Komponenten besteht, kann es schwierig sein, festzustellen, ob eine bestimmte Komponente korrekt instanziiert werden kann. Mit "richtig instanziiert" 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 dies bereitstellt:

  • die erforderlichen Abhängigkeiten haben,
  • nicht an ungültigen Abhängigkeitszyklen beteiligt sein, und
  • im Fall von MEF muss nur eine Instanz angegeben werden.

Im zweiten von Ihnen angegebenen Beispiel, in dem der Konstruktor zum Abrufen der Abhängigkeiten zum IoC-Container wechselt, können Sie nur so testen, ob eine Instanz von Foo korrekt instanziiert werden kann Mit der eigentlichen Laufzeitkonfiguration Ihrer App soll diese tatsächlich erstellen .

Dies hat zur Testzeit alle möglichen unangenehmen Nebenwirkungen, da Code, der zur Laufzeit funktioniert, nicht unbedingt unter einem Test-Harness funktioniert. Mocks reichen nicht aus, da die eigentliche Konfiguration das ist, was wir testen müssen, nicht irgendeine Testzeiteinstellung.

Die Wurzel dieses Problems ist der Unterschied, der bereits von @Jon angesprochen wurde: Das Einfügen 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 tatsächlich Instanzen der beteiligten Komponenten zu erstellen. Viele populäre Behälter bieten eine Variation davon; Microsoft.Composition , die Version von MEF für .NET 4.5-Apps im Web- und Metro-Stil, enthält ein Beispiel für CompositionAssert in der Wiki-Dokumentation . Mit ihm können Sie Code wie folgt schreiben:

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

CompositionAssert.CanExportSingle<Foo>(container);

(Siehe dieses Beispiel ).

Indem Sie die Composition Roots Ihrer Anwendung zum Testzeitpunkt überprüfen, können Sie möglicherweise einige Fehler feststellen, die andernfalls später im Prozess durch den Test schlüpfen könnten.

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

5

Bei beiden handelt es sich um Implementierungstechniken von IoC. Es gibt auch andere Muster, die Inversion of Control implementieren:

  • fabrikmuster
  • service Locator
  • abhängigkeitsinjektion (Konstruktorinjektion, Parameterinjektion (falls nicht erforderlich), Setterinjektion der Schnittstelleninjektion) ...

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

Der Hauptunterschied besteht darin, wie sich die Abhängigkeiten befinden. In Service Location-Clientcode werden die Abhängigkeiten angefordert. In DI verwenden wir Container, um alle Objekte zu erstellen, und es wird Abhängigkeit als Konstruktorparameter (oder Eigenschaften) eingefügt. 

5
Nininea

In diesem vereinfachten Fall gibt es keinen Unterschied und sie können austauschbar verwendet werden. .__ Probleme der realen Welt sind jedoch nicht so einfach. Nehmen Sie einfach an, dass die Bar-Klasse selbst eine andere Abhängigkeit namens D hatte. In diesem Fall könnte Ihr Service Locator diese Abhängigkeit nicht auflösen und müsste sie innerhalb der D-Klasse instanziieren. denn es liegt in der Verantwortung Ihrer Klassen, ihre Abhängigkeiten zu instanziieren. Es würde sogar noch schlimmer werden, wenn die D-Klasse 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.

3
Daniel

Hinweis: Ich beantworte die Frage nicht genau. Ich bin jedoch der Meinung, dass dies für neue Lernende des Dependency Injection-Musters nützlich sein kann, die mit dem Service Locator (Anti-) Pattern verwechselt werden und auf diese Seite stoßen.

Ich kenne den Unterschied zwischen dem Service Locator (er scheint jetzt als Anti-Pattern zu gelten) und Abhängigkeitsinjektionsmustern und kann jedes Beispiel mit konkreten Beispielen verstehen, aber ich wurde durch Beispiele verwirrt, die einen Service Locator im Konstruktor zeigen (nehme an, wir ' Konstruktorinjektion durchführen).

"Service Locator" wird häufig sowohl als Name eines Musters als auch als Name für das Objekt verwendet (angenommen auch), das in diesem Muster zum Abrufen von Objekten verwendet wird, ohne den neuen Operator zu verwenden. Nun kann derselbe Objekttyp auch in composition root verwendet werden, um Abhängigkeitseinspritzungen durchzuführen. Hier kommt die Verwirrung ins Spiel.

Beachten Sie, dass Sie möglicherweise ein Service Locator-Objekt innerhalb eines DI-Konstruktors verwenden, jedoch nicht das "Service Locator-Muster" verwenden. Es ist weniger verwirrend, wenn man es stattdessen als IoC-Containerobjekt bezeichnet, da Sie vielleicht schon vermutet haben, dass sie im Wesentlichen dasselbe tun (korrigieren Sie mich, wenn ich falsch liege).

Ob es sich dabei um einen Service Locator (oder nur Locator) oder um einen IoC-Container (oder nur Container) handelt, spielt keine Rolle, da Sie davon ausgehen, dass sie sich wahrscheinlich auf dieselbe Abstraktion beziehen (korrigieren Sie mich, wenn ich falsch liege ). Es ist nur so, dass der Aufruf eines Service Locators nahelegt, dass das Anti-Pattern des Service Locators zusammen mit dem Dependency Injection-Muster verwendet wird.

Wenn Sie IMHO den Namen "locator" anstelle von "location" oder "locating" nennen, kann auch der Eindruck entstehen, dass der Service Locator in einem Artikel auf den Service Locator-Container verweist und nicht auf das Service Locator-Muster (Anti-) Dies gilt insbesondere, wenn es sich um ein verwandtes Muster handelt, das als Abhängigkeitsinjektion und nicht als Abhängigkeitsinjektor bezeichnet wird.

3
blizpasta

Was ist der Unterschied (falls vorhanden) zwischen Abhängigkeitseinspritzung und Service Locator? Beide Muster eignen sich gut für die Umsetzung des Prinzips der Abhängigkeitsinversion. Das Service Locator-Muster ist in einer vorhandenen Codebase einfacher zu verwenden, da das gesamte Design lockerer wird, ohne dass Änderungen an der öffentlichen Schnittstelle erzwungen werden. Aus diesem Grund ist Code, der auf dem Service Locator-Muster basiert, weniger lesbar als gleichwertiger Code, der auf Abhängigkeitseinspritzung basiert.

Das Dependency Injection-Muster macht deutlich, seit der Signatur welche Abhängigkeiten eine Klasse (oder eine Methode) haben wird. Aus diesem Grund ist der resultierende Code sauberer und lesbarer.

1
Yogesh Joshi