ClassFactory ähnlich wie Spring Framework, Hilfe gesucht!
Hallo Zusammen!
Ich möchte in einer GlobalenListe einen Bezug vom Interface zu Klasse herstellen.
Delphi-Quellcode:
Um dann an einer anderen Stelle:
Unit MyFooImp;
Interface Implementation Uses MyContainer,MyInterfaces; // Defionition von IFoo Type TFoo = class(TInheritedObject,iFoo); Procedure bla; // bla bla end; Initialization GlobalContainer.Registerclass<IFoo,TFoo>; // Dictionary end.
Delphi-Quellcode:
Bei der Formfactory war das irgentwie kein Problem mit der Class of TForm, aber hier bekomme ich es einfach nicht hin, aus GlobalContainer.GetClass<IFoo> logisch TFoo.Create zu machen.
Procedure DoFoo;
var Foo : IFoo; begin Foo := GlobalContainer.GetClass<IFoo>; // Result unterschiedlich IFoo & TObject Foo.Bla; end; Hat jemand ne Idee? |
AW: ClassFactory ähnlich wie Spring Framework, Hilfe gesucht!
Dann wäre doch DSPING etwas für dich, bzw. kannst du dort nachschauen, wie das dort gelöst wird.
|
AW: ClassFactory ähnlich wie Spring Framework, Hilfe gesucht!
Zitat:
|
AW: ClassFactory ähnlich wie Spring Framework, Hilfe gesucht!
|
AW: ClassFactory ähnlich wie Spring Framework, Hilfe gesucht!
Damit geht das
Delphi-Quellcode:
unit Container;
interface uses System.Generics.Collections; type GlobalContainer = class private class var _ClassDict : TDictionary<TGUID, TClass>; protected class constructor Create; class destructor Destroy; public class procedure RegisterClass<TInterface : IInterface; TRegClass : class>; class function GetClass<T : IInterface> : T; end; implementation uses System.SysUtils, System.TypInfo; { GlobalContainer } class constructor GlobalContainer.Create; begin _ClassDict := TDictionary<TGUID, TClass>.Create; end; class destructor GlobalContainer.Destroy; begin _ClassDict.Free; end; class function GlobalContainer.GetClass<T> : T; var LGUID : TGUID; LClass : TClass; LObj : TObject; begin LGUID := GetTypeData( TypeInfo( T ) ).Guid; if _ClassDict.ContainsKey( LGUID ) then begin LClass := _ClassDict.Items[LGUID]; LObj := LClass.Create; Supports( LObj, LGUID, Result ); end; end; class procedure GlobalContainer.RegisterClass<TInterface, TRegClass>; var LGUID : TGUID; begin LGUID := GetTypeData( TypeInfo( TInterface ) ).Guid; _ClassDict.AddOrSetValue( LGUID, TRegClass ); end; end. |
AW: ClassFactory ähnlich wie Spring Framework, Hilfe gesucht!
Wenn man auch noch das hier
Delphi-Quellcode:
abgetütet haben will, dann so
type
IFoo = interface ['{4C53C006-737E-4F3C-A424-D5A34335EC0C}'] procedure Bar; end; TFoo = class( TInterfacedObject, IFoo ) private procedure Bar; end; TAnotherFoo = class( TFoo ) private procedure Bar; end; { TFoo } procedure TFoo.Bar; begin Writeln( Self.ClassName + '.Bar' ); end; { TAnotherFoo } procedure TAnotherFoo.Bar; begin Writeln( Self.ClassName + '.Bar' ); end; procedure Test; var LFoo : IFoo; begin GlobalContainer.RegisterClass<IFoo, TFoo>; GlobalContainer.RegisterClass<IFoo, TAnotherFoo>( 'Another' ); LFoo := GlobalContainer.GetClass<IFoo>; LFoo.Bar; // -> TFoo.Bar LFoo := GlobalContainer.GetClass<IFoo>( 'Another' ); LFoo.Bar; // -> TAnotherFoo.Bar end;
Delphi-Quellcode:
unit Container;
interface uses System.SysUtils, System.Generics.Collections; type EContainerException = class( Exception ); GlobalContainer = class private class var _ClassDict : TDictionary<string, TClass>; protected class constructor Create; class destructor Destroy; public class procedure RegisterClass<TInterface : IInterface; TRegClass : class>( const Name : string = '' ); class function GetClass<T : IInterface>( const Name : string = '' ) : T; end; implementation uses System.TypInfo; { GlobalContainer } class constructor GlobalContainer.Create; begin _ClassDict := TDictionary<string, TClass>.Create; end; class destructor GlobalContainer.Destroy; begin _ClassDict.Free; end; class function GlobalContainer.GetClass<T>( const Name : string ) : T; var LGUID : TGUID; LKey : string; LClass : TClass; LObj : TObject; begin LGUID := GetTypeData( TypeInfo( T ) ).Guid; LKey := LGUID.ToString + Name.ToUpper; if _ClassDict.ContainsKey( LKey ) then begin LClass := _ClassDict.Items[LKey]; LObj := LClass.Create; Supports( LObj, LGUID, Result ); end else raise EContainerException.CreateFmt( 'Keine Klasse zum Interface%s(%s) gefunden', [LGUID.ToString, Name] ); end; class procedure GlobalContainer.RegisterClass<TInterface, TRegClass>( const Name : string ); var LGUID : TGUID; LKey : string; begin LGUID := GetTypeData( TypeInfo( TInterface ) ).Guid; LKey := LGUID.ToString + Name.ToUpper; _ClassDict.AddOrSetValue( LKey, TRegClass ); end; end. |
AW: ClassFactory ähnlich wie Spring Framework, Hilfe gesucht!
Zitat:
Musste noch meinen GlobalContainer umbauen, das war auch ein Interface und da wollte Generics nicht. Mavarik |
AW: ClassFactory ähnlich wie Spring Framework, Hilfe gesucht!
Zitat:
Es fehlt allerdings bei der Registrierung die Prüfung, ob die Klasse das Interface unterstützt. Aber war auch nur auf die Schnelle ;) |
AW: ClassFactory ähnlich wie Spring Framework, Hilfe gesucht!
Zitat:
Delphi-Quellcode:
Mavarik
if _ClassDict.ContainsKey( LGUID ) then
begin LClass := _ClassDict.Items[LGUID]; LObj := LClass.Create; if not(Supports( LObj, LGUID, Result )) then raise EContainerException.CreateFmt( 'Keine Klasse zum Interface%s(%s) gefunden', [LGUID.ToString, Name] ); end; |
AW: ClassFactory ähnlich wie Spring Framework, Hilfe gesucht!
Zitat:
Es ist nämlich nicht immer richtig den Parameterlosen Standardkonstruktor aufzurufen, da er eigentlich versteckt wurde (z.B. bei TComponent) Über deine Logik würde zwar der richtige Speicher allokiert aber niemals die ganzen Dinge aus dem
Delphi-Quellcode:
durchlaufen. Auch Spring4d ist nicht komplett wasserdicht, da die RTTI keine Möglichkeit bietet zu ermitteln, ob eine Methode überschrieben oder verdeckt wurde. Auch der constructor Constraint bei generics leidet unter dieser Macke, so dass ein
Create(AOwner: TComponent)
Delphi-Quellcode:
ziemlich böse Folgen haben kann.
T.Create
Aktueller Source von Spring4d liegt übrigens hier: https://bitbucket.org/sglienke/spring4d (das Google Code Repo wird nicht mehr aktualisiert) Mein Vorschlag wäre übrigens, nicht Interface/Klasse als Kombination zu registrieren, sondern Interface/Factory. Somit würde aus deinem Dictionary ein
Delphi-Quellcode:
.
TDictionary<string, TFunc<IInterface>>
Die RegisterClass Methode würd ich dann so schreiben (nicht Compiler checked):
Delphi-Quellcode:
Das hat nun den Vorteil, dass du auch eine
class procedure GlobalContainer.RegisterClass<TInterface: IInterface, TRegClass: class, constructor>( const Name : string );
var LGUID : TGUID; LKey : string; begin // hier aufpassen, kann auch keine guid haben, dann sollte man hier einen Fehler werfen, denn das resolven wird nicht ohne funktionieren // ebenfalls sollte man checken, ob die Klasse, das Interface überhaupt supported LGUID := GetTypeData( TypeInfo( TInterface ) ).Guid; LKey := LGUID.ToString + Name.ToUpper; _ClassDict.AddOrSetValue( LKey, function: IInterface var obj: TObject; begin // wie gesagt, hier aufpassen, kann sein, dass das den falschen Konstruktor aufruft, das fängt auch der constructor Contraint nicht korrekt ab obj := TRegClass.Create; // kein if notwendig, wenn oben geschaut wurde, ob das hier auch funktioniert Supports(obj, LGUID, Result); end); end;
Delphi-Quellcode:
schreiben kannst und nicht nur auf Zuordnung von Interface zu Klasse beschränkt bist.
RegisterClass<IMyIntf>(function: IMyIntf begin Result := GetMyIntfFromSomewhere() end)
|
AW: ClassFactory ähnlich wie Spring Framework, Hilfe gesucht!
Zitat:
Der Einfache TFoo.Create; macht doch keine Probleme... Mavarik |
AW: ClassFactory ähnlich wie Spring Framework, Hilfe gesucht!
Zitat:
|
AW: ClassFactory ähnlich wie Spring Framework, Hilfe gesucht!
Zitat:
Mavarik |
AW: ClassFactory ähnlich wie Spring Framework, Hilfe gesucht!
Noch'n Tip: Lagere das Register in eine extra Unit aus und mach das nicht im initialization Part der Unit mit der Klasse.
Dadurch erreichst du nämlich keineswegs eine Entkopplung sondern nur 1. Untestbarkeit deiner Klasse (dadurch, dass sie im Implementation Teil deiner Unit versteckt ist) und 2. indirekte Kopplung deiner Klasse auf den Container (dadurch, dass die Container Unit im Uses deiner Klassen Unit steht). |
AW: ClassFactory ähnlich wie Spring Framework, Hilfe gesucht!
Zitat:
Mavarik |
AW: ClassFactory ähnlich wie Spring Framework, Hilfe gesucht!
Stevie hat aber Recht mit der Entkopplung. Das hatten wir gestern schon mal. Genau wie Supports ;)
|
AW: ClassFactory ähnlich wie Spring Framework, Hilfe gesucht!
Zitat:
Delphi-Quellcode:
Den Spaß werden wir hier implementieren:
unit MyInterfaces;
interface const SID_Sample : string = '{13B2116B-B78B-4E89-8070-83F9E36720EF}'; SID_OtherSample : string = '{74EF1A68-1EED-4DA4-9845-5F77AEB6D2DA}'; type ISample = interface(IUnknown) [SID_Sample] procedure Foo; function Bar : Boolean; end; IOtherSample = interface(IUnknown) [SID_OtherSample] procedure FooFoo; function BarHocker : Boolean; end; implementation end.
Delphi-Quellcode:
Die Unit Container ist ja jedem hier bekannt; so melden wir alle schön an:
unit MyClasses;
interface uses MyInterfaces; type TMySampleClass = class(TInterfacedObject, ISample) private function Bar: Boolean; procedure Foo; end; TMyOtherSampleClass = class(TMySampleClass, IOtherSample) private function BarHocker: Boolean; procedure FooFoo; end; implementation {blablabla - Methoden über Methoden} end.
Delphi-Quellcode:
Tataaaa! :party:
unit RegisterMyClasses;
interface implementation uses Container, MyInterfaces, MyClasses; initialization Container.GlobalContainer.RegisterClass<ISample, TMySampleClass>; Container.GlobalContainer.RegisterClass<IOtherSample, TMyOtherSampleClass>; end. |
AW: ClassFactory ähnlich wie Spring Framework, Hilfe gesucht!
Wie war noch das Design-Pattern!
Niemals Klassendefinitionen im Interface Teil. Sonst Referenziert man noch dagegen? Mavarik |
AW: ClassFactory ähnlich wie Spring Framework, Hilfe gesucht!
@Mavarik
Den Zahn hab ich Nick Hodges schon lange gezogen, der das ne Weile gepredigt hat - aus genau den oben genannten Gründen. Das Verstecken von Klassen im Implementation Teil, damit sie ja keiner so nutzt, ist Unfug, wenn man sie dann über ein Interface von hintenrum verfügbar macht. Denn das ist der Tod für jegliche Testbarkeit der Klasse und dann fangen einige an, mit Compiler switches und weiß der Teufel das ganze für Unittests sichtbar zu machen. Außerdem ist das garantiert kein Design Pattern sondern eine Delphi Eigenheit. Außerdem sollte man seinen Code so schreiben, dass er auch ohne einen DI Container durch manuelles Zusammenstecken funktionieren würde. Und das ist hier auch nicht der Fall. @TiGü :thumb: |
AW: ClassFactory ähnlich wie Spring Framework, Hilfe gesucht!
Ich habe den Thread noch nicht gelesen und mir für's Wochenende aufgehoben.
Weniger zu Spring (für deine unermüdliche Arbeit an Spring4D stelle ich in meinem Garten irgendwann mal eine Statur mit Lorbeerkranz von dir auf), sondern zu Klassen-Sichtbarkeit: Klassendefinitionen als Ersatz für "protected/package/... class" in den implementation-Teil zu schieben handhabe ich teilweise auch noch so. Habt Ihr die Diskussion öffentlich geführt? Rein interessenshalber. |
AW: ClassFactory ähnlich wie Spring Framework, Hilfe gesucht!
Zitat:
Wir beide betrachten DI und "coding against abstractions" nicht als Selbstzweck, sondern als Mittel zu entkoppeltem und damit isoliert testbarem Source. Und das erreicht man nicht, wenn man die eine offensichtliche Kopplung - nämlich die Benutzung einer konkreten Implementierung (Klasse) statt einer Abstraktion (Interface oder abstrakte Klasse) - durch eine andere - nämlich die Kopplung auf den Container - ersetzt. Wenn ich also meine TMySampleClass nicht mehr in einem Projekt benutzen kann, ohne den Container auch zu benutzen (da er ja fest in der Unit verdrahtet ist), dann läuft was falsch. P.S. :mrgreen: |
AW: ClassFactory ähnlich wie Spring Framework, Hilfe gesucht!
Zitat:
Ich finde es einfach praktisch, nicht eine Uses Zeile mit 30 Units zu haben, sondern nur eine für den Container. Es hat sich mir auch nicht erschlossen, warum ich die Implementierung des Interfaces nicht in der gleichen Unit machen soll. Mavarik |
AW: ClassFactory ähnlich wie Spring Framework, Hilfe gesucht!
Zitat:
Man merkt sowas immer schnell, wenn man mal kurz eine kleine Testapplikation schreibt, welche ein
Delphi-Quellcode:
testen will. Und plötzlich wundert man sich, dass in die Anwendung auch
TToaster
Delphi-Quellcode:
,
TStadtwerke
Delphi-Quellcode:
,
TUmspannwerk
Delphi-Quellcode:
und
TKraftwerk
Delphi-Quellcode:
einkompiliert werden, nur weil man den
TBraunkohletagebau
Delphi-Quellcode:
an den Strom anschließen kann. :)
TToaster
Ich stimme dir aber zu, wenn man das so praktiziert, kann das zu langen uses Klauseln kommen, dahingehend hat man es in Java oder C# einfacher mit richtigem Namespacing (so nach dem Motto
Delphi-Quellcode:
statt
uses MyLib.*;
Delphi-Quellcode:
).
uses MyLib.Foo, MyLib.Bar, MyLib.MoreStuff, ...;
|
AW: ClassFactory ähnlich wie Spring Framework, Hilfe gesucht!
Zitat:
|
AW: ClassFactory ähnlich wie Spring Framework, Hilfe gesucht!
Zitat:
15 Interface-Units + 15 Class-Units (+ Container-Unit) in der Uses-Sektion der Register-Unit. Alles übersichtlich auf einen Blick, die Interfaces und Classes wissen nichts von den DI-Kram. oder In den 15 Class-Units die Container-Unit inkludieren. Bisschen weniger in der Uses-Sektion, aber man halt wieder diese Abhängigkeit. |
AW: ClassFactory ähnlich wie Spring Framework, Hilfe gesucht!
Meine persönliche Meinung auch:
Echte Namespaces würden Delphi sicher gut tun, aber viel zu häufig sehe ich in anderen Sprache Leute mit allem möglichen immer
Delphi-Quellcode:
includen. In Delphi ist das sicher noch stärker ein Problem da ich (zumindest in der RTL und VCL) oft richtig viel Code in den initialization/finalization-Blöcken sehe und der Linker die (eigentlich unbenutzten) Sachen nicht wieder entfernt.
.*
Ich bin sogar eher ein Freund von vielen Units in der uses-Clause. Man kann allein daran schon irgendwie sehen, was los ist. Genauso ist der eigene var-Block in Methoden etwas, das ich an Pascal wirklich lieben gelernt habe. In anderen Sprachen fehlt mir das. Aber wir kommen irgendwie auch vom Thema ab 8-) |
AW: ClassFactory ähnlich wie Spring Framework, Hilfe gesucht!
Zitat:
|
AW: ClassFactory ähnlich wie Spring Framework, Hilfe gesucht!
Zitat:
Beispiel:Ich definiere im interface nur den Getter, aber die Klasse hat 'intern' auch einen Setter, weil das nunmal für die Implementierung des Subsystems notwendig ist. Nur die Anwender (also die, die nur das Interface sehen) sollen nur den Getter sehen bzw. benutzen dürfen. Ich bin schon ne Weile nicht mehr so in Delphi drin, daher ist die Frage bestimmt ein wenig blöd. Welche Frage eigentlich? Ach, ja: s.o. ;-) |
AW: ClassFactory ähnlich wie Spring Framework, Hilfe gesucht!
Die Frage hat doch mit dem Beispiel nichts zu tun?
Das (public|<nichts>) in Java bzw. (public|internal) in C# für Typen gibt es in Delphi schlichtweg nicht. Sichtbarkeit halt nur entweder "Alle" oder "Nur diese Unit" - Je nachdem ob die Deklaration im Interface- oder Implentation-Teil stattfindet. Namespaces bzw. Packages gibt es grundsätzlich nicht, deshalb erübrigt sich dann auch der Gedanke, bsp. den Setter nur im Package und nicht außerhalb verfügbar zu machen. Die Delphi-Dokumentation nennt es gerne Namespaces, aber das ist schlichtweg nicht wahr. Es sind einfach nur Prefixe im Unit-Namen. |
AW: ClassFactory ähnlich wie Spring Framework, Hilfe gesucht!
Wenn ich richtig informiert bin, macht man aber auch in C# solche Klassen, über die wir hier sprechen nicht internal sondern public, auch wenn man sie direkt nicht benutzt bzw instanziiert.
Denn auch dort schreibt man DI Container Code ja nicht direkt in diesen Namespace sondern hat einen extra Codebereich dafür (oder machts über XML). Womit wir wieder beim gleichen "Problem" wären. Man versteckt die Klassen ja nicht, damit irgendein unwissender Entwickler sie nicht aus Versehen benutzt. |
AW: ClassFactory ähnlich wie Spring Framework, Hilfe gesucht!
Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 10:49 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