.
Anmeldung | Registrieren | Hilfe
in Suchen

Eine Sinnfrage zu DI

Letzter Beitrag 08. Jul 2010 20:27 von Rainer Hilmer. 10 Antworten.
Seite 1 von 1 (11 Treffer)
Beiträge sortieren: Zurück Weiter
  • 02. Mrz 2010 15:53

    • Rainer Hilmer
    • Top 10 Mitwirkender
      Männlich
    • Registriert am 22. Jan 2008
    • Braunschweig
    • Beiträge 927
    • Punkte 14.105

    Eine Sinnfrage zu DI

    Hallo,
    als Vorwort möchte ich einen Wikipedia-Artikel ausschnittsweise zitieren:
    "Dependency Injection überträgt die Verantwortung für das Erzeugen und die Verknüpfung von Objekten an ein extern konfigurierbares Framework, entsprechend einem Komponentenmodell. Dadurch wird der Code des Objektes unabhängig von seiner Umgebung und von der konkreten Umsetzung der Klassen, die es benötigt."

    Nachzulesen hier.

    Wenn wir von Dependeny Injection sprechen, meinen wir doch lose Kopplung. ist das richtig? Um sicher zu gehen daß wir die gleiche Sprache sprechen, möchte ich kurz harte und lose Kopplung mit meinen Worten definieren.

    Definitionen

    Harte Kopplung bedeutet, daß eine Klasse B direkt in einer Klasse A referenziert wird.

    IMyInterface instance = new MyClass();

    Das Interface ändert auch nichts daran. Was davon ist jetzt die Referenzierung, die Zuweisung durch das Gleichheits-Zeichen? Nein, alleine schon die bloße Existenz des Klassennamens ist es! Wer mir da nicht zustimmt, möge bitte erst einmal weiterlesen.

    Eine lose Kopplung liegt dann vor, wenn eine Klasse nichts von der anderen weiß, und trotzdem arbeiten beide zusammen. Der Unity Application Block soll angeblich lose Kopplung bieten:

    IUnityContainer unityContainer = new UnityContainer();
    unityContainer.RegisterType<IMyInterface, MyClass>();
    IMyInterface instance = unityContainer.Resolve<IMyInterface>();
    instance.DoSomething();

    Ist das wirklich lose Kopplung? Dann kommentiere mal MyClass aus! Man muß gar nicht erst einen Kompilierversuch starten um zu wissen was passiert. Daher meine Definition der Referenz (s.o.).
    Wenn man sich den Code einmal genau anschaut, stellt man fest daß es sich hierbei tatsächlich um eine harte Kopplung handelt. Sie sieht nur anders aus als gewohnt.

    Bei einer wirklich losen Kopplung könnte ich MyClass auskommentieren und der Fehler würde sich erst zur Laufzeit offenbaren!

    Was für einen Sinn hat also DI (nicht IOC!)? Ich kann keinen erkennen, solange die Konstruktion aussieht wie in obigem Code. Bestimmt kann mir jemand die Augen öffnen. Smile

    P.S.: Ich weiß daß Unity auch mit Configdateien arbeiten kann. Dann wird DI zu IOC und das ergibt dann auch Sinn.

    Gruß,
    Rainer
    • IP-Adresse ist Registriert
  • 02. Mrz 2010 19:02 Antwort zu

    Eine Sinnfrage zu DI

    Antworten

    Eine kleinen Sinn mag das ganze evlt. ergeben. Wenn du es aus Sicht der Komponentenverwaltung betrachtest. Denn du verlagerst das ganze an einen Zentralen Punkt in deiner Applikation. Aber über DI wird in letzter Zeit sowieso viel gestritten. Denn oft wird DI als Factory für Objekte mit Singletoncharakter verwendet. Singleton sind nichts anders als globale variablen. Also ist das auch schon wieder mit Vorsicht zu genießen.


    Du kannst aber meiner Meinung nach im ersten Schritt bedenkenlos das gane in deiner App-Main konfigurieren und dann zu gegebenem Zeitpunkt X wo anders hin verlagern. Du könntest z.B. eine weitere Assembly bauen, die deine Konfiguration vornimmt, gegen die du referenzierst. Also ein Interface zum Mappen. 


    Irgendwo musst du ja dein Mapping vornehmen. Ob das app.config, irgendeine neuemodisch Skriptsprache auf Basis der DLR oder eine weitere C# Assembly ist, macht hier nichts aus. 


    Ob du nun DI per Hand machst oder per IoC-Container will ich mal aussen vor lassen. DI ansicht ist auch nur eine Symptomkur. Das eigentliche Problem ist die Lose Kopplung und Kommunikation von Komponenten. Martin Fowler beschreibt es in seinem Fazit über IoC und DI eigentlich auch genauso. Das Problem ist die Konfiguration deiner Services. Ralf Westphal hat ja da seine bevorzugte Architektur mit dem XcoAppSpace bzw. ganz neu den Event-Based Komponents, das im Prinzip, so wie ich das verstehe auch einem Nachrichtenbus entspricht, nur eben "sexy". 

    Das Problem ist im Grunde für mich immer das gleiche. Ich habe Komponenten, die Schnittstellen haben, also wiederverwendet werden können. Wie schaffe ich nun die Konfiguration dieser Schnittstellen durchzuführen?

    1. Dynamischer Code: Nachteil: keine Typsicherheit. Vorteil: Komponenten können zur Laufzeit getauscht werden)

    2. Statischer Code: Nachteil: Fest verdrahtet, Kompontenten müssen zur Compilezeit bekannt sein. Vorteil: Typsicherheit. Contract kann zur Compilezeit garantiert werden.

    AOP mit dem Aspektweaving gibts ja in seinen beiden Formen. Service Konfiguration ist ein anderer Ansatz. Aber zum Schluss bleibt es immer noch offen. Wo findet diese Konfiguration statt.


    Ein weiterer Ansatz ist ein dynamischer mit Typsicherheit. In Boo oder F# (oder ab .NET 4.0) gibt es das Interface IStructuralEquatable das auf die gleichheit der Werte innerhalb einer Klasse/Struktur schaut. (Wird für das Ducktyping verwendet). Damit bindest du ein Protokoll ein. Du schaust nach, ob deine Klasse Methode A, B und C einbindet und nimmst dann einfach diese um es auszuführen. Das ist aber auch nicht ganz so typsicher. Entspricht dann einem Patternmatching auf Objektebene. (Hoffe das verwirrt jetzt nicht zu sehr).

    Mache Entwickler sind früher dazu übergegangen Konventionen auf zu stellen. Aber wir wissen ja "configuration over convention". Also überleg dir weiter, was dir lieber ist. Du solltest aber sowieso keine App bauen, die alle Abhängigkeiten zentral in einem Container verwaltet. Nimm dir z.B. MEF und verwalte damit einzelne Komponenten, oder einen Container mit Childcontainer. StructureMap kann das laut Björn Rochel ganz gut. Siehe dieser Beitrag. Da gehts nach dem Beitrag in der Diskussion darum, ob die Funktionalität die StructureMap bietet nicht auch ausreicht. In folgendem Post beschreibt er eine Funktion von StructureMap das dem Reflection nahe kommt. Es wird als eine Convention zum Konfigurieren erstellt. Komische Bezeichnung.

    Warum machen wir das alles? Um nicht in einem ContainerSpaghetti-Code zu landen. Denn als Hinweis: IOC-Container sind erfunden worden um den SpaghettiCode zu beseitigen und nicht einen neuen zu erzeugen.


    Probier mal eine App mit 50 Komponenten aus. Kannst du mir versprechen, dass da jede Komponente überall gebraucht wird? Ich werde demnächst auch mal anfangen eine kleine App mit MEF und IOC zu mische. So enstehen klenie funktionale Parts die ihrerseits mit IOC konfiguriert werden. Diese Parts können dann in anderen Applikationen erneute eingestetzt werden.


    Die Strukturierung von DI sollte also so aussehen: Jeweils kleine Container für die Micro-Configuration und einen Container für die Architekturelle Configuration, also dem zusammenschalten der "Micro-Controller" (schönes Wortspie lBig Smile). Das ist mein jetziger Stand der Dinge.

    • IP-Adresse ist Registriert
  • 03. Mrz 2010 9:29 Antwort zu

    • helium
    • Top 500 Mitwirkender
    • Registriert am 03. Mrz 2010
    • Beiträge 2
    • Punkte 40

    Eine Sinnfrage zu DI


    Moment. Der Code, den du bei "Lose Kopplung" geschrieben hast, ist nicht lose gekoppelt. DI bedeutet dass du deine Abhängigkeiten nicht selbst erzeugst, sondern sie dir "injezieren" lässt.

    class OhneInjektion {
       IMyInterface instance = new MyClass();  // selbst erstellt
    }
    
    
    
    
    class MitInjektion {
        IMyInterface instance;  // wird über den Konstruktor injeziert
       
        public MitInjektion(IMyInterface instance)
        {
              this.instance = instance;
        }
    }

    Jetzt müsste man um ein MitInjektion zu erzeugen erstmal etwas erzeugen, das IMyInterface implementiert (beispielsweise MyClass). Bei mehreren Abhängigkeiten, die ihrerseits ja auch wieder Abhängigkeiten haben können, die injeziert werden müssen kann das anstrengend werden. DI-Frameworks nehmen einem diese Arbeit ab.

    Ich habe nie mit Unity gearbeitet, deswegen verzei mir mögliche Fehler, aber ich nehme an es sähe so aus:

       IUnityContainer unityContainer = new UnityContainer();
       unityContainer.RegisterType<IMyInterface, MyClass>();
       
        MitInjektion foo = unityContainer.Resolve<MitInjektion>();

    Ich sage dem Container, dass IMyInterface durch MyClass aufgelöst werden soll. Jeder, der ein IMyInterface injeziert haben möchte bekommt jetzt MyClass. Wie du siehst wird MyClass aber un MitInjektion niemals erwähnt. Ich kann also beispeilsweise im Produktionscode einen Container erstellen, der alle IMyInterfaces zu einem echten MyClass auflöst und in meinen Unittests einen, der einen Dummy injeziert, oder aber ich erzeuge per Hand.

    Hoffe das hilft weiter.

    Abgelegt unter:
    • IP-Adresse ist Registriert
  • 03. Mrz 2010 11:35 Antwort zu

    • Rainer Hilmer
    • Top 10 Mitwirkender
      Männlich
    • Registriert am 22. Jan 2008
    • Braunschweig
    • Beiträge 927
    • Punkte 14.105

    Eine Sinnfrage zu DI

    Hallo helium,
    so wie in deinem Unity-Codebeispiel ist es eben nicht, sondern so wie in meinem Beispiel. Ich könnte allerdings die Unity-Instanz für andere Komponenten zur Verfügung stellen. Dort würde der Klassenname dann tatsächlich nicht auftauchen. Es fragt sich nur wieviel Sinn das macht. Das ist der Punkt den Rainer Schuster erläutert hat.

    @Rainer Schuster,
    Da kann man mal sehen wie die Meinungen doch auseinandergehen. Ralf Westphal bemerkt in seinem Blogartikel
    http://ralfw.blogspot.com/2010/02/bindungsenergie-gedanken-uber-ein.html
    "Konsequente Namensgebung (convention over configuration) hat schon andere Plattformen einen Siegeszug antreten lassen."

    Deine Ausführungen sind, wie immer, sehr gut. Über große Applikationen mit 50 und mehr Komponenten, und deren Verbindungen, habe ich mir bislang tatsächlich noch keine Gedanken gemacht. Es ist gut das mal zu lesen und gleich auch einen Architekturtip zu bekommen.

    Mit EBC beschäftige ich mich auch schon seit Ralfs erstem Blogartikel zu dem Thema - ist echt ne tolle Sache, aber der ComponentBinder muß noch verbessert werden (siehe Diskussion).
    Ich weiß nicht ob du dich noch erinnern kannst, es ist schon eine ganze Weile her, da gab es mal eine Diskussion über meine Idee der Picobots. Das ist im Prinzip das Gleiche wie EBC, nur daß die Elemente asynchron laufen. EBC ist schon verdammt nahe an meiner Vision. Smile

    P.S.: Ein weiterer guter Ansatz zu event based programming ist das Reactive Framework.

    Gruß,
    Rainer
    • IP-Adresse ist Registriert
  • 03. Mrz 2010 16:56 Antwort zu

    • helium
    • Top 500 Mitwirkender
    • Registriert am 03. Mrz 2010
    • Beiträge 2
    • Punkte 40

    Eine Sinnfrage zu DI

    > so wie in deinem Unity-Codebeispiel ist es eben nicht

    Ich habe mir gerade mal Unity runtergeladen installiert, meinen Code getestet und er läuft wie erwartet. Es wird ein MitInjektion erzeugt und für IMyInterface wird eine MyClass instanz injeziert.

    Was ist nicht so wie ich es behaupte?


    Wenn du DI generell nicht verstehen solltest, dann guck dir vielleicht einfach mal das hier an. Es geht zwar um Google Guice, ein Java-DI-Framework, aber ich denke es erklärt DI generell ganz gut.

    • IP-Adresse ist Registriert
  • 03. Mrz 2010 18:37 Antwort zu

    • Rainer Hilmer
    • Top 10 Mitwirkender
      Männlich
    • Registriert am 22. Jan 2008
    • Braunschweig
    • Beiträge 927
    • Punkte 14.105

    Eine Sinnfrage zu DI

    Entschuldigung, ich habe deinen Beitrag wohl nicht richtig gelesen.
    Gruß,
    Rainer
    • IP-Adresse ist Registriert
  • 02. Jun 2010 9:32 Antwort zu

    • karle
    • Top 25 Mitwirkender
    • Registriert am 20. Okt 2008
    • Beiträge 172
    • Punkte 2.030

    Eine Sinnfrage zu DI

    Hm, an dem thema hängste glaub a bissl gell?

    für mich hört sich das alles etwas verkompliziert an. Ich seh halt die vorteile und da seh ich 2:

    1.  an einen interface-zeiger heranzuklommen, ohne dass ich ein objekt instantieren muss. (der instantierte typ kann z.b. dynamisch nachgeladen werden => plugins)

    2. an einen interface-zeiger heranzuklommen ohne dass ich diesen durch diverse konstruktoren reichen muss, bis er da ankommt, wo er benötigt wird. stattdessen reiche ich nur den di-container durch und habe eine erweiterbare infrastruktur, um eine unmenge von objekten kontextbezogen verfügbar zu machen, ohne viel code schreiben zu müssen.

    vielleicht konnte ich dir ja weiterhelfen. vielleicht kannst du mir mal erklären, was der unterschied zwischen einer brücke und einem adapter ist... hehe, das ewige mysterium ;-)

    karle

    In meinem Keller ist der Eingang zur Hölle - aber es stehen Kartons davor.
    • IP-Adresse ist Registriert
  • 02. Jun 2010 10:19 Antwort zu

    • Rainer Hilmer
    • Top 10 Mitwirkender
      Männlich
    • Registriert am 22. Jan 2008
    • Braunschweig
    • Beiträge 927
    • Punkte 14.105

    Eine Sinnfrage zu DI

    karle:
    Hm, an dem thema hängste glaub a bissl gell?

    Nöö, das Thema ist für mich längst abgehakt.

    Gruß,
    Rainer
    • IP-Adresse ist Registriert
  • 02. Jun 2010 15:37 Antwort zu

    • karle
    • Top 25 Mitwirkender
    • Registriert am 20. Okt 2008
    • Beiträge 172
    • Punkte 2.030

    Eine Sinnfrage zu DI

    ok, sorry. hatte nur irgendwie in erinnerung, dass das schonmal da war.
    karle

    In meinem Keller ist der Eingang zur Hölle - aber es stehen Kartons davor.
    • IP-Adresse ist Registriert
  • 08. Jul 2010 0:49 Antwort zu

    • Peter Bucher
    • Top 50 Mitwirkender
      Männlich
    • Registriert am 29. Jan 2008
    • Schweiz, Luzern
    • Beiträge 38
    • Punkte 560

    Eine Sinnfrage zu DI

    Salute zusammen

    Ich kann das so nicht stehen lassen.

    @Rainer
    Verwechsle nicht lockere Kopplung und späte Bindung.

    Lose oder lockere Kopplung erreichst du mit der Abstraktion, dabei ist es erst einmal egal ob du eine nicht- oder abstrakte Klasse, oder ein Interface nimmst.
    Sobald du eine Generalisierung / Abstraktion hast und diese auch anstelle deiner konkreten Implementation nutzt, hast du eine lockere Kopplung erreicht.

    Jedoch hast du den konkreten Typ bei jeder Erzeugung dabei. Beseitigen kannst du das Problem mit dem Factory-Pattern, indem du die Erzeugung auslagerst.

    Im einfachsten Fall des Factory-Patterns hast du aber immer noch keine späte Bindung, sondern nutzt den konkreten Typ einfach in der Factory-Klasse / Methode.

    Lockere Kopplung: Lösung der Abhängigkeiten, angefangen bei der Benutzung in anderen Komponenten / Klassen.

    Späte Bindung: Laden des Typs zur Laufzeit und damit arbeiten, bspw. Methoden dynamisch aufrufen. Dabei kann er auch zur Laufzeit aus einer zusätzlichen Assembly ausgelesen werden. Die Geschichte läuft nur per Reflection, also auf Meta-Ebene.

    Bei einer späten Bindung brauchst du auch keine "harten" Referenzen zu den Implementationen, sondern nur zu der Abstraktion. Harte Referenzen bspw. die Referenzen in Visual Studio, zur Kompilezeit bekannt.

    Deine Definition / Vorstellung von IoC und Dependency Injection gefällt mir nicht.
    Die einfache Variante:

    IoC:
    Das Prinzip von der Umkehrung der Kontrolle, also wird die Verantwortlichkeit der Erzeugung von Abhängigkeiten an externe Klassen gegeben. Die Anwendung kann in verschiedenen Arten geschehen, beschreibt aber keine konkrete Anwendung.

    Dependency Injection:
    Abhängigkeiten die eine Klasse besitzt werden (per Hand oder automatisiert von einem Framework) von aussen am Objekt weitergegeben, überlicherweise über Konstruktorargumente oder Eigenschaften.
    Das ist eine (möglichst) implizite Anwendung von IoC.

    Service Locator:
    Das ist eine explizitere Anwendung von IoC, da Klasse seine Abhängigkeiten explizit anfordert. Die Abhängigkeit auf ein etwaiges Framework / API ist dadurch natürlich stärker.

    Mehr zu DI vs. SL hier:

    Die Gründe Anwendungen von IoC zu benutzen gibt es einige:

    • Zentralisierung der Konfiguration
    • Möglichkeit eine Anwendung ohne Neukompilierung umzukonfigurieren (Xml Konfiguration + (DI)-Komponente)
    • Bessere Testbarkeit einer Anwendung
    • Automatisch eine schönere Architektur, bessere Aufteilung der Verantwortlichkeiten durch bewusst sein dessen
    • Weniger "Noise" im Code
    • Bessere Erweiterbarkeit des Codes. Wenn bewusst genutzt, dann umso mehr

    Diese Liste ist sicher nicht vollständig.

    HTH

    • IP-Adresse ist Registriert
  • 08. Jul 2010 20:27 Antwort zu

    • Rainer Hilmer
    • Top 10 Mitwirkender
      Männlich
    • Registriert am 22. Jan 2008
    • Braunschweig
    • Beiträge 927
    • Punkte 14.105

    Eine Sinnfrage zu DI

    Danke Peter. Es ist schön daß mal so aufgebröselt zu sehen. Das macht das Thema wirklich transparenter. Yes
    Gruß,
    Rainer
    • IP-Adresse ist Registriert
Seite 1 von 1 (11 Treffer)

WPF Forum | ASP.NET Forum | ASP.NET MVC Forum | Silverlight Forum | Windows Phone 7 Forum | SharePoint Forum | Dotnet Jobs | Dotnet Termine | Developer Blogs | Dotnet News

Das Team | Regeln | Impressum