AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Algorithmen, Datenstrukturen und Klassendesign Delphi Dependency Injection - Ein paar allgemeine Fragen
Thema durchsuchen
Ansicht
Themen-Optionen

Dependency Injection - Ein paar allgemeine Fragen

Ein Thema von Dawn87 · begonnen am 10. Jun 2014 · letzter Beitrag vom 11. Jun 2014
Antwort Antwort
Seite 1 von 2  1 2      
Dawn87

Registriert seit: 15. Feb 2007
Ort: Lüdenscheid
189 Beiträge
 
Delphi XE5 Professional
 
#1

Dependency Injection - Ein paar allgemeine Fragen

  Alt 10. Jun 2014, 21:38
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
  Mit Zitat antworten Zitat
Benutzerbild von Stevie
Stevie

Registriert seit: 12. Aug 2003
Ort: Soest
4.027 Beiträge
 
Delphi 10.1 Berlin Enterprise
 
#2

AW: Dependency Injection - Ein paar allgemeine Fragen

  Alt 11. Jun 2014, 00:30
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.
Stefan
“Simplicity, carried to the extreme, becomes elegance.” Jon Franklin

Delphi Sorcery - DSharp - Spring4D - TestInsight

Geändert von Stevie (11. Jun 2014 um 00:38 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.184 Beiträge
 
Delphi 12 Athens
 
#3

AW: Dependency Injection - Ein paar allgemeine Fragen

  Alt 11. Jun 2014, 00:42
(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.
$2B or not $2B
  Mit Zitat antworten Zitat
Benutzerbild von Stevie
Stevie

Registriert seit: 12. Aug 2003
Ort: Soest
4.027 Beiträge
 
Delphi 10.1 Berlin Enterprise
 
#4

AW: Dependency Injection - Ein paar allgemeine Fragen

  Alt 11. Jun 2014, 01:17
(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.
Und welche Bugs sollen das sein?
Stefan
“Simplicity, carried to the extreme, becomes elegance.” Jon Franklin

Delphi Sorcery - DSharp - Spring4D - TestInsight
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.184 Beiträge
 
Delphi 12 Athens
 
#5

AW: Dependency Injection - Ein paar allgemeine Fragen

  Alt 11. Jun 2014, 08:11
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.
$2B or not $2B

Geändert von himitsu (11. Jun 2014 um 08:15 Uhr)
  Mit Zitat antworten Zitat
mkinzler
(Moderator)

Registriert seit: 9. Dez 2005
Ort: Heilbronn
39.861 Beiträge
 
Delphi 11 Alexandria
 
#6

AW: Dependency Injection - Ein paar allgemeine Fragen

  Alt 11. Jun 2014, 08:14
Hast Du die Fehler gemeldet? Bzw. gibt es schon Einträge in QC?
Markus Kinzler
  Mit Zitat antworten Zitat
mquadrat

Registriert seit: 13. Feb 2004
1.113 Beiträge
 
Delphi XE2 Professional
 
#7

AW: Dependency Injection - Ein paar allgemeine Fragen

  Alt 11. Jun 2014, 08:15
Also ich hatte mit GetAttributes bisher keine Probleme (XE2). Bringt dir natürlich nichts
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.184 Beiträge
 
Delphi 12 Athens
 
#8

AW: Dependency Injection - Ein paar allgemeine Fragen

  Alt 11. Jun 2014, 08:35
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.
$2B or not $2B

Geändert von himitsu (11. Jun 2014 um 10:19 Uhr) Grund: weil ich es kann
  Mit Zitat antworten Zitat
Dawn87

Registriert seit: 15. Feb 2007
Ort: Lüdenscheid
189 Beiträge
 
Delphi XE5 Professional
 
#9

AW: Dependency Injection - Ein paar allgemeine Fragen

  Alt 11. Jun 2014, 09:39
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
  Mit Zitat antworten Zitat
Benutzerbild von Stevie
Stevie

Registriert seit: 12. Aug 2003
Ort: Soest
4.027 Beiträge
 
Delphi 10.1 Berlin Enterprise
 
#10

AW: Dependency Injection - Ein paar allgemeine Fragen

  Alt 11. Jun 2014, 10:15
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.
Stefan
“Simplicity, carried to the extreme, becomes elegance.” Jon Franklin

Delphi Sorcery - DSharp - Spring4D - TestInsight

Geändert von Stevie (11. Jun 2014 um 10:19 Uhr)
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      

 

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 23:16 Uhr.
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