24

Ich benutze Castle Windsor für ein Tierprojekt, an dem ich gerade arbeite. Ich fange an zu bemerken, dass ich den IoC-Container an verschiedenen Stellen in meinem Code aufrufen muss, um neue Objekte zu erstellen. Diese Abhängigkeit vom Container macht die Pflege meines Codes schwieriger.IoC, Wo setzt du den Container?

Es gibt zwei Lösungen, die ich verwendet habe, um dieses Problem

ich um die Behälter abstrakte Fabriken als Wrapper zu erstellen versucht zu lösen, die ich in Teile meiner Anwendung injizieren könnte, die Objekte erstellen müssen. Das funktioniert, hat aber einige Nachteile, da Castle es schwer hat, seinen eigenen Container als Abhängigkeit zu injizieren. Also muss ich das per Hand machen, diese Art besiegt den gesamten Zweck des IoC-Containers.

Ich habe die Hauptanwendung Controller-Klasse verwendet, um den IoC-Container zu wickeln und als zentrale Factory/Repository zu arbeiten. Das war sehr erfolgreich, aber diese Klasse wird zu groß und wirkt wie ein zentrales Gottobjekt, fast jedes andere Objekt hat einen Bezug darauf.

Beide Lösungen funktionieren, aber beide haben ihre Nachteile. Ich bin neugierig, ob andere Leute das gleiche Problem haben und bessere Lösungen gefunden haben.


bearbeiten Das Problem ist nicht für Objekt A, das hier Objekt B. hängt ich in der Regel nur Konstruktor Injektion verwenden und alles funktioniert. Manchmal habe ich Objekte vom Typ A, die während ihrer Lebensdauer eine variable Anzahl anderer Objekte vom Typ B erzeugen müssen. Ich bin mir nicht sicher, wie ich das machen soll.

@Blair Conrad: Die Wartungsprobleme sind bis jetzt nicht streng. Ich hatte einige Klassen abhängig von der Container-Objekt aufrufen container.Resolve <>. Und ich möchte meinen Code nicht von meiner Infrastruktur abhängig machen. Ich probiere immer noch Dinge aus, also habe ich festgestellt, dass ich viel Code ändern musste, als ich für dieses Projekt von Ninject nach Castle wechselte.

@flowers: Hmm. Ich mag deine Fäuste-Lösung. Es kombiniert die Dinge, die von beiden Lösungen funktionieren, die ich ausprobiert habe. Ich denke, ich dachte immer noch zu viel an Objekte und nicht genug an Schnittstellen/Verantwortlichkeiten. Ich habe eigens gebaute Fabriken ausprobiert, aber ich möchte, dass sie den Container hinter den Kulissen benutzen, um die Objekte zu erstellen, und ich habe noch nicht herausgefunden, wie ich den Container sauber in Objekte zerlegen kann.

+0

Ich bin neugierig, und die Antworten können uns helfen zu beantworten. Welche Wartungsprobleme hatten Sie? –

Antwort

3

Der Hauptvorteil von Dependency Injection, zumindest in meinen Anwendungen, ist die Fähigkeit, Code zu schreiben, der kontextunabhängig ist. Aus dieser Perspektive scheint Ihre zweite Lösung den Nutzen, den DI Ihnen geben könnte, zu untergraben. Wenn das 'god object' jeder Klasse verschiedene Interfaces zur Verfügung stellt, die darauf verweisen, ist es möglicherweise nicht zu böse. Aber wenn du so weit gegangen bist, sehe ich nicht, warum du es nicht bis zum Reifen gehst.

Beispiel: Ihr God Objekt hat eine getFoo() Methode und eine getBar() Methode. Objekt A benötigt ein Foo, Objekt B benötigt ein Bar. Wenn A nur ein Foo braucht, sollte Foo direkt in A injiziert werden und A sollte sich gar nicht bewusst sein, dass Gott es ist. Aber wenn A weiterhin Foos erschaffen muss, ist es ziemlich unvermeidlich, A einen Bezug zu Gott zu geben. Aber du kannst dich selbst vor dem Schaden schützen, der dadurch verursacht wird, dass du Gott herumgereicht hast, indem du die Art der Bezugnahme auf Gott verengt hast. Wenn du Gott dazu bringst, FooFactory zu implementieren und A einen Hinweis auf die von Gott implementierte FooFactory zu geben, kannst du den Code in A kontextneutral schreiben. Das verbessert die Möglichkeiten für die Wiederverwendung von Code und erhöht das Vertrauen, dass eine Änderung an Gott keine unerwarteten Nebenwirkungen verursacht. Zum Beispiel können Sie sicher sein, wenn Sie getBar() von Gott entfernen, dass Klasse A nicht bricht.

ABER ...Wenn Sie sowieso alle diese Schnittstellen haben, sind Sie wahrscheinlich besser dran, speziell angefertigte Fabrikklassen zu schreiben und all Ihre Objekte, einschließlich der Fabriken, innerhalb des Containers zu verkabeln, anstatt den Container überhaupt einzuwickeln. Der Container kann weiterhin die Fabriken konfigurieren.

2

Während ich die Explizitheit von "zweckgebauten Fabriken" schätze und sie sogar selbst benutze, fühlt sich das wie ein Code-Geruch in meinen eigenen Entwürfen an, weil sich die öffentliche Schnittstelle (kleines "i") ständig mit einer neuen Fabrik ändert eine neue GetX-Methode für jede Implementierung. Nachdem ich Jeremy Millers It's time for IoC Container Detente gelesen habe, vermute ich Generika und das Einspritzen des Behälters selbst ist der Weg zu gehen.

Ich würde Ninject, StructureMap oder Windsor in irgendeine Art von IServiceLocator-Schnittstelle wie die in Jeremys Artikel vorgeschlagene einfügen. Dann haben Sie eine Container-Fabrik, die einfach einen IServiceLocator überall in Ihrem Code zurückgibt, sogar in Schleifen, wie Sie ursprünglich vorgeschlagen haben.

IServiceLocator container = ContainerFactory.GetContainer(); 
while(keepLooping) 
{ 
    IExample example = container.GetInstance<IExample>(); 
    keepLooping = example.DoWork(); 
} 

Ihre Containerfabrik kann immer die gleiche Zahl zurückgeben, Sie können IoC-Frameworks austauschen, was auch immer.

11

Bitte verwenden Sie niemals statische Klassen wie IoC.Container.Resolve oder ContainerFactory.GetContainer!

Dies macht den Code komplizierter, schwieriger zu testen, zu warten, wiederzuverwenden und zu lesen.

Normalerweise hat jede einzelne Komponente oder Dienstleistung nur einen einzigen Injektionspunkt - das ist der Konstruktor (mit optionalen Eigenschaften). Und im Allgemeinen sollten Ihre Komponenten oder Serviceklassen niemals etwas über Container wissen.

Wenn Ihre Komponenten wirklich dynamische Auflösung innen haben müssen (dh der Ausnahmebehandlung Politik oder Workflow-Lösung, basierend auf dem Namen), dann empfehle ich lending IoC powers via the highly-specific providers

+0

Danke für den Link. Ich muss es besser lesen, wenn ich Zeit habe, aber ich denke, dass ich am Ende so etwas ausprobiert habe, entschied mich dann dagegen und benutzte eine abstrakte Fabrik, um die dynamischen Objekte zu erstellen, anstatt den IoC-Container zu verwenden. Ich habe nur den Container benutzt, um die Fabrik selbst zu erstellen. – Mendelt

+2

@Rinat, mit dem ursprünglichen Szenario des OP: Sie sind in A, und Sie brauchen n Anzahl von B-Instanzen. Ohne Bezug auf den Container, wie bekommen Sie Ihre Bs? – flipdoubt

+0

Ihr Link funktioniert jetzt nicht ... könnten Sie ihn bitte aktualisieren? –

1

zu betrachten Als Follow-up zu @flipdoubt

Wenn Sie verwenden am Ende ein Service-Locator-Muster, das Sie vielleicht auschecken möchten http://www.codeplex.com/CommonServiceLocator. Einige gängige IoC-Frameworks (windsor, structuremap) sind verfügbar, die hilfreich sein könnten.

Viel Glück.

+0

Vielen Dank! Ich werde das überprüfen. Ich fand heraus, dass es sehr einfach ist, einen eigenen Service Locator zu erstellen, aber ich könnte Ideen bekommen, wie sie Dinge gemacht haben! – Mendelt

+0

Keine Sorge, viel Glück. – smaclell

1

Ich würde in diesem Fall empfehlen stark typisierte Fabriken wie Sie erwähnt, die injiziert werden. Diese Fabriken können den Container umhüllen, können aber zusätzlichen Kontext zulassen und zusätzliche Bearbeitung vornehmen. Zum Beispiel könnte das Create für die OrderFactory kontextabhängige Parameter akzeptieren.

Statische Abhängigkeiten von einem generischen Service Locator zu haben, ist eine schlechte Idee, wenn Sie die Absicht und den Kontext verlieren. Wenn ein IoC eine Instanz aufbaut, kann er die richtigen Abhängigkeiten bereitstellen, die auf einer Vielzahl von Faktoren basieren, wie zum Beispiel Pro fi le, Kontext usw., da er das große Bild hat.

CommonServiceLocator ist nicht für diesen Zweck, obwohl man versucht sein könnte, es zu verwenden. Der Hauptzweck von CommonServiceLocator ist für Apps/Frameworks, die Cross-IoC-Container-kompatibel sein wollen. Apps, die verwenden, sollten den Locator jedoch nur einmal optimal aufrufen, um eine Hierarchie von Komponenten und deren Abhängigkeiten aufzubauen. Es sollte nie direkt wieder aufgerufen werden. Wenn wir etwas durchsetzen könnten, hätten wir es getan. In Prism (http://www.microsoft.com/compositewpf) haben wir eine IContainerFacade zum Aufbau von Modulen vorgestellt. Das ist ein Service-Locator, obwohl ein Low-Level-Service-Locator. Im Rückblick hätten wir wahrscheinlich eine ModuleFactory oder etwas erstellen sollen und IContianerFacade verwendet, um sie zu erfassen, und dann diese Module verwendet, um direkt zur Facade zu gelangen. Rückblick ist 20/20.Es ist niedrig genug, obwohl es die Dinge nicht wirklich beeinflusst.

Auf CSL rangen wir mit der Namensgebung, weil es zu Verwirrung führen könnte. Am Ende haben wir uns für CSL entschieden, weil technisch gesehen die Schnittstelle nicht für Sie geeignet war.

10
+0

Danke! Hinzugefügt zu meiner Leseliste :-) – Mendelt

+0

Dies war ein ausgezeichneter Artikel und half mir, dieses Problem zu klären! –

+0

Ich mochte den Link. Aber ich würde auch lieber eine hochrangige Zusammenfassung als nur eine Verbindung bevorzugen. – KFL

0

Das ist ein wirklich comon Problem. Windsor's eingebautes Typed Factory Facility wird Ihnen die Vorteile einer Fabrik ohne die erwähnten Nachteile geben.

Verwandte Themen