Wie zu vermeiden, Service-Locator Anti-Pattern?
Ich versuche zu entfernen, einen Service Locator von einer abstrakten Klasse, aber ich bin mir nicht sicher, was zu ersetzen. Hier ist ein Pseudo-Beispiel, was ich habe:
public abstract class MyController : Controller
{
protected IKernel kernel;
public MyController(IKernel kernel) { this.kernel = kernel); }
protected void DoActions(Type[] types)
{
MySpecialResolver resolver = new MySpecialResolver(kernel);
foreach(var type in types)
{
IMyServiceInterface instance = resolver.Get(type);
instance.DoAction();
}
}
}
Den problem mit dieser ist, dass die instanciator einer abgeleiteten Klasse muss nicht wissen, welche Bindungen die kernel haben muss, um zu halten MySpecialResolver
aus eine Ausnahme zu werfen.
Könnte dies an sich unlösbar, denn ich weiß nicht von hier, die Typen, die ich lösen müssen. Die abgeleiteten Klassen sind verantwortlich für die Erstellung der types
parameter, aber Sie sind nicht hardcoded überall. (Die Arten werden basierend auf dem Vorhandensein der Attribute, die tief in der abgeleiteten Klasse Komposition, Hierarchie) an.
Habe ich versucht zu reparieren, das mit lazy loading Delegierten, aber bisher habe ich noch keine saubere Lösung.
Update
Gibt es wirklich hier zwei Probleme, das eine ist, dass die IoC-Containers übergeben wird, der controller fungiert als service-locator. Dieser ist leicht zu entfernen-Sie können verschieben die Position nach oben oder unten auf dem Aufruf-stack mit allen möglichen Techniken.
Das zweite Problem ist die schwierige, wie können Sie sicherstellen, dass der controller hat die erforderlichen Leistungen, wenn die Anforderungen nicht ausgesetzt, die erst zur Laufzeit. Es sollte gewesen offensichtlich von Anfang an: das kannst du nicht! Sie werden immer abhängig sein von entweder dem aktuellen Status des service locator oder den Inhalt einer Sammlung. In diesem speziellen Fall keine Menge hantieren jemals Behebung des Problems beschrieben, in dieser Artikel mit staticly typisierte Abhängigkeiten. Ich denke, dass das, was ich werde zu tun ist die übergabe eines Lazy-array in dem controller-Konstruktor und eine Ausnahme werfen, wenn eine erforderliche Abhängigkeit fehlt.
- Ähm, ich weiß, das ist nicht die trendige Antwort, aber Service Locator ist nicht ein anti-pattern. Es ist eine alternative zu der Verwendung von einem IoC-container, aber Sie können dependency injection mit ein (es ist ein bisschen seltsam, injizieren von Abhängigkeiten in Dienst-Lage-Klassen, aber nicht unmöglich). Das heißt, wenn Sie einen IoC-container, und dann mit einem Service-Locator sowie würde wahrscheinlich qualifizieren als code smell.
- Wie es aussieht, bist du mit mvc und möglicherweise Windsor, aber ich bin mir nicht sicher - wenn ja, können Sie commonservicelocator.codeplex.com und dann irgendwo in der Application_Start-call ServiceLocator.SetLocatorProvider(() => { return new WindsorServiceLocator(_container); });DependencyResolver.SetResolver(ServiceLocator.Strom); Dann dies wird Ihnen ermöglichen, zu injizieren und Ihre Abhängigkeiten in den controller-Konstruktoren. Andere Container unterstützt werden, aber ich habe nur verwendet Windsor für diese
- Paul, können Sie hinzufügen, ein Beispiel für die abgeleitete Klasse Attribut Abhängigkeit? Sind die Attribute der abgeleiteten controller und die daraus abgeleiteten controller-Abhängigkeiten?
- diese basieren auf den abgeleiteten Controller-Eigenschaften-befindet sich in einer anderen assembly. So, während die benötigten Abhängigkeiten sind technisch statischen zur build-Zeit, wird der Netto-Effekt ist, dass Sie nicht bekannt sind, um den IoC-container oder unit-test, bis der controller instanziiert wurde.
- Macht Sinn. Meine Antwort kann immer noch mit dieser situation umzugehen. Wir können starten einer chat-Sitzung, wenn Sie wollen, mehr zu besprechen.
- Paul, Sie sollten wirklich posten Sie Ihre Lösung (mit code, vorzugsweise als Antwort und akzeptieren es. Ihr update nur irgendwie ließ uns hängen. Ich bin mir nicht sicher, ich Stimme mit deiner Lösung, aber es ist Ihr Vorrecht zu entscheiden.
- Ich weiß nicht wirklich einverstanden mit Ihrer Antwort...wie @chrischris sagte, es klingt wie Sie Ihre Basis-controller hat zu viele Aufgaben und sollten Sie diesen service-Aufrufe an den richtigen stellen. Aber Sie haben das Letzte Wort.
- was ich geschrieben habe, ist die Lösung, die ich mit gestartet. Ich mochte es nicht, also fragte ich die Frage hier. Ich will nicht zu posten/zu akzeptieren, eine irreführende Antwort. Ich bin mir nicht sicher, ob es eine Lösung gibt, führt meine (zugegebenermaßen etwas willkürlich) Ziele für den oben genannten Gründen. Architektur-Probleme sind nicht immer sofort klar, also für den moment ich werde versuchen, das zu machen, was ich habe, zu arbeiten und wieder diese Frage mal ich hab ein klareres Verständnis von den Auswirkungen.
- fair genug. Ich hoffe, Sie finden die richtige Lösung für Ihre situation.
- Obwohl es ein "Anti-Muster", ich habe es nützlich gefunden, die passieren in der Kernel-Schnittstelle, vor allem, wenn es darum geht zu testen und Ninject.Mock und pass in eine Mock version des Kernels.
- Was wollen Sie erreichen? Möchte einfach nur eine Liste der Instanzen des angegebenen Typen? Warum ist Ihre Steuerung haben etwas zu tun mit den Typen und nicht die Rolle-Schnittstellen?
- Aus irgendeinem Grund war es nicht immediently mir klar, fast ein Jahr her, als ich diese Frage gestellt, aber es ist keine Lösung für dieses problem. Der Grund dafür ist, dass in diesem Fall der controller ist eine abstrakte Basisklasse, die einfach NICHT WISSEN, welche Dienste es müssen erst zur Laufzeit. Sehen Sie, wo die
DoAction
Methode wird immer eine Liste der Typen? Der controller kann nicht wissen, zur compile-Zeit, welche Arten es wird schließlich zur Verfügung gestellt werden, wie diese durch die Unterklassen. Ohne dieses wissen ist es unmöglich zu vermeiden, das problem von einer falsch initialisiert service-locator. - es ist keine missbräuchliche Verwendung eines service locator, wenn Sie wirklich tun service Lage.
DoActions(Type[] types)
ist ein text-Buch-Beispiel von service-Standort. Der service locator ist eine grundlegende Muster, die keine alternativen, außer schlechte Implementierungen des service-locator. Der service locator ist ein elementarer Baustein aller IOC-Container. Das service locator pattern ist nicht ein anti-pattern, und ich vehement widersprechen Mark Seemann mit seinem deklarieren, dass es so wäre. Falsche Anwendung des Musters ist die anti-pattern. - "Service-Position" sollte bei der Zusammensetzung root. Der Begriff "anti-pattern" ist nicht eine intrinsische, absolute Eigenschaft kann man Rational argumentieren, etwas, das 'ist' oder 'ist nicht', sondern ein zugeschrieben, basierend auf der menschlichen Beobachtung und Erfahrung. In diesem Fall kann ich persönlich bezeugen, um die Gefahren der Streuung service locator-Aufrufe, wie diese in der gesamten Anwendung.
- mit großer macht kommt große Verantwortung. Der service locator wird als lose gekoppelt als code können Sie immer werden (kurz -, null-Kopplung, was bedeutet, dass keinerlei Interaktionen), die Locator-Punkte willkürlich wäre in der Tat gefährlich sein. Nur weil Messer gefährlich sind, bedeutet nicht, Sie machen jedes Messer ein Messer in butter, obwohl Sie müssen ein steak-Messer. Einfachstes Beispiel ist
Validator.Validate(obj)
dass nicht Richtlinien-basierte Validierung, basierend auf den Inhalt von obj. Das kann einfach nicht sein fertig in der Zusammensetzung root kurzen kleben voodoo in den container.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Stimme ich mit @chrisichris und @Mark Seemann.
Graben der kernel aus dem controller. Ich würde wechseln, resolver Zusammensetzung ein wenig, so dass Ihr controller kann entfernen Sie die Abhängigkeit von den IoC-container und erlauben der resolver die einzige Sache, die sorgen über den IoC-container.
Dann, ich würde das lassen-resolver übergeben bekommen in den Konstruktor des Controllers. Dies ermöglicht es dem controller weit mehr getestet werden.
Beispiel:
Jetzt deinen controller nicht gekoppelt an einen bestimmten IoC container. Auch Ihre Steuerung ist viel mehr getestet werden, da Sie verspotten den Resolver und nicht verlangen, einen IoC-container für Ihre tests.
Alternativ, wenn Sie nicht bekommen, um zu Steuern, Wann ein controller instanziiert wird, kann man Sie leicht modifizieren:
Würden Sie dann rufen Sie diese an Ihrem einen Neustart der Anwendung zur Initialisierung der resolver:
Wir Taten dies, um Griff-Elemente in XAML erstellt, die Abhängigkeiten aufgelöst, aber wir wollten zu entfernen Service Locator gerne Anfragen.
Bitte entschuldigt eventuelle syntaktische Fehler 🙂
Schreibe ich einen blog-post-Serie zum Thema refactoring einer MVVM-Anwendung mit dem Service Locator-Aufrufe in der Sicht Modelle, die Sie interessant finden könnte. Teil 2 kommt bald 🙂
http://kellabyte.com/2011/07/24/refactoring-to-improve-maintainability-and-blendability-using-ioc-part-1-view-models/
Kernal
Abhängigkeit benötigt wird.MyController
. Jeden einzelnen Dienst ein eigenes stand-alone-test. Dies ist einer der größten Vorteile für die richtige service-Standort. Sie haben extrem prüfbar Dienstleistungen, die Sie verfassen und die Reihenfolge zu tun großartige Dinge. Zum testen der Zusammensetzung ein full-stack testen, sonst ist es sinnlos.MyController
in Ihrer integration testen, konfigurieren Sie den locator return X, Y, Z. Nun alle Prüfungen sind vorbei und Sie haben ein Falsches Gefühl der Sicherheit. Ein Entwickler geht und verändert die service-Registrierung-code return A, B, C. Ihre integration test ist vorbei, aber Ihre Website zum Absturz zu bringen.Vielleicht sollte man einfach tun, Weg der Kernel -, Arten-und MySpecialResolver und lassen Sie die Unterklassen rufen DoActions mit der IMyServiceInterface Instanzen, die Sie benötigen als argument direkt. Und lassen Sie die Unterklassen entscheiden, wie Sie zu diesen Fällen sollten Sie am besten wissen (oder in den Fall, Sie nicht wissen, welche genau, wer überhaupt entscheidet, welche Instanzen von IMyServiceInterface benötigt werden)
Ich hätte gerne ein bisschen mehr Informationen, bevor dieses posting Antworten, aber Kelly hat mich auf der Stelle. 🙂 Sagt mir mein code, wo mein Mund ist, so zu sprechen.
Wie ich schon sagte in meinem Kommentar zu Kelly, ich bin nicht einverstanden mit der Verschiebung des resolver/locator von einer statischen Implementierung einer injizierten Umsetzung. Ich Stimme mit ChrisChris, dass die Abhängigkeiten der abgeleiteten Typs muss geklärt werden sollten, die Klasse und nicht delegiert, um die base-Klasse.
Gesagt, hier ist, wie würde ich entfernen, das service location...
Erstellen-Befehl-Schnittstelle
Zuallererst würde ich erstellen Sie eine Kommando-Schnittstelle für die konkrete Implementierung. In diesem Fall sind die Typen gesendet, mit der DoActions Methode erzeugt aus Parametern, so würde ich erstellen Sie eine
IAttributeCommand
. Ich bin das hinzufügen einerMatches
Methode, um den Befehl, um zu erklären den Befehl für die Nutzung durch bestimmte Arten.Befehl Hinzufügen Implementierungen
Die Schnittstelle implementieren, ich pass auf die spezifischen Abhängigkeiten, die ich ausführen müssen mein Kommando (gelöst zu werden, durch meine container). Ich hinzufügen, ein Prädikat zu, meine Methode Entspricht, und definieren Sie mein Verhalten Führen.
Register Befehle mit Container
In StructureMap (verwenden Sie Ihre Lieblings-container), ich würde registrieren Sie das array wie folgt:
Auswählen und Ausführen von Befehlen, die auf der Basis des Typs
Schließlich, auf der Basis Klasse, ich definiere eine
IAttributeCommand
array in meine Argumente im Konstruktor injiziert werden, die von der IOC-container. Wenn der abgeleitete Typ geht in dietypes
array, werde ich ausführen, den entsprechenden Befehl basierend auf dem Prädikat.Wenn Sie mehrere Befehle verarbeiten kann, ein Typ, Sie können ändern Sie die Implementierung:
commands.Where(x=>x.Matches(type)).ToList().ForEach(Execute);
Der Effekt ist der gleiche, aber es ist ein subtiler Unterschied, wie die Klasse aufgebaut ist. Die Klasse hat keine Kopplung an einen IOC-container, und es gibt keine service-Standort. Die Umsetzung wird mehr getestet als der Klasse konstruiert werden können, die mit seiner realen Abhängigkeiten, keine Notwendigkeit, Draht, eine container - /resolver.
DoActions(Type[] types)
es ist per definition service-Standort. Wer wrangles die bits ist eine eher unbedeutende Punkt. Service-Standort angewendet sowie Ergebnisse in unglaublich dynamische und erweiterbare Systeme. Service Position schlecht Ergebnisse in unglaublich zerbrechlich und inextensible-Systeme. Dies ist die Grundlage der software-Architektur. Die Lösung der richtigen Probleme, die Erfolge, die die Lösung der falschen Probleme, die Ergebnisse in einen dampfenden Haufen...