Einzelnen Beitrag anzeigen

Sequitar

Registriert seit: 8. Jan 2016
74 Beiträge
 
Delphi 10.4 Sydney
 
#1

Dependency Injection - zirkulare Abhängigkeiten / Lazy Init?

  Alt 16. Apr 2019, 00:10
Hallo, ich habe mich in der letzten zeit mal intensiver mit DI beschäftigt und mir ein eigenes basisframework hierfür erstellt (Ja es gibt professionelle lösungen, weiss ich, solll aber nicht das "grosse" thema werden ^^)

Angefangen mit einer Factory und einem entsprechenden Classregister kann ich diverse Typen registrieren und über ihren Klassennamen entweder direkt oder - besser - als interface ausspucken. Damit sind zzt auch standard registierungen möglich um sowas wie tfactory.new<iasimpleinterface>('standardclassforthis') durch einen kürzeren aufruf von tfactory.new<iasimpleinterface> ausführen zu lassen.

Beim Lesen von Hodges' buch zum thema "coding in delphi" und dessen fortsetzung, konnte ich damit folgendeen zum sinnvollen leben erwecken (test klasse, fragt nicht nach der sinnhaftigkeit, es geht ums prinzip):
Delphi-Quellcode:
 
Iweapon = Interface
    ['{EC1ED609-7896-4075-B9BF-51CD70E821B5}']
    Procedure Punch;
  End;

  Iknight = Interface
    ['{7C65730B-93A9-4F92-AB6E-E192E31199E1}']
    Procedure Attack;
    Procedure Setweapon(Weapon: Iweapon);
    Function Getweapon: Iweapon;
    Property Weapon: IWeapon Read Getweapon Write Setweapon;
  End;

  Tsword = Class(Tinterfacedobject, Iweapon)
    Procedure Punch;
  End;

  Tfist = Class(Tinterfacedobject, Iweapon)
    Procedure Punch;
  End;

  TKnight = Class(Tinterfacedobject, Iknight)
  Private
    [Inject('tfist')]
    FWeapon2: IWeapon;
    [Inject('tsword')]
    FWeapon: IWeapon;
    // [Inject('tfist')]
    // does only work on variables and fields
    Property Weapon2: Iweapon Read Fweapon2;
    Procedure Setweapon(Weapon: Iweapon);
    Function Getweapon: Iweapon;

  Public
    Procedure Attack;
  End;
.
Das ganze wird mit normaler RTTI typinfo über felder attribute und so gelöst..
Ausführen liesse sich das dann zb so:

Delphi-Quellcode:
Procedure Knighttest;
Begin
  Tfactory.Regdefault<Iknight>(Tknight);
  Tfactory.Reg<Iweapon>(Tfist);
  Tfactory.Regdefault<Iweapon>(Tsword);
  Var
  Knight := Tfactory.New<Iknight>;
  Knight.Attack;
  // Knight.Weapon := Tfist.Create;
  Knight.Attack;
  Tfactory.UnReg([Tknight, Tsword, Tfist]);
End;
.

So weit so gut. Nur was mache ich wenn ich dem ersten edlen Ritter einen freund oder eine ganze mannschaft bereitstellen will?:
Dabei bin ich auf das problem von zirkularen abhängigkeiten gestoßen..:

Delphi-Quellcode:
//...
 TKnightcompanion = Class(Tinterfacedobject, Iknight)
  Private
    [Inject('tsword')] // does only work on variables and fields, but ok
    FWeapon: IWeapon;
    
   [Inject('tknight')]
    [Weak]
    Fcompanion: Iknight; //!!!! HIER.: circular reference.
  

    Property Weapon: Iweapon Read Fweapon;
    Procedure Setweapon(Weapon: Iweapon);
    Function Getweapon: Iweapon;

  Public
    Procedure Attack;
  End;
Wenn ich also, bisher, den Ritter allein kämpfen lasse, funktioniert alles einwandfrei. Es ist aber offensichtlich (hoffentlch), dass beim automatisierten Erstellen und Einfügen eines Freundes (zur Zeit gleicher oder abgeleiteter Klasse) über die Factory oder zentrale Registry eine endlose Recursion bildet.


Lösungsideen:
[weak]- oder late binding, creation on demand oder sowas? Woher weiss ich welche Klasse später was braucht>>>dependency tree? (find ich auch nach langem Selbststudium schwer zu realisieren)
Kann man das "elegant" / simpel / ... lösen oder umgehen?
..

Nachtrag: Am simpelsten wäre wohl constructor / property injection. Davon wollte ich jetzt mal absehen, nachdem ich das zusammenstöpseln der objekte auch schöner hinbekam.

Danke für Eure hinweise.

Geändert von Sequitar (16. Apr 2019 um 00:15 Uhr)
  Mit Zitat antworten Zitat