Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Delphi Dependency Injection - Ein paar allgemeine Fragen (https://www.delphipraxis.net/180706-dependency-injection-ein-paar-allgemeine-fragen.html)

Dawn87 10. Jun 2014 20:38

Dependency Injection - Ein paar allgemeine Fragen
 
Hallo,

seit einigen Tagen lese ich mich etwas in das Thema "Dependency Injection" bzw. "Inversion of Control" ein.

Ich verwende für meine ersten Schritte Spring4d, so einiges wird mir aber erst nach und nach klar. Einige Fragen, die ich teilweise auch schon selbst beantworte, bringe ich heute Abend in dieses Forum mit. Es wäre schon, wenn ihr dazu ein paar Worte verlieren könntet und mich entweder bestätigt oder aber korrigiert.

Frage 1)

Sollte man konsequent die Verwendung von Field-Injection zugunsten von Constructor-Injection vermeiden?

Meine Antwort dazu:
Die Verwendung von Field bzw. Property-Injection erschwert es den Code zu testen, da man für den Test sonst auf die Hilfe des GlobalContainers angeweisen ist. Außerdem ist diese Art der Injection fehleranfälliger. Man denke nur daran, dass man eine Property per [Inject] markiert, aber es versäumt die entsprechende Unit einzubinden, in der dieses Attribut definiert ist.

Die Antwort ist also "Ja".

Frage 2)

Was ist die beste Vorgehensweise für DI in Verbindung mit Oberflächen? Sollte meine "TForm" ebenfalls ein bestimmtes Interface implementieren und im Container registriert werden? Eine andere Möglichkeit wäre eine Klasse zu bauen, die mein Formular aggregiert und per direktem Aufruf des Constructors implementiert. Ich habe dazu keine Beispiele oder Informationen gefunden.

Aus dem Bauch heraus würde ich wohl eine Factory dafür nehmen.

Frage 3)

Wie sieht bei einer Anwendung welche DI nutzt der Startup-Code aus (also von den ersten Zeilen in der DPR bis zur Anzeige des ersten Fensters)? Habt ihr eine bestimmte Klasse, welche sich um die Konfiguration des Containers kümmert und anschließend die MainForm instanziert? Idealerweise hat jene Klasse als einzige eine Zugriff auf den GlobalContainer und füttert alle anderen Klassen?

Als Resultat hätte man ggf. umfangreiche Konstruktoren, da man ja alles von der "obersten Ebene" durchreichen muss. Ein Verstoß gegen dieses Gebot und man ich ruck-zuck dabei eher einen ServiceLocator zu verwenden, statt richtige Dependency Injection durchzuführen.

Um hier nochmal das Szenario aus Frage 2 zurückzugreifen:
Meine Main-Form müsste also z.B. im Konstruktor auch die Instanzen aller Unter-Formulare mitgegeben bekommen bzw. eine Factory mit dessen diese zum geeigneten Zeitpunkt erzeugt werden können? Über den gleichen Mechanismus würde man der Main-Form auch z.B. bestimmte "Behaviours" mitgeben können, also z.B. ein Interface, dass das Verhalten beim Schließen steuert (Soll gefragt werden ob gespeichert werden soll oder nicht usw.) um das separat Testen zu können.

Frage 4)

Zur Erzeugung der Objekte die nur für die Datenhaltung vorgesehen sind, wird eine Factory im Container registriert. Dies wird anscheinend gemacht, da die Erzeugung dieser Objekte durch den Container eher wenig performant ist und eine Factory bereits die nötige Flexibilität mitbringt.

Frage 5)

Mal in die Zukunft geschaut: Könnte man theoretisch mit DI auch eine Art Plugin-System umsetzten? So dass also beispielsweise die Implementierungen auch aus einem Package oder sogar über COM kommen könnten und man so das Verhalten der Anwendung nachträglich ohne Neukompilierung beeinflussen kann? Grundsätzlich spricht da nichts gegen oder?

Frage 6)

Ist es korrekt so, dass der Delphi Debugger für Interfaces keine Properties anzeigen kann bzw. nur dann, wenn ich auch explizit den Namen der Property angebe? Nehmt ihr das so hin oder gibt es da einen Trick? Das sehe ich momentan noch als ziemlichen Nachteil an.

Ich hoffe ich konnte mich soweit zumindest für diejenigen, denen das Thema Dependency Injection nicht fremd ist, verständlich äußern und freue mich auch Eure Antworten.

Beste Grüße
Stefan

Stevie 10. Jun 2014 23:30

AW: Dependency Injection - Ein paar allgemeine Fragen
 
Zu 1) Generell gilt bei DI immer: man sollte den Code so schreiben, dass man auch manuell seine Sachen zusammen stecken kann und nicht auf RTTI "Magic" des DI Containers angewiesen ist (Mark Seemann nennt das immer "poor mans DI".
Weniger würde ich hier als Grund die Vermeidung von Attributen sehen - wenn die Unit dafür fehlt, wird immerhin eine Warning ausgegeben, die man auch leicht als Fehler markieren kann in den Einstellungen. Natürlich braucht man für ctor injection standardmäßig keine Attribute aber man kann sie trotzdem an verschiedenen Stellen einsetzen und spart sich u.U. eine Menge registrierungs Code (noch gibt es das nicht, aber in Zukunft plane ich eine automatische Registrierung aller Typen, die mit entsprechenden Attributen versehen sind)

Zu 2) Das ist ein komplexes Thema und kommt drauf an, welche Architektur du hier verwendest, MVP, MVC, MVVM, view first, model first, etc (einfach mal diverse Literatur zu lesen). Factories bieten sich aber auf jeden Fall für Forms an, welche nicht gleich zu Beginn da sind und evtl auch während einer Session niemals aufgerufen werden. Allerdings muss man bei Forms/Controls und dem DI Container natürlich auf das Memory Management achten. Wenn du ein Form als Interface im Container registrierst dann hat der Container keine Chance, da richtig zu verwalten, weils keine Referenzzählung gibt (außer du implementierst die selber als eine TRefCountedForm oder so). Als Singleton Registrierung würde noch gehen, wenn das Form nur einmal in der Anwendung instanziert wird.

Zu 3) In DSharp haben wir mal angefangen einen Caliburn.Micro (MVVM Framework aus .NET) zu portieren, die ganze Sache ist aber noch nicht rund und durch das Setzen der Prioritäten auf Spring4D für 2014 ist das erstmal ein wenig ins Hintertreffen geraten. Es gibt dort ein kleines Demo wie solch eine MVVM Anwendung aussieht - aber schonmal die Warnung vorweg, MVVM kann erstmal ziemlich verwirrend sein, grad wenn man alles mit CoC macht.
Interessant, diese Frage nach dem "Durchreichen" taucht immer wieder in Verbindung mit DI auf. Egal ob Container oder "poor mans DI". Du reichst nix durch. Warum? Weil du ja auch nichts selber erzeugst. Du gibst nur eins ins andere hinein. Hierbei auch beachten, nur Abhängigkeiten hineingeben, die direkt von dieser Klasse benutzt werden. Natürlich kommen hier vermutlich vermehrt factories zum Einsatz, aber diese werden auch im composition root erzeugt und an die entsprechenden Stellen verteilt. Diese Antwort erklärt das auch ganz schön.
Und ja, wenn man "poor mans DI" macht, dann hat man u.U. an einer Stelle in seiner Anwendung kilometerweise ctor Aufrufe. Davon aber nicht abschrecken lassen.

Zu 4) Datenhaltungsobjekte haben eigentlich nix im Container zu suchen, nur die Factory dafür, weil die ja in die entsprechenden Klassen injiziert werden muss, um dann dort Objekte erstellen zu können. Aber es ist auch nichts dagegen einzuwenden, Datenhaltungsobjekte an den Stellen zu erzeugen, wo man sie braucht. Hier gilt der Unterschied zwischen "newable" und "injectable".

zu 5) natürlich, allerdings wird es mit dem Spring4D Container etwas schwer sofern du Plugins auch wieder entladen willst, denn es ist nicht vorgesehen, Registrierungen während eines Programmdurchlaufes wieder zu entfernen.

zu 6) du kannst es zumindest nicht so leicht inspecten wie eine Objekt Instanz aber ich muss eher selten von außen die Eigenschaften eines Interfaces untersuchen. Außerdem muss ich ganz ehrlich dazu sagen, sobald du mehr bestimmte Prinzipien und Herangehensweisen nutzt, vermindert sich das wirkliche Debuggen (im Sinne von ich steppe durch den Code) und somit auch das inspecten diverser Eigenschaften. Unit tests helfen dabei sehr, da du da immer nur eine Klasse im Fokus hast.

P.S. Hier noch ein relativ umfassender Artikel zum Thema DI.

himitsu 10. Jun 2014 23:42

AW: Dependency Injection - Ein paar allgemeine Fragen
 
Zitat:

Zitat von Stevie (Beitrag 1261879)
(noch gibt es das nicht, aber in Zukunft plane ich eine automatische Registrierung aller Typen, die mit entsprechenden Attributen versehen sind)

Sowas wollte ich mir ja grade basteln und bin dann voll in den Bugs gelandet, welche natürlich erst später gegen Geld behoben wurden. :cry:

Stevie 11. Jun 2014 00:17

AW: Dependency Injection - Ein paar allgemeine Fragen
 
Zitat:

Zitat von himitsu (Beitrag 1261880)
Zitat:

Zitat von Stevie (Beitrag 1261879)
(noch gibt es das nicht, aber in Zukunft plane ich eine automatische Registrierung aller Typen, die mit entsprechenden Attributen versehen sind)

Sowas wollte ich mir ja grade basteln und bin dann voll in den Bugs gelandet, welche natürlich erst später gegen Geld behoben wurden. :cry:

Und welche Bugs sollen das sein?

himitsu 11. Jun 2014 07:11

AW: Dependency Injection - Ein paar allgemeine Fragen
 
Daß diese Attribute nicht funktionieren? Es gibt, nach nicht erkennbaren Mustern, Fehler in der RTTI, welche beim Auflisten der Attribute (GetAttributes) zu Exceptions/Zugriffsverletzungen führen und es sich somit keine Attribute auslesen lassen.
In XE6 geht es scheinbar, zumindestens die paar Fälle, welche ich testen konnte.

Das ToString der TRttiIndexedProperty ist ebenfalls defekt, aber das konnte ich durch manuelles Auslesen abfangen und so wichtig sind diese Strings auch nicht unbedingt, weswegen es kaum auffällt.

mkinzler 11. Jun 2014 07:14

AW: Dependency Injection - Ein paar allgemeine Fragen
 
Hast Du die Fehler gemeldet? Bzw. gibt es schon Einträge in QC?

mquadrat 11. Jun 2014 07:15

AW: Dependency Injection - Ein paar allgemeine Fragen
 
Also ich hatte mit GetAttributes bisher keine Probleme (XE2). Bringt dir natürlich nichts :-)

himitsu 11. Jun 2014 07:35

AW: Dependency Injection - Ein paar allgemeine Fragen
 
Nee, die Fehler sind ja scheinbar schon behoben und glaubt denn wirklich jemand, daß es für XE3/XE4 noch Bugfixes geben wird?

Ich hab den Code noch drin, und hab das Schritt für Schritt aktiviert und diesesmal knallte es gleich bei der zweiten Verwendung.
Das ändert sich gern mal und es ist nicht erkennbar, warum/wann es passiert ... ist fast wie bei den erühmten Internen-Compiler-Fehlern, welche auch mal verschwinden, wenn man irgendwo eine Leerzeile einfügt.


Delphi-Quellcode:
// Kurzfassung, ohne Dokumentation und so

{$IF CompilerVersion < 27}  // kann auch 26 (XE5) sein, aber das konnte ich nicht testen
  {$DEFINE SCHROTTIGERTTI}
{$IFEND}

type
  EOTAException        = class(ENonAIRException);

  OTAIntfAttribute     = class(TCustomAttribute);
  NoOTAIntfAttribute   = class(TCustomAttribute)
    ExternalIntf: Boolean;
    constructor Create(ExternalIntf: Boolean=False);
  end;
  TOTARegisterMode     = (AsClass, AsComponent, NoIconComponent);
  OTARegisterAttribute = class(TCustomAttribute) // RegisterClass, RegisterComponent, RegisterNoIcon
    Mode: TOTARegisterMode;
    constructor Create(AsClass: Boolean=False);   overload;
    constructor Create(Mode:   TOTARegisterMode); overload;
  end;
  OTAPropCatAttribute  = class(TCustomAttribute) // SetPropertyCategory
    Category, Properties: string;
    constructor Create(Category: string; Properties: string);
  end;
  ROAttribute          = class(TCustomAttribute);
Und da, wo es knallt, ist nichtmal etwas "Schlimmes" bei:
Delphi-Quellcode:
type
  {$IFNDEF SCHROTTIGERTTI}[NoOTAIntf, OTARegister]{$ENDIF}
  TOTAModuleEvents = class(TOTABase)
  end;
Wenn ich das auskommentiere, dann knallt es irgendwann später irgendwo und manchmal meistens gehen genau solche Zeilen problemlos durch.

Anfangs dachte ich, das liegt eventuell an den Parametern oder dem überladenen Consructor, aber selbst wo ich die mal alle ausgbaut hatte und es Ohne versuchte, da knallte es irgendwann dennoch wieder.



Also, ich hab's noch nicht aufgegeben, aber aktuell ist es für mich leider nicht nutzbar und nach mehreren Tagen genervtem Rumprovieren hatte ich's dann einfach vor'm Compiler versteckt.

Dawn87 11. Jun 2014 08:39

AW: Dependency Injection - Ein paar allgemeine Fragen
 
Vielen Dank für die Antwort.

Ich werde heute Abend ein wenig Zeit in meinem Demo-Projekt verbringen. Die Links und die Stichworte helfen mir vermutlich erstmal weiter.

Grüße

Stevie 11. Jun 2014 09:15

AW: Dependency Injection - Ein paar allgemeine Fragen
 
Dass GetAttributes manchmal nix ausliest, hatte ich schonmal. Allerdings hatte dies letztlich auf Multithreading in Verbindung mit Modulen laden/entladen zu tun, da dort eine raise condition auftritt (gibt's auch einen schönen Kommentar in der Rtti.pas zu)
Wie wäre es mal mit einem SSCCE zu dem Problem? Stückweise unkompilierbarer Samplecode hilft da nicht weiter.
Übrigens, schau mal ob du irgendwo die $RTTI direktive nutzt. Das kann nämlich dazu führen, dass Attribute nicht ausgelesen werden können.


Alle Zeitangaben in WEZ +1. Es ist jetzt 12:56 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz