![]() |
Custom Constructor /DI bei factory-basierter Objekterstellung
hallo, zusammen.
ich arbeite zzt relativ viel über DLL grenzen und hab das ganze auch schon gut mit interfaces und einer zentralen factory zur Registrierung und Erstellung neuer Typen hinbekommen. Jetzt würde ich neben der bisherigen Vorgehensweise (Interfaces, Value access per properties )auch gerne objekte bereits bei der Erstellung mit startparametern ausstatten (incl. und besonders auch i.V.m Dependency injection). Die Frage die sich mir stellt, ist wie ich hierzu eigens definierte / verdeckende constructoren einführe, die ich in einer Factory nutzen kann, was ja bei normaler Vererbung möglich ist (also z.b.
Delphi-Quellcode:
in anderer unit /anderem DLL modul soll dann sowas aufgerufen werden
// in einer DLL registrierter Typ
Type Ix = Interface [GUID] Function Getint: Integer; Procedure Setint(Anint: Integer); Property Anint: Integer Read Getint Write Setint; End; Tx = Class(Tinterfacedobject, Ix) Private Fint: Integer; Function Getint: Integer; Procedure Setint(Anint: Integer); Public Constructor Create(Anint: Integer); End; { ... } Constructor Tx.Create(Anint: Integer); Begin Fint := Anint; End; Function Tx.Getint: Integer; Begin Result := Fint; End; Procedure Tx.Setint(Anint: Integer); Begin Fint := Anint; End; Initialization // globale factory, oder davon abgeleitet: Txfactory=Tfactory<TX>; Txfactory.Register(Tx); // läuft soweit Finalization Txfactory.Unregister(Tx);
Delphi-Quellcode:
bisher mach ich das dann so, kann aber keine dependencies mitgeben //fehlende konstructoren:
Procedure New;
Var Xclass: Txclass; // txclass ist der hauptanwendung und anderen modulen ja an sich unbekannt, daher später vlt // Var xclass:tinterfacedclass; X,y: Tinterfacedobject; Begin Xclass := Txclass { Tinterfacedclass } (Txfactory.Getclass('tx'));//txclass i.A. unbekannt, da in externer DLL definiert und registriert. steht jetz nur mal so da zur verdeutlichung. // custom constructor, future dependency injection planned X := Xclass.Create(5); //oder auch möglich y:=txfactory.new('tx'); // << << Wie Kann Ich in beiden fällen Auf Den Custom Constructor Von Tx Zugreifen ? TX und TXclass sind normal ausserhalb der DDL unbekannt Assert((X as ix).Anint = 5); X.Free; y.free; End;
Delphi-Quellcode:
ich könnte mir auch vorstellen, die jeweiligen constructoren direkt mit in der factory zu registrieren (reference to constructor gibts aber leider nicht, theoretisch würde das mein problem ja lösen, aber ich weiss einfach nicht wie das zu realisieren ist)
procedure new;
var x,y:ix; xclass:tinterfacedclass; Xclass := Tinterfacedclass{Txclass}(Txfactory.Getclass('tx')); // custom constructor, future dependency injection planned X := Xclass.create as ix;//<<<< wie kann ich hier auf den custom constructor von tx zugreifen? x.anint:=5; Assert(X.Anint = 5); x:=nil; //oder so: y:=txfactory.new('tx')as ix; y.anint:=42; y:=nil; {...} also sowas in der art:
Delphi-Quellcode:
txfactory.register(tx,tx.create({custom parameters}));
|
AW: Custom Constructor /DI bei factory-basierter Objekterstellung
Leider kann ich dir nicht direkt weiterhelfen. Was ich sagen kann ist, dass es geht auf die Parameter der Konstruktoren einer Klasse zuzugreifen. Unser selbstgeschriebener Dependecy-Container kann das zumindest. Der Code der den Contrainer verwendet benutzt jedoch nie den Konstruktor für andere Parameter. Der Konstruktor wird nur verwendet um Abhängigkeiten zu injizieren. Weitere Werte kommen dann auch per Methoden oder Eigenschaften rein.
(Es gab hier mal eine Diskussion den Dependecy-Container zu erweitern, so dass man auch die berüchtigen "AOwner" Parameter übergeben kann. Ich gehe aber davon aus, dass der Dependecy-Container dann eine weitere Methode bekommen hätte.) |
AW: Custom Constructor /DI bei factory-basierter Objekterstellung
Zitat:
Okay danke für die Antwort schonmal. es ging mir ja auch hauptsächlich um die von dir genannten abhängingkeiten Wie habt ihr denn diese Konstruktoren aufgerufen (gemeinsame baseclass?), in welcher form waren die angängigkeiten? ich natürlich kann ich werte per eigenschaften über diverse interfaces setzen , aber das Problem (bei euch gings ja irgendwie) was ich hab, ist dass ich durch die factory ja nicht
Delphi-Quellcode:
aufrufen kann, ausser wenn ich ne basisklasse bereits kenne die den konstruktor zumindest abstrakt kennt.
Tx.Create(Anint: Integer)
Das mit den interfaces ist ein eigentlich praktikabler ansatz, den ich auch bisher verfolge, nur kann ich bei Objekterstellung halt den nutzer nicht ZWINGEN, möglicherweise notwendige initiale abhängigkeiten zu übergeben, bevor ers vlt vergisst als property zuzuweisen..., ausserdem bräuchte ich dann bei jedem "receiver" einer injektion dann sowas wie "Finjectable.init", also müsste ich dann ein extra interface erstellen, das von allen möglichen dependencies zu implementieren wäre...geht zwar, find ich aber irgenwie unpraktisch und unschön dazu. Zitat:
Wie ist das gemeint? b) wie wäre das denn möglich? Via RTTI, vielleicht, damit hab ich bisher noch fast gar keuine Erfahrung.Merci |
AW: Custom Constructor /DI bei factory-basierter Objekterstellung
Ja RTTI - und auch ich habe da fast keine Erfahrung damit.
|
AW: Custom Constructor /DI bei factory-basierter Objekterstellung
Hab das jetzt so zu lösen versucht. Nicht elegant und definitiv ausbaufähig(je nachdem wo man als nächstes gegen eine wand läuft:shock:) //bisher fiel mir keine bessere Lösung ein für das problem, als ein neues interface einzuführen auf das bei erstellung geprüft wird und bei erfolg die enstprechenden abhängigkeiten mitgegeben werden. Man KÖNNTE das dann wohl auch über delegates den einzelen objekten mitgeben, damit die nicht so "hässlich" werden. Die frage bleibt ob das nicht auch einfacher / eleganter geht??
Delphi-Quellcode:
Type
//Nachteil? IInjectable müsste dann von jedem "abhängigen" object implementiert werden (zb auch über delegates) Iinjectable = Interface ['{ECC8E26C-7D14-43FD-9247-066435DAEDA4}'] Procedure Inject(Dependencies: Array Of Iinterface); // Function DependsOn(Dependency: Tobject): Boolean; End; Tinjectfactory = Class(Tifactory) Class Function New(Classname: String; Dependencies: Array Of IInterface) : Iinterface;overload; Static; End; //[...] Class Function Tinjectfactory.New(Classname: String; Dependencies: Array Of IInterface): Iinterface; Var Temp: Iinjectable; Begin Result := {inherited} New(Classname);//new(param:string)inherited from parent class Try If Supports(Result, Iinjectable, Temp) Then Temp.Inject(Dependencies); Except Raise End; End; //Nutzungsbeispiel Procedure Tinjecttest.Test_factoryinject; Var Parent: Iinterface; Bla: Ibla; Begin Try Tinjectfactory.Register([Tinjectabletestobject, Tbla, Tblub]); Parent := Tinjectfactory.New('Tinjecttest.Tinjectabletestobject', [Tbla.Create, Tblub.Create]); //statt direkterstellung auch zb verschachtelung mehrerer "abhängiger" factoryaufrufe // assert(iinjectable(parent).dependson()) (Parent As Ibla).Bla; (Parent As Iblub).Blub; Bla := Parent As Ibla; Bla.Bla; Tinjectfactory.UnRegister([Tinjectabletestobject, Tbla, Tblub]); Except On E: Exception Do Raise E End; End; |
AW: Custom Constructor /DI bei factory-basierter Objekterstellung
Du könntest den DI Container von Spring4D einsetzen ;)
Damit kannst du alles injecten, was du willst. Aber dessen ungeachtet meine Empfehlung: designe den code so, dass du ihn über "pure DI" (sprich, wie würde ich das per hand machen) nutzen kannst - der Einsatz eines wie auch immer gearteten Containers kommt dann bloß oben drauf. Also keine versteckten extra für DI Konstruktoren oder Interfaces. |
AW: Custom Constructor /DI bei factory-basierter Objekterstellung
Zitat:
Natürlich wird sich sowas bei Großprojekten lohnen, bei mir handelt es sich eher um relativ kleine Anwendungen. However, würde ich auch dort gerne möglichst flexibel bleiben und einen einheitlichen Stil etablieren.... Gut, die Interfaces werd ich wohl nutzen müssen, da ich über dynamisch ladende DLLs arbeite. Daher lass ich mir alle objekte über eine zentrale stelle registieren und kann dann über entsprechende factories, die schnittstellen (intf.) zurückliefern. Das läuft ja alles soweit. Nur verstecken wollte ich eigentlich nichts (ausser wie üblich Encapsulation), mir ist nur noch kein weg eingefallen (normalerweise könnte man das, zb bei statischen packages, mit metaclasses (class of) lösen, aber da ich in den DLLS nicht an die klassen komme, wird das nur bei gemeinsamen vorfahren gehen und lässt sich somit auch relativ schwer verallgemeinern...? Wie genau ist das mit dem "per Hand machen" (pure DI) gemeint? Ist das nicht sowas wie
Delphi-Quellcode:
Constructor TX.Create(dep:TY;dep2:TZ);
begin //init with dep,dep2 end; |
AW: Custom Constructor /DI bei factory-basierter Objekterstellung
Zitat:
2. Der Unterschied ist nicht zwischen groß und klein, sondern eher Hobby oder beruflich. Wenn Du professionell Softwareentwickler bist, dann musst du m.E. dich eh in das Thema reindenken. |
AW: Custom Constructor /DI bei factory-basierter Objekterstellung
Zitat:
Pure DI ist dann das hier:
Delphi-Quellcode:
Und genau das macht ein DI Container auch. Wenn du ein IX haben willst, weiß er, dass er erst ein IY und IZ braucht (also TY und TZ bauen muss, evtl mit deren Abhängigkeiten) und gibt die dann beim Konstruktoraufruf mit.
var
x: IX; y: IY; z: IZ; begin y := TY.Create(...); z := TZ.Create(...); x := TX.Create(y, z); Ich hab vor einiger Zeit mal nen ![]() |
AW: Custom Constructor /DI bei factory-basierter Objekterstellung
Zitat:
So hab ich mir das auch vorgestellt.(IY;IZ zu nutzen ist naturlich sehr sinnvoll). Das problem bei dieser Lösung ist nur dass ich bis auf ausnahmen der in einem gemeinsamen package declarierten Typen nicht so einfach anwenden kann. (Also entweder müsste ich auf statische packages zurück, alle Klassen irgendwo gemeinsame Vorfahren mit
Delphi-Quellcode:
haben lassen oder es gibts noch einen anderen weg,über DLL's
Constructor TBase.Create(a,b,c:iirgendwas);virtual;
Delphi-Quellcode:
zu finden und mit Params Y und Z aufzurufen (evtl RTTI, so wie im o.g beispiel
X:=Tx.create(y:ianinterface,z:ianotherinterface)
![]() Und genau DAS ist zzt mein problem...ohne parameter ist ja einfach>>Tclass |
Alle Zeitangaben in WEZ +1. Es ist jetzt 07:50 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz