Beide Muster scheinen eine Umsetzung des Prinzips der Umkehrung der Kontrolle zu sein. Das heißt, dass ein Objekt nicht wissen sollte, 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 verkabelt und foo seine Leiste 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<IBar>();
}
//...
}
Da unsere Abhängigkeiten nur Objekte selbst sind, haben diese Abhängigkeiten Abhängigkeiten, die noch mehr Abhängigkeiten aufweisen, und so weiter und so fort. So wurde die Inversion des Kontrollcontainers (oder DI-Containers) geboren. Beispiele: Castle Windsor, Ninject, Strukturkarte, Frühling usw.)
Ein IOC / DI-Container sieht jedoch genauso aus wie ein Service Locator. Ist es ein schlechter Name, wenn man es DI-Container nennt? Ist ein IOC / DI-Container nur eine andere Art von Service Locator? Liegt die Nuance in der Tatsache, dass wir DI-Container meistens verwenden, wenn wir viele Abhängigkeiten haben?
quelle
Antworten:
Der Unterschied mag geringfügig erscheinen, aber selbst mit dem ServiceLocator ist die Klasse weiterhin für das Erstellen ihrer Abhängigkeiten verantwortlich. Es wird nur der Service Locator verwendet, um dies zu tun. Mit DI erhält die Klasse ihre Abhängigkeiten. Es weiß weder, noch kümmert es, woher sie kommen. Ein wichtiges Ergebnis davon ist, dass das DI-Beispiel viel einfacher zu testen ist, da Sie es Scheinimplementierungen seiner abhängigen Objekte übergeben können. Sie können beides kombinieren - und den Service Locator (oder eine Fabrik) einspritzen, wenn Sie möchten.
quelle
Wenn Sie einen Service Locator verwenden, ist jede Klasse von Ihrem Service Locator abhängig. Dies ist bei der Abhängigkeitsinjektion nicht der Fall. Der Abhängigkeitsinjektor wird normalerweise beim Start nur einmal aufgerufen, um Abhängigkeiten in eine Hauptklasse zu injizieren. Bei den Klassen, von denen diese Hauptklasse abhängt, werden ihre Abhängigkeiten rekursiv eingefügt, bis Sie ein vollständiges Objektdiagramm haben.
Ein guter Vergleich: http://martinfowler.com/articles/injection.html
Wenn Ihr Abhängigkeitsinjektor wie ein Service Locator aussieht, bei dem die Klassen den Injector direkt aufrufen, handelt es sich wahrscheinlich nicht um einen Abhängigkeitsinjektor, sondern um einen Service Locator.
quelle
Service Locators verbergen Abhängigkeiten - Sie können anhand eines Objekts nicht erkennen, ob es auf eine Datenbank trifft oder nicht (zum Beispiel), wenn es Verbindungen von einem Locator erhält. Bei der Abhängigkeitsinjektion (zumindest der 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 mit jedem Singleton :
Bei der Abhängigkeitsinjektion haben die Abhängigkeiten eines Objekts, sobald sie angegeben wurden, die Kontrolle über das Objekt selbst.
quelle
With dependency injection (at least constructor injection) the dependencies are explicit.
. Bitte erkläre.Martin Fowler erklärt :
Kurz gesagt: Service Locator und Dependency Injection sind nur Implementierungen des Dependency Inversion Principle.
Das wichtige Prinzip lautet „Abhängig von Abstraktionen, nicht von Konkretionen“. Dadurch wird Ihr Software-Design „lose gekoppelt“, „erweiterbar“ und „flexibel“.
Sie können diejenige verwenden, die Ihren Anforderungen am besten entspricht. Für eine große Anwendung mit einer großen Codebasis sollten Sie einen Service Locator verwenden, da für die Abhängigkeitsinjektion mehr Änderungen an Ihrer Codebasis erforderlich sind.
Sie können diesen Beitrag überprüfen: Abhängigkeitsinversion: Service Locator oder Abhängigkeitsinjektion
Auch der Klassiker: Inversion von Kontrollcontainern und das Abhängigkeitsinjektionsmuster von Martin Fowler
Entwerfen wiederverwendbarer Klassen von Ralph E. Johnson & Brian Foote
Das, was mir die Augen öffnete, war jedoch: ASP.NET MVC: Auflösen oder Injizieren? Das ist das Problem… von Dino Esposito
quelle
Eine Klasse, die den Konstruktor DI verwendet, zeigt dem konsumierenden Code an, dass Abhängigkeiten erfüllt werden müssen. Wenn die Klasse den SL intern zum Abrufen solcher Abhängigkeiten verwendet, kennt der konsumierende Code die Abhängigkeiten nicht. Dies mag auf den ersten Blick besser erscheinen, aber es ist tatsächlich hilfreich, explizite Abhängigkeiten zu kennen. Aus architektonischer Sicht ist es besser. Wenn Sie Tests durchführen, müssen Sie wissen, ob eine Klasse bestimmte Abhängigkeiten benötigt, und den SL so konfigurieren, dass geeignete gefälschte Versionen dieser Abhängigkeiten bereitgestellt werden. 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 Speicherort für allgemeine Abhängigkeiten zu haben (z. B. Einstellungen, Logger usw.). Wenn eine Klasse solche deps verwendet, können Sie einen "echten" Konstruktor erstellen, der die deps empfängt, und einen Standardkonstruktor (ohne Parameter), der vom SL abgerufen und an den "echten" Konstruktor weitergeleitet wird.
BEARBEITEN: und wenn Sie den SL verwenden, führen Sie natürlich eine gewisse Kopplung an diese Komponente ein. Das ist ironisch, da die Idee einer solchen Funktionalität darin besteht, Abstraktionen zu fördern und die Kopplung zu verringern. Die Bedenken können ausgeglichen werden und es hängt davon ab, an wie vielen Stellen Sie den SL verwenden müssten. Wenn dies wie oben vorgeschlagen erfolgt, nur im Standardklassenkonstruktor.
quelle
Beide sind Implementierungstechniken von IoC. Es gibt auch andere Muster, die Inversion of Control implementieren:
Service Locator und DI Container scheinen sich ähnlicher zu sein. Beide verwenden einen Container, um Abhängigkeiten zu definieren, der die Abstraktion der konkreten Implementierung zuordnet.
Der Hauptunterschied besteht darin, wie sich die Abhängigkeiten befinden. In Service Locator fordert der Clientcode die Abhängigkeiten an. In DI Container verwenden wir einen Container, um alle Objekte zu erstellen, und er fügt Abhängigkeiten als Konstruktorparameter (oder Eigenschaften) ein.
quelle
In meinem letzten Projekt benutze ich beide. Ich verwende die Abhängigkeitsinjektion für die Einheitentestbarkeit. 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 darauf angewiesen. Und sobald es veraltet ist oder aus irgendeinem Grund, wenn Sie es austauschen möchten, müssen / müssen Sie möglicherweise Ihre Implementierung ändern - zumindest den Kompositionsstamm. Aber Service Locator abstrahiert diese Phase.
Wie würden Sie sich nicht auf Ihren IoC-Container verlassen? Entweder müssen Sie es selbst verpacken (was eine schlechte Idee ist) oder Sie verwenden Service Locator, um Ihren IoC-Container zu konfigurieren. Sie teilen dem Service Locator mit, welche Schnittstelle Sie benötigen, und rufen den IoC-Container auf, der zum Abrufen dieser Schnittstelle konfiguriert ist.
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 lediglich 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/
quelle
Ich denke, die beiden arbeiten zusammen.
Abhängigkeitsinjektion bedeutet, dass Sie eine abhängige Klasse / Schnittstelle an eine konsumierende Klasse (normalerweise an deren Konstruktor) senden. Dies entkoppelt die beiden Klassen über eine Schnittstelle und bedeutet, dass die konsumierende Klasse mit vielen Arten von Implementierungen mit "injizierter Abhängigkeit" arbeiten kann.
Die Rolle des Service Locators besteht darin, Ihre Implementierung zusammenzuführen. Sie richten zu Beginn Ihres Programms einen Service Locator über ein Boot Strapping ein. Beim Bootstrapping wird ein Implementierungstyp einer bestimmten Zusammenfassung / Schnittstelle zugeordnet. Was zur Laufzeit für Sie erstellt wird. (basierend auf Ihrer Konfiguration oder Ihrem Bootstrap). Wenn Sie die Abhängigkeitsinjektion nicht implementiert hätten, wäre es sehr schwierig, einen Service Locator oder einen IOC-Container zu verwenden.
quelle
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 "korrekt instanziiert" meine ich, dass in diesem Beispiel basierend auf der
Foo
Komponente eine Instanz vonIBar
und verfügbar sein wird und dass die Komponente, die sie bereitstellt, Folgendes tun wird:Im zweiten Beispiel , das Sie gab, wo der Konstruktor die IoC - Container geht seine Abhängigkeiten abzurufen, dass der einzige Weg , können Sie testen , dass eine Instanz der
Foo
Lage sein wird , richtig instanziiert wird mit der tatsächlichen Laufzeitkonfiguration Ihrer App ist eigentlich Konstrukt es .Dies hat zur Testzeit alle möglichen unangenehmen Nebenwirkungen, da Code, der zur Laufzeit funktioniert, nicht unbedingt unter einem Testkabel funktioniert. Mocks reichen nicht aus, da die eigentliche Konfiguration das ist, was wir testen müssen, nicht irgendein Testzeit-Setup.
Die Wurzel dieses Problems ist der Unterschied, auf den @Jon bereits hingewiesen hat: 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 beliebte 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
CompositionAssert
Beispiel in der Wiki-Dokumentation. Mit ihm können Sie Code schreiben wie:(Siehe dieses Beispiel ).
Durch Überprüfen der Zusammensetzungswurzeln Ihrer Anwendung zum Testzeitpunkt können Sie möglicherweise einige Fehler erkennen, die andernfalls später im Prozess durch den Test rutschen könnten.
Hoffe, dies ist eine interessante Ergänzung zu dieser ansonsten umfassenden Reihe von Antworten zum Thema!
quelle
Hinweis: Ich beantworte die Frage nicht genau. Ich bin jedoch der Meinung, dass dies für neue Lernende des Abhängigkeitsinjektionsmusters nützlich sein kann, die darüber mit dem Service Locator-Muster (Anti-Muster) verwechselt werden , das zufällig auf diese Seite stößt.
Ich kenne den Unterschied zwischen dem Service Locator (er scheint jetzt als Anti-Pattern angesehen zu werden) und dem Dependency Injection-Muster und kann konkrete Beispiele für jedes Muster verstehen, war jedoch durch Beispiele verwirrt, die einen Service Locator im Konstruktor zeigen (nehmen wir an, wir ' Konstruktorinjektion durchführen).
"Service Locator" wird häufig sowohl als Name eines Musters als auch als Name verwendet, um auf das in diesem Muster verwendete Objekt zu verweisen (auch angenommen), um Objekte zu erhalten, ohne den neuen Operator zu verwenden. Dieser Objekttyp kann jetzt auch im Kompositionsstamm verwendet werden , um eine Abhängigkeitsinjektion durchzuführen, und hier kommt die Verwirrung ins Spiel.
Der Punkt ist, dass Sie möglicherweise ein Service Locator-Objekt in einem DI-Konstruktor verwenden, aber nicht das "Service Locator-Muster". Es ist weniger verwirrend, wenn man es stattdessen als IoC-Containerobjekt 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 nur als Locator) oder als IoC-Container (oder nur als Container) bezeichnet wird, spielt keine Rolle, wie Sie vermuten, da sie sich wahrscheinlich auf dieselbe Abstraktion beziehen (korrigieren Sie mich, wenn ich falsch liege ). Es ist nur so, dass die Bezeichnung "Service Locator" darauf hindeutet, dass das Service Locator-Antimuster zusammen mit dem Abhängigkeitsinjektionsmuster verwendet wird.
IMHO, wenn man es als "Locator" anstelle von "Location" oder "Locating" bezeichnet, kann man manchmal auch denken, dass sich der Service Locator in einem Artikel auf den Service Locator-Container und nicht auf das Service Locator (Anti-) Muster bezieht , insbesondere wenn es ein verwandtes Muster namens Dependency Injection und nicht Dependency Injector gibt.
quelle
In diesem stark vereinfachten Fall gibt es keinen Unterschied und sie können austauschbar verwendet werden. Probleme in der realen Welt sind jedoch nicht so einfach. Nehmen Sie einfach an, dass die Bar-Klasse selbst eine andere Abhängigkeit mit dem Namen D hatte. In diesem Fall könnte Ihr Service Locator diese Abhängigkeit nicht auflösen, und Sie müssten sie innerhalb der D-Klasse instanziieren. weil es in der Verantwortung Ihrer Klassen liegt, ihre Abhängigkeiten zu instanziieren. Es würde 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.
quelle
bar
Klasse selbst eine Abhängigkeit hat, dann hatbar
sie auch einen Service Locator, das ist der springende Punkt bei der Verwendung von DI / IoC.Was ist der Unterschied (falls vorhanden) zwischen Dependency Injection und Service Locator? Beide Muster können das Prinzip der Abhängigkeitsinversion gut implementieren. Das Service Locator-Muster ist in einer vorhandenen Codebasis einfacher zu verwenden, da es das gesamte Design lockert, ohne Änderungen an der öffentlichen Schnittstelle zu erzwingen. Aus demselben Grund ist Code, der auf dem Service Locator-Muster basiert, weniger lesbar als gleichwertiger Code, der auf Dependency Injection basiert.
Das Abhängigkeitsinjektionsmuster macht seit der Signatur deutlich, welche Abhängigkeiten eine Klasse (oder eine Methode) haben wird. Aus diesem Grund ist der resultierende Code sauberer und besser lesbar.
quelle
Die folgende einfache Konzeption gab mir ein klareres Verständnis des Unterschieds zwischen Service Locator und DI Container:
Service Locator wird im Consumer verwendet und ruft Services per ID auf direkte Anfrage des Verbrauchers aus einem Speicher ab
DI Container irgendwo außerhalb gelegen und es dauert Dienstleistungen aus einer gewissen Lagerung und drückt sie an den Verbraucher (egal über Konstruktor oder über Verfahren)
Wir können jedoch nur im Zusammenhang mit der konkreten Nutzung durch die Verbraucher über Unterschiede zwischen diesen sprechen. Wenn Service Locator und DI Container in Composition Root verwendet werden, sind sie fast ähnlich.
quelle
DI-Container ist eine Obermenge von Service Locator. Es kann verwendet werden, um einen Dienst zu lokalisieren , mit der zusätzlichen Fähigkeit, die Injektionen der Abhängigkeit zusammenzusetzen (zu verdrahten) .
quelle
Für die Aufzeichnung
Sofern Sie nicht wirklich eine Schnittstelle benötigen (die Schnittstelle wird von mehr als einer Klasse verwendet), dürfen Sie sie NICHT verwenden . In diesem Fall ermöglicht IBar die Verwendung jeder Serviceklasse, die diese implementiert. Normalerweise wird diese Schnittstelle jedoch von einer einzelnen Klasse verwendet.
Warum ist es eine schlechte Idee, eine Schnittstelle zu verwenden? Weil es wirklich schwer zu debuggen ist.
Angenommen, die Instanz "bar" ist fehlgeschlagen. Frage: Welche Klasse ist fehlgeschlagen? Welchen Code sollte ich reparieren? Eine einfache Ansicht, die zu einer Schnittstelle führt, und hier endet meine Straße.
Wenn der Code stattdessen eine harte Abhängigkeit verwendet, ist es einfach, einen Fehler zu debuggen.
Wenn "bar" fehlschlägt, sollte ich die Klasse BarService überprüfen und auslösen.
quelle
contract
und definiert nur ein Verhalten, nicht die Aktion. Anstatt das eigentliche Objekt weiterzugeben, wird nur die Schnittstelle gemeinsam genutzt, damit der Verbraucher nicht auf den Rest Ihres Objekts zugreift. Auch beim Testen von Einheiten hilft es, nur das Teil zu testen, das getestet werden muss. Ich denke, mit der Zeit würden Sie seine Nützlichkeit verstehen.