Ich verstehe, dass das direkte Instanziieren von Abhängigkeiten innerhalb einer Klasse als schlechte Praxis angesehen wird. Dies ist sinnvoll, da alles so eng miteinander verbunden ist, was wiederum das Testen sehr schwierig macht.
Fast alle Frameworks, auf die ich gestoßen bin, scheinen die Abhängigkeitsinjektion mit einem Container der Verwendung von Service-Locators vorzuziehen. Beide scheinen dasselbe zu erreichen, indem sie es dem Programmierer ermöglichen, anzugeben, welches Objekt zurückgegeben werden soll, wenn eine Klasse eine Abhängigkeit erfordert.
Was ist der Unterschied zwischen den beiden? Warum sollte ich eine der anderen vorziehen?
dependency-injection
ioc-containers
service-locator
tom6025222
quelle
quelle
Antworten:
Wenn das Objekt selbst dafür verantwortlich ist, seine Abhängigkeiten anzufordern, anstatt sie über einen Konstruktor zu akzeptieren, werden einige wichtige Informationen ausgeblendet. Es ist nur geringfügig besser als der sehr eng gekoppelte Fall
new
, Abhängigkeiten mithilfe von zu instanziieren. Es reduziert die Kopplung, da Sie tatsächlich die Abhängigkeiten ändern können, die es erhält, aber es hat immer noch eine Abhängigkeit, die es nicht erschüttern kann: den Service-Locator. Das wird das, wovon alles abhängt.Ein Container, der Abhängigkeiten über Konstruktorargumente bereitstellt, bietet die größte Übersichtlichkeit. Wir sehen von vornherein, dass ein Objekt sowohl ein
AccountRepository
als auch ein brauchtPasswordStrengthEvaluator
. Wenn Sie einen Service Locator verwenden, werden diese Informationen nicht sofort angezeigt. Sie würden sofort einen Fall sehen, in dem ein Objekt 17 Abhängigkeiten hat, und sich selbst sagen: "Hmm, das scheint viel zu sein. Was ist dort los?" Anrufe an einen Service Locator können sich auf die verschiedenen Methoden verteilen und sich hinter bedingter Logik verstecken, und Sie bemerken möglicherweise nicht, dass Sie eine "Gott-Klasse" erstellt haben - eine, die alles tut. Vielleicht könnte diese Klasse in 3 kleinere Klassen umgestaltet werden, die fokussierter und daher überprüfbarer sind.Betrachten Sie nun das Testen. Wenn ein Objekt einen Service-Locator verwendet, um seine Abhängigkeiten zu ermitteln, benötigt Ihr Test-Framework auch einen Service-Locator. In einem Test konfigurieren Sie den Service-Locator so, dass die Abhängigkeiten für das zu testende Objekt bereitgestellt werden - möglicherweise ein
FakeAccountRepository
und einVeryForgivingPasswordStrengthEvaluator
, und führen Sie dann den Test aus. Dies ist jedoch mehr Arbeit als die Angabe von Abhängigkeiten im Konstruktor des Objekts. Und Ihr Test-Framework wird auch vom Service Locator abhängig. Es ist eine andere Sache, die Sie in jedem Test konfigurieren müssen, was das Schreiben von Tests weniger attraktiv macht.Suchen Sie nach "Serivce Locator ist ein Anti-Pattern" für Mark Seemans Artikel darüber. Wenn Sie in der .Net-Welt sind, holen Sie sich sein Buch. Es ist sehr gut.
quelle
constructor supplied dependencies
vs hinzufügen muss,service locator
ist, dass das erstere zur Kompilierungszeit überprüft werden kann, während das letztere nur zur Laufzeit überprüft werden kann.But that's more work than specifying dependencies in the object's constructor.
ich möchte Einwände erheben. Mit einem Service Locator müssen Sie nur die 3 Abhängigkeiten angeben, die Sie tatsächlich für Ihren Test benötigen. Bei einer konstruktorbasierten DI müssen Sie ALLE 10 angeben, auch wenn 7 nicht verwendet werden.Stellen Sie sich vor, Sie sind Arbeiter in einer Fabrik, die Schuhe herstellt .
Sie sind verantwortlich für die Montage der Schuhe und so werden Sie eine Menge Dinge, um das zu tun.
Und so weiter.
Sie arbeiten in der Fabrik und können loslegen. Sie haben eine Liste mit Anweisungen, wie Sie vorgehen sollen, aber Sie haben noch keine Materialien oder Werkzeuge.
Ein Service Locator ist wie ein Foreman, der Ihnen dabei hilft, das zu bekommen, was Sie brauchen.
Sie fragen den Service Locator jedes Mal, wenn Sie etwas benötigen, und er sucht es für Sie. Dem Service Locator wurde im Voraus mitgeteilt, wonach Sie wahrscheinlich fragen und wie Sie ihn finden.
Du solltest besser hoffen, dass du nicht nach etwas Unerwartetem fragst. Wenn der Locator nicht im Voraus über ein bestimmtes Werkzeug oder Material informiert wurde, kann er es nicht für Sie besorgen und zuckt nur mit den Schultern.
Ein Dependency Injection (DI) -Container ist wie eine große Kiste, die zu Beginn des Tages mit allem gefüllt wird, was jeder braucht.
Beim Start der Fabrik greift der Big Boss, der als Composition Root bekannt ist, nach dem Container und übergibt alles an die Linienmanager .
Die Linienvorgesetzten haben jetzt das, was sie für ihre täglichen Aufgaben benötigen. Sie nehmen das, was sie haben und geben das, was sie brauchen, an ihre Untergebenen weiter.
Dieser Prozess setzt sich fort, wobei Abhängigkeiten die Produktionslinie durchdringen. Schließlich erscheint ein Behälter mit Materialien und Werkzeugen für Ihren Vorarbeiter.
Ihr Vorarbeiter verteilt jetzt genau das, was Sie benötigen, an Sie und andere Mitarbeiter, ohne dass Sie danach fragen.
Sobald Sie zur Arbeit erscheinen, ist praktisch alles, was Sie brauchen, bereits in einer Kiste für Sie bereit. Du musstest nichts darüber wissen, wie du sie bekommst.
quelle
Ein paar zusätzliche Punkte, die ich beim Durchsuchen des Webs gefunden habe:
quelle
Ich komme zu spät zu dieser Party, aber ich kann nicht widerstehen.
Manchmal überhaupt keine. Was den Unterschied ausmacht, ist, was über was weiß.
Sie wissen, dass Sie einen Service-Locator verwenden, wenn der Client, der die Abhängigkeit sucht, über den Container Bescheid weiß. Ein Client, der weiß, wie er seine Abhängigkeiten findet, auch wenn er einen Container durchsucht, um sie abzurufen, ist das Service-Locator-Muster.
Heißt das, wenn Sie den Service Locator umgehen möchten, können Sie keinen Container verwenden? Nein. Sie müssen nur Kunden davon abhalten, über den Container Bescheid zu wissen. Der Hauptunterschied besteht darin, wo Sie den Container verwenden.
Sagen wir
Client
BedürfnisseDependency
. Der Behälter hat eineDependency
.Wir haben uns gerade an das Suchmuster gehalten, weil wir
Client
wissen, wie man es findetDependency
. Sicher , es wird ein hart codiert ,ClassPathXmlApplicationContext
aber auch wenn Sie injizieren , dass Sie noch einen Service Locator da habenClient
Anrufebeanfactory.getBean()
.Um den Service Locator zu umgehen, müssen Sie diesen Container nicht verlassen. Sie müssen es nur herausziehen,
Client
damit SieClient
nichts davon wissen.Beachten Sie, wie
Client
jetzt keine Ahnung hat, dass der Container existiert:Verschieben Sie den Container aus allen Clients und platzieren Sie ihn in main, um einen Objektgraphen aller Ihrer langlebigen Objekte zu erstellen. Wählen Sie eines dieser Objekte aus, um es zu extrahieren und eine Methode aufzurufen, und Sie beginnen mit dem Ticking des gesamten Diagramms.
Dadurch wird die gesamte statische Konstruktion in die XML-Container verschoben, und Ihre Kunden wissen nicht, wie sie ihre Abhängigkeiten finden können.
Aber main weiß immer noch, wie man Abhängigkeiten findet! Ja tut es. Indem Sie dieses Wissen jedoch nicht verbreiten, vermeiden Sie das Kernproblem des Service Locator. Die Entscheidung, einen Container zu verwenden, wird nun an einem Ort getroffen und kann geändert werden, ohne dass Hunderte von Clients neu geschrieben werden müssen.
quelle
Ich denke, der einfachste Weg, um den Unterschied zwischen den beiden zu verstehen und warum ein DI-Container so viel besser ist als ein Service-Locator, ist zu überlegen, warum wir überhaupt eine Abhängigkeitsinversion durchführen.
Wir führen eine Abhängigkeitsinversion durch, damit jede Klasse explizit angibt, wovon sie für die Operation abhängig ist . Wir tun dies, weil dies die lockerste Kopplung schafft, die wir erreichen können. Je lockerer die Kopplung ist, desto einfacher ist es, etwas zu testen und umzugestalten (und in der Regel ist das geringste Refactoring in der Zukunft erforderlich, da der Code sauberer ist).
Schauen wir uns die folgende Klasse an:
In dieser Klasse geben wir ausdrücklich an, dass wir einen IOutputProvider und nichts anderes benötigen, damit diese Klasse funktioniert. Dies ist vollständig testbar und hängt von einer einzelnen Schnittstelle ab. Ich kann diese Klasse an eine beliebige Stelle in meiner Anwendung verschieben, einschließlich eines anderen Projekts. Sie benötigt lediglich Zugriff auf die IOutputProvider-Schnittstelle. Wenn andere Entwickler dieser Klasse etwas Neues hinzufügen möchten, was eine zweite Abhängigkeit erfordert, müssen sie explizit angeben, was sie im Konstruktor benötigen.
Schauen Sie sich dieselbe Klasse mit einem Service-Locator an:
Jetzt habe ich den Service Locator als Abhängigkeit hinzugefügt. Hier sind die Probleme, die sofort offensichtlich sind:
Warum machen wir den Service Locator nicht zu einer statischen Klasse? Lass uns mal sehen:
Das ist doch viel einfacher, oder?
Falsch.
Angenommen, IOutputProvider wird von einem sehr lang laufenden Webdienst implementiert, der die Zeichenfolge in fünfzehn verschiedenen Datenbanken auf der ganzen Welt schreibt und dessen Fertigstellung sehr lange dauert.
Lassen Sie uns versuchen, diese Klasse zu testen. Für den Test benötigen wir eine andere Implementierung von IOutputProvider. Wie schreiben wir den Test?
Dazu müssen wir einige ausgefallene Konfigurationen in der statischen ServiceLocator-Klasse vornehmen, um eine andere Implementierung von IOutputProvider zu verwenden, wenn dieser vom Test aufgerufen wird. Sogar das Schreiben dieses Satzes war schmerzhaft. Die Umsetzung wäre mühsam und ein Alptraum für die Instandhaltung . Wir sollten niemals eine Klasse speziell zum Testen modifizieren müssen, insbesondere wenn diese Klasse nicht die Klasse ist, die wir tatsächlich testen möchten.
Jetzt haben Sie entweder a) einen Test, der auffällige Codeänderungen in der nicht verwandten ServiceLocator-Klasse verursacht; oder b) überhaupt kein Test. Und Ihnen bleibt auch eine weniger flexible Lösung.
So ist die Service - Locator - Klasse hat in den Konstruktor injiziert werden. Was bedeutet, dass wir mit den zuvor erwähnten spezifischen Problemen zurückbleiben. Der Service Locator benötigt mehr Code, teilt anderen Entwicklern mit, dass er Dinge benötigt, die er nicht benötigt, ermutigt andere Entwickler, schlechteren Code zu schreiben, und gibt uns weniger Flexibilität bei der Weitergabe.
Einfach ausgedrückt: Service Locators erhöhen die Kopplung in einer Anwendung und ermutigen andere Entwickler, stark gekoppelten Code zu schreiben .
quelle