Kürzlich habe ich Mark Seemanns Artikel über Service Locator Anti-Pattern gelesen .
Der Autor weist auf zwei Hauptgründe hin, warum ServiceLocator ein Anti-Pattern ist:
API-Verwendungsproblem (mit dem ich vollkommen
einverstanden bin) Wenn die Klasse einen Service Locator verwendet, ist es sehr schwierig, ihre Abhängigkeiten zu erkennen, da die Klasse in den meisten Fällen nur einen PARAMETERLESS-Konstruktor hat. Im Gegensatz zu ServiceLocator macht der DI-Ansatz Abhängigkeiten explizit über Konstruktorparameter verfügbar, sodass Abhängigkeiten in IntelliSense leicht erkennbar sind.Wartungsproblem (was mich
verwirrt ) Betrachten Sie das folgende Beispiel
Wir haben eine Klasse 'MyType', die einen Service Locator-Ansatz verwendet:
public class MyType
{
public void MyMethod()
{
var dep1 = Locator.Resolve<IDep1>();
dep1.DoSomething();
}
}
Jetzt möchten wir der Klasse 'MyType' eine weitere Abhängigkeit hinzufügen.
public class MyType
{
public void MyMethod()
{
var dep1 = Locator.Resolve<IDep1>();
dep1.DoSomething();
// new dependency
var dep2 = Locator.Resolve<IDep2>();
dep2.DoSomething();
}
}
Und hier beginnt mein Missverständnis. Der Autor sagt:
Es wird viel schwieriger zu sagen, ob Sie eine bahnbrechende Änderung einführen oder nicht. Sie müssen die gesamte Anwendung verstehen, in der der Service Locator verwendet wird, und der Compiler wird Ihnen nicht helfen.
Aber warten Sie eine Sekunde, wenn wir den DI-Ansatz verwenden, würden wir eine Abhängigkeit mit einem anderen Parameter im Konstruktor einführen (im Fall einer Konstruktorinjektion). Und das Problem wird immer noch da sein. Wenn wir möglicherweise vergessen, ServiceLocator einzurichten, vergessen wir möglicherweise, eine neue Zuordnung in unseren IoC-Container einzufügen, und der DI-Ansatz hätte das gleiche Laufzeitproblem.
Auch der Autor erwähnte über Unit-Test-Schwierigkeiten. Aber haben wir nicht Probleme mit dem DI-Ansatz? Müssen wir nicht alle Tests aktualisieren, die diese Klasse instanziiert haben? Wir werden sie aktualisieren, um eine neue verspottete Abhängigkeit zu übergeben, nur um unseren Test kompilierbar zu machen. Und ich sehe keine Vorteile aus diesem Update und dem Zeitaufwand.
Ich versuche nicht, den Service Locator-Ansatz zu verteidigen. Aber dieses Missverständnis lässt mich denken, dass ich etwas sehr Wichtiges verliere. Könnte jemand meine Zweifel zerstreuen?
UPDATE (ZUSAMMENFASSUNG):
Die Antwort auf meine Frage "Ist Service Locator ein Anti-Pattern" hängt wirklich von den Umständen ab. Und ich würde definitiv nicht vorschlagen, es von Ihrer Werkzeugliste zu streichen. Dies kann sehr praktisch sein, wenn Sie mit Legacy-Code arbeiten. Wenn Sie das Glück haben, ganz am Anfang Ihres Projekts zu stehen, ist der DI-Ansatz möglicherweise die bessere Wahl, da er einige Vorteile gegenüber Service Locator bietet.
Und hier sind die Hauptunterschiede, die mich überzeugt haben, Service Locator nicht für meine neuen Projekte zu verwenden:
- Am offensichtlichsten und wichtigsten: Service Locator verbirgt Klassenabhängigkeiten
- Wenn Sie einen IoC-Container verwenden, werden wahrscheinlich alle Konstruktoren beim Start gescannt, um alle Abhängigkeiten zu überprüfen und Ihnen sofort Feedback zu fehlenden Zuordnungen (oder falscher Konfiguration) zu geben. Dies ist nicht möglich, wenn Sie Ihren IoC-Container als Service Locator verwenden
Für Details lesen Sie ausgezeichnete Antworten, die unten gegeben werden.
Antworten:
Wenn Sie Muster als Anti-Muster definieren, nur weil es Situationen gibt, in denen sie nicht passen, dann ist JA ein Anti-Muster. Aber mit dieser Überlegung wären alle Muster auch Anti-Muster.
Stattdessen müssen wir prüfen, ob die Muster gültig verwendet werden, und für Service Locator gibt es mehrere Anwendungsfälle. Aber schauen wir uns zunächst die Beispiele an, die Sie gegeben haben.
Der Wartungs-Albtraum bei dieser Klasse ist, dass die Abhängigkeiten ausgeblendet sind. Wenn Sie diese Klasse erstellen und verwenden:
Sie verstehen nicht, dass es Abhängigkeiten gibt, wenn diese über den Dienstort ausgeblendet werden. Wenn wir stattdessen die Abhängigkeitsinjektion verwenden:
Sie können die Abhängigkeiten direkt erkennen und die Klassen nicht verwenden, bevor Sie sie erfüllt haben.
In einer typischen Geschäftsanwendung sollten Sie aus diesem Grund die Verwendung des Servicestandorts vermeiden. Es sollte das Muster sein, das verwendet werden soll, wenn keine anderen Optionen vorhanden sind.
Ist das Muster ein Anti-Muster?
Nein.
Zum Beispiel würde die Inversion von Steuercontainern ohne den Servicestandort nicht funktionieren. So lösen sie die Dienste intern auf.
Ein viel besseres Beispiel ist jedoch ASP.NET MVC und WebApi. Was macht Ihrer Meinung nach die Abhängigkeitsinjektion in den Controllern möglich? Das ist richtig - Servicestandort.
Deine Fragen
Es gibt zwei weitere schwerwiegende Probleme:
Bei der Konstruktorinjektion mit einem Container erhalten Sie diese kostenlos.
Das ist richtig. Mit der Konstruktorinjektion müssen Sie jedoch nicht die gesamte Klasse scannen, um herauszufinden, welche Abhängigkeiten fehlen.
Einige bessere Container überprüfen auch alle Abhängigkeiten beim Start (indem sie alle Konstruktoren scannen). Mit diesen Containern erhalten Sie den Laufzeitfehler also direkt und nicht zu einem späteren Zeitpunkt.
Nein, da Sie nicht von einem statischen Service Locator abhängig sind. Haben Sie versucht, parallele Tests mit statischen Abhängigkeiten durchzuführen? Das ist kein Spaß.
quelle
Ich möchte auch darauf hinweisen, dass das Service Locator-Muster nicht nur kein Anti-Pattern ist, sondern auch eine praktische Notwendigkeit, wenn Sie Legacy-Code umgestalten. Niemand wird jemals einen Zauberstab über Millionen von Codezeilen schwingen und plötzlich wird der gesamte Code DI-fähig sein. Wenn Sie also mit der Einführung von DI in eine vorhandene Codebasis beginnen möchten, ändern Sie häufig Dinge, um langsam zu DI-Diensten zu werden, und der Code, der auf diese Dienste verweist, ist häufig KEIN DI-Dienst. Daher müssen DIESE Dienste den Service Locator verwenden, um Instanzen der Dienste abzurufen, die für die Verwendung von DI konvertiert wurden.
Wenn ich also große Legacy-Anwendungen umgestalte, um DI-Konzepte zu verwenden, würde ich sagen, dass Service Locator NICHT nur kein Anti-Pattern ist, sondern dass dies die einzige Möglichkeit ist, DI-Konzepte schrittweise auf die Codebasis anzuwenden.
quelle
Aus Testsicht ist Service Locator schlecht. Siehe Misko Heverys Google Tech Talk, eine nette Erklärung mit Codebeispielen http://youtu.be/RlfLCWKxHJ0 ab Minute 8:45. Ich mochte seine Analogie: Wenn Sie 25 Dollar brauchen, fragen Sie direkt nach Geld, anstatt Ihre Brieftasche zu geben, von wo Geld genommen wird. Er vergleicht Service Locator auch mit einem Heuhaufen, der die Nadel hat, die Sie benötigen, und weiß, wie man sie abruft. Klassen, die Service Locator verwenden, sind daher schwer wiederzuverwenden.
quelle
Es gibt zwei verschiedene Gründe, warum die Verwendung von Service Locator in dieser Hinsicht schlecht ist.
Schlicht und einfach: Eine Klasse mit einem Service Locator ist schwieriger wiederzuverwenden als eine Klasse , die ihre Abhängigkeiten über ihren Konstruktor akzeptiert.
quelle
Mein Wissen ist nicht gut genug, um dies zu beurteilen, aber im Allgemeinen denke ich, wenn etwas in einer bestimmten Situation einen Nutzen hat, bedeutet dies nicht unbedingt, dass es kein Anti-Muster sein kann. Insbesondere wenn Sie mit Bibliotheken von Drittanbietern arbeiten, haben Sie nicht die volle Kontrolle über alle Aspekte und verwenden möglicherweise die nicht sehr beste Lösung.
Hier ist ein Absatz aus Adaptive Code Via C # :
quelle
Der Autor begründet, dass "der Compiler Ihnen nicht hilft" - und das ist wahr. Wenn Sie eine Klasse entwerfen, sollten Sie ihre Benutzeroberfläche sorgfältig auswählen - unter anderem, um sie so unabhängig wie möglich zu machen.
Indem der Client den Verweis auf einen Dienst (auf eine Abhängigkeit) über eine explizite Schnittstelle akzeptiert, können Sie
Sie haben Recht, dass DI seine Probleme / Nachteile hat, aber die genannten Vorteile überwiegen bei weitem ... IMO. Sie haben Recht, dass mit DI eine Abhängigkeit in die Schnittstelle (Konstruktor) eingeführt wurde - aber dies ist hoffentlich genau die Abhängigkeit, die Sie benötigen und die Sie sichtbar und überprüfbar machen möchten.
quelle
Ja, Service Locator ist ein Anti-Pattern, das die Kapselung verletzt und fest ist .
quelle