Laden...

LightCore: Warum kein TryResolve

Erstellt von Uwe81 vor 13 Jahren Letzter Beitrag vor 13 Jahren 2.289 Views
U
Uwe81 Themenstarter:in
282 Beiträge seit 2008
vor 13 Jahren
LightCore: Warum kein TryResolve

Hallo!

Ich verwende in meinem Projekt Lightcore und bin sehr angetan. Ist das das erste mal, dass ich mit DI-Frameworks arbeite.

Es gibt bei mir ein paar Komponenten, die sehr viele Abhängigkeiten haben. Anstelle dass ich nun alle im Konstruktor übergebe, übergebe ich einen Lightcore.IContainer und rufe im Konstruktor dann jeweils Resolve auf.

Nun sind einige Abhängigkeiten optional (die Komponente kann auch ohne diese Abhängigkeit sinnvoll funktionieren). Die optionalen Abhängigkeiten per Property Injection zu injizieren wäre das sauberste, finde ich aber in diesem Fall unschön. Dann müssen im Setter viele Events registriert bzw. bei erneutem Setzen wieder deregistriert werden müssen etc. Geht alles, ist aber viel Aufwand dafür, dass ich es eigentlich nicht brauche.

Was ich gerne hätte, wäre eine Methode wie


bool TryResolve<TContract>(out TContract)

Jetzt kann ich mir die Schnell per Extension Method schreiben, indem ich ich ein Resolve ausführe und Exceptions fange. Aber hat es einen Grund, warum das noch nicht existiert? Ist das schlechter Stil? Kann man sowas besser lösen (außer Property Injection)?

Vielen Dank,
Uwe

6.911 Beiträge seit 2009
vor 13 Jahren

Hallo,

Es gibt bei mir ein paar Komponenten, die sehr viele Abhängigkeiten haben. Anstelle dass ich nun alle im Konstruktor übergebe, übergebe ich einen Lightcore.IContainer und rufe im Konstruktor dann jeweils Resolve auf.

Ich würde im Ctor nicht den Container übergeben, sondern die Komponenten explizit angeben da (auch wenn nicht alles bei Übergabe des Containers zutrifft)*die Abhändigkeiten sofort sichtbar sind *der Container auch diese Klasse auflösen kann *im Ctor die Argumente einfach geprüft werden können *durch den Ctor ein valides Objekt erstellt werden sollte *es einfacher zu testen ist da die Mocks übergeben werden können ohne dass ein Container mti den Mocks erstellt werden muss

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

1.044 Beiträge seit 2008
vor 13 Jahren

Hallo Uwe81,

die Resolve-Methode sollte in aller Regel null zurückgeben oder eine Exception werfen. Das ist genau das gleiche, als wenn dir eine TryResolve-Methode zusteht. Wenn du eine solche Methode haben möchtest, schreib eine Extension Method und fertig.

Das hört sich für mich irgendwie nach IoC: Aufgelöste Objekte oder DI-Container übergeben?. gfoidl hat da schon ein paar Bemerkungen und Meinungen dazu abgegeben.

Abgesehen davon solltest du dir überlegen, ob du nicht einen ServiceLocator verwendest solltest. Das, was du vor hast, ist nicht ganz "DI-like".

Der Author - Peter Bucher - ist unter anderem ein Mitglied bei uns im Forum. Du kannst ihn auch hier erreichen und fragen. Peter Bucher.

zero_x

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo Uwe81,

ich habe nicht verstanden, wie dir ein TryResolve nützen würde. Was machst du, wenn TryResolve false liefert? Würdest du da dann nicht doch den Code schreiben müssen, der die Initialisierung per Property Injection durchführt? Und wenn du den Code so oder so schreiben musst, was gewinnst du dann durch TryResolve?

herbivore

U
Uwe81 Themenstarter:in
282 Beiträge seit 2008
vor 13 Jahren

Hallo!

Ich würde im Ctor nicht den Container übergeben, sondern die Komponenten explizit angeben

Hatte ich zuerst. Aus den genannten Gründen (insbesondere Testbarkeit). Ich fand es aber dann auf dauer nervig, nicht alle Konstruktorparameter im Container sind. Ich musste also bei Resolve einige Parameter übergeben, der Rest sollte aufgelöst werden. Das führte aber immer wieder zu Resolve Exceptions (weil ich z.B. ein Foo anstelle eines IFoo übergeben hatte etc.) So übergebe ich den Container, verschleier damit Abhängigkeiten, aber habe zumindest nur einfache, parameterlose Resolve-Aufrufe.

ich habe nicht verstanden, wie dir ein TryResolve nützen würde. Was machst du, wenn TryResolve false liefert? Würdest du da dann nicht doch den Code schreiben müssen, der die Initialisierung per Property Injection durchführt? Und wenn du den Code so oder so schreiben musst, was gewinnst du dann durch TryResolve?

Die Initialisierung muss ich so oder so schreiben. Ich muss bei der Komponente ggf. mehrere Events registrieren und die Logik hinzufügen, diese bei Dispose wieder Deregistrieren zu können.
Wenn TryResolve false liefert, würde ich einfach mit einem NullObject arbeiten.

Beispiel: Ich habe ein WinForms-Control (Grid), welches Punkte in einem Koordinatensystem anzeigt. Meine Komponente verbindet nun dieses Grid, mit einem Model. Jetzt kann es in dem Model eine IColorDimension geben, welche Farbinformarmationen beinhaltet. Um nicht immer die Unterscheidung treffen zu müssen, ob es eine IColorDimension im Model gibt, initialisiere ich mir sonst ein NullObject, also eine IColorDimension, die immer schwarz ist.

Im folgende verwende ich aber beide Äquivalient. Ich muss mich bei der IColorDimension für Events registrieren (wenn z.B. Farben verändert werden). Aber es findet eben die ganze Initialisierung im Konstuktor statt, die IColorDimension kann zur Laufzeit nicht mehr geändert werden.

Warum nicht mit Properties: Es wird nicht vorkommen, dass sich die ColorDimension zur Laufzeit ändert. Entweder sie existiert, oder nicht. Bei Properties müsste ich aber mit der ganzen Logik des "neu setzens" klarkommen. Also bei der alten Deregistrieren, bei der neuen Registrieren. Wenn es auf null gesetzt wird, dann ein Null-Objekt erzeugen. Geht alles, aber eigentlich brauche ich es nicht. Und es verkompliziert den Code.

Abgesehen davon solltest du dir überlegen, ob du nicht einen ServiceLocator verwendest solltest. Das, was du vor hast, ist nicht ganz "DI-like".

Habe gerade erst nach ServiceLocator suchen müssen. Ja, eigentlich ist es genau das, was ich tue, stimmt. Werde mal schauen, ob ich nicht dann auch ein entsprechendes Framework dafür verwende.

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo Uwe81,

Um nicht immer die Unterscheidung treffen zu müssen, ob es eine IColorDimension im Model gibt, initialisiere ich mir sonst ein NullObject, also eine IColorDimension, die immer schwarz ist.

das klingt mir aber irgendwie danach, dass du an der Stelle selber DI-Framework spielst bzw. Aufgaben, die eigentlich das DI-Framework übernehmen sollte, selbst übernimmst. IColorDimension ist doch eine Abhängigkeit wie andere auch, wird aber von dir manuell behandelt.

herbivore

U
Uwe81 Themenstarter:in
282 Beiträge seit 2008
vor 13 Jahren

das klingt mir aber irgendwie danach, dass du an der Stelle selber DI-Framework spielst bzw. Aufgaben, die eigentlich das DI-Framework übernehmen sollte, selbst übernimmst. IColorDimension ist doch eine Abhängigkeit wie andere auch, wird aber von dir manuell behandelt.

Da ist was Wahres dran. Ich frage mich, wie man das besser machen könnte.

Es geht um ein GUI-Framework. Optional kann es viele Dinge geben (Anzeige von Farben, Anzeige von Symbolen, Vorschau von Farbskalen, geschickte Navigationsstrategien etc). Wir auch alles in dem ein oder anderen Projekt verwendet. Aber eben immer nur ein Teil.

Um die Handhabung einfach zu halten, wollte ich nun, dass der Benutzer im DI-Framework das registriert, was er Nicht-Standard haben will, der Rest als Standard angenommen wird (bzw. ein paar Sache müssen im DI-Framework existieren).

Bisher implementiere ich das in der Tat in jeder Komponente selbst, schöner wäre es, wenn ich immer davon ausgehen könnte, dass alles im DI-Container steckt. Damit ich eben nicht, wie du schreibst, DI-Aufgaben übernehme.

Gibt es in DI-Containern so sagen wie "Nimm StdFoo als Standard für IFoo, wenn einer was anderes registriert, vergiss es". Oder zumindest, dass ich alle Registrierten Typen bekomme?
Dann könnte ich an einer Zentralen Stelle den DI-Container quasi mit Default-Werten auffüllen.

1.378 Beiträge seit 2006
vor 13 Jahren

Meiner Meinung nach ist das keine Aufgabe eines DI-Containers sondern einer eigens dafür entwickelten Factory, welche dann im Container registriert wird. In deinen Objekten rufst du dann nur Factory oder Provider GetGuiElements oder was auch immer auf und du bekommst was du brauchst.

Lg XXX

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo Uwe81,

Um die Handhabung einfach zu halten, wollte ich nun, dass der Benutzer im DI-Framework das registriert, was er Nicht-Standard haben will, der Rest als Standard angenommen wird

Lightcore im Detail kenne ich nicht, aber was hindert dich daran, wenn in der Konfiguration der Abhängigkeiten die ganzen Standard-Klassen eingetragen sind und wenn der Benutzer was anderes will, dass registriert er eben (im Code) was anderes. Zumindest vom Grundprinzip her sollte sich das machen lassen.

herbivore

U
Uwe81 Themenstarter:in
282 Beiträge seit 2008
vor 13 Jahren

Meiner Meinung nach ist das keine Aufgabe eines DI-Containers sondern einer eigens dafür entwickelten Factory, welche dann im Container registriert wird.

Lightcore im Detail kenne ich nicht, aber was hindert dich daran, wenn in der Konfiguration der Abhängigkeiten die ganzen Standard-Klassen eingetragen sind und wenn der Benutzer was anderes will, dass registriert er eben (im Code) was anderes. Zumindest vom Grundprinzip her sollte sich das machen lassen.

Hallo!

Sorry für die Verzögerung bei der Antwort.
Ja, das habe ich im Wesentlichen jetzt gemacht. In Lightcore gibt es keine Möglichkeit, direkt an die registrierten Typen dran zu kommen, aber ich brauchte sowieso einen Wrapper um den ContainerBuilder (musste wollte biem Build-Event etwas ausführen, siehe DI-Container, LightCore: Objekt bei build() zuweisen). Dort merke ich mir nun die registrierten Typen und fülle den Rest mit default-Werten auf.

In der Tat ist es schöner, weil dadurch in dem einzelnen Komponenten die Erstellung von Default-Objekten entfällt.