![]() |
AW: Entkoppeln von Forms
Wir haben das komplett über Interfaces und passende Factories gelöst.
Es gibt also ein Interface für eine bestimme Form z.B
Delphi-Quellcode:
Dann haben wir verschiedene Factories (Die meistens auch Interfaces sind) für unsere Forms
ICncMachineConverterForm = interface
['{DC5376B6-B124-4035-B641-6BECAD516DE9}'] function ShowcheckMarking(const aGetMark: int64; const aContact : tCncContact) : boolean; end; also zB:
Delphi-Quellcode:
Im Source dann einfach
TCnFormFactory = class // Static
class function getICncMachineConverterForm : ICncMachineConverterForm; static; end;
Delphi-Quellcode:
Ob die Form nun selber das Interface zur verfügung stellt oder wo auch immer das herkommt ist unwichtig.
iCncForm := TCnFormFactory. getICncMachineConverterForm;
if IcncForm. ShowcheckMarking(14, myContact) then...... |
AW: Entkoppeln von Forms
Ich hab jetzt mal sowas:
Delphi-Quellcode:
Damit kann eine eigene Unit, die einzelnen Forms registrieren:
type
TCreateFormFunction = reference to function(Owner: TComponent): TForm; TFormFactory = class FList: TDictionary<string, TCreateFormFunction>; public constructor Create; virtual; procedure RegisterForm(sName: String; CreateFormFunction: TCreateFormFunction); function CreateForm(sName: String; Owner: TComponent): TForm; virtual; end; implementation constructor TFormFactory.Create; begin FList := TDictionary<string, TCreateFormFunction>.Create; end; function TFormFactory.CreateForm(sName: String; Owner: TComponent): TForm; begin Result := FList[sName](Owner); end; procedure TFormFactory.RegisterForm(sName: String; CreateFormFunction: TCreateFormFunction); begin FList.Add(sName, CreateFormFunction); end;
Delphi-Quellcode:
Und mein Form01, der gar nichts von Form02 weiß, kann dieses machen:
var
FormFactory: TFormFactory; implementation uses Form01.View, Form02.View; procedure Formregistration; begin FormFactory := TFormFactory.Create; FormFactory.RegisterForm('Form02', function(Owner: TComponent): TForm begin Result := TForm02.Create(Owner); end); FormFactory.RegisterForm('Form01', function(Owner: TComponent): TForm begin Result := TForm01.Create(Owner); end); end;
Delphi-Quellcode:
Das ist doch schon mal nett. :-)
Formfactory.CreateForm('Form02',Self).Show;
Das verfeinere ich jetzt noch wie folgt: - Interfaces statt TForms - Ich will ja nicht x-beliebige Forms registrieren, Sinn hat das ja nur für jene, die das gleiche tun. Also eine eigene factory je Interface. Mal sehen. Feedback gerne, danke. |
AW: Entkoppeln von Forms
Das sieht ziemlich vertraut aus. Aber zusätzlich ist die Factory bei mir ein Singleton und wenn du keinen abgeleiteten Konstruktor verwendest, wäre mir
Delphi-Quellcode:
zu viel Tipparbeit :-D.
TCreateFormFunction
Delphi-Quellcode:
Die Registrierung würde ich eher direkt beim Form machen. Sonst wird mal was übersehen oder es kommt eine "riesige" Unit mit unzähligen Abhängigkeiten dabei raus.
interface
uses System.Classes, System.SysUtils, System.Generics.Collections, Vcl.Forms; type IFactory = interface ['{D91ADAAF-971F-497F-8EDA-A4644BA33087}'] procedure RegisterForm(AClass: TCustomFormClass); procedure UnRegisterForm(AClass: TCustomFormClass); function GetInstance(AClass: TCustomFormClass; AOwner: TComponent): TCustomForm; overload; function GetInstance(const AClassName: String; AOwner: TComponent): TCustomForm; overload; end; function FormFactory: IFactory; implementation type TFactory = class(TInterfacedObject, IFactory) class var FactoryInstance: IFactory; class constructor CreateClass; class destructor DestroyClass; strict private FClassList: TDictionary<String, TCustomFormClass>; public constructor Create; destructor Destroy; override; procedure RegisterForm(AClass: TCustomFormClass); procedure UnRegisterForm(AClass: TCustomFormClass); function GetInstance(AClass: TCustomFormClass; AOwner: TComponent): TCustomForm; overload; function GetInstance(const AClassName: String; AOwner: TComponent): TCustomForm; overload; end; function FormFactory: IFactory; begin Result := TFactory.FactoryInstance; end; { TFactory } constructor TFactory.Create; begin inherited; FClassList := TDictionary<String, TCustomFormClass>.Create; end; class constructor TFactory.CreateClass; begin TFactory.FactoryInstance := TFactory.Create; end; destructor TFactory.Destroy; begin FClassList.Free; inherited; end; class destructor TFactory.DestroyClass; begin TFactory.FactoryInstance := nil; end; function TFactory.GetInstance(AClass: TCustomFormClass; AOwner: TComponent): TCustomForm; begin Result := GetInstance(AClass.ClassName, AOwner); end; function TFactory.GetInstance(const AClassName: String; AOwner: TComponent): TCustomForm; var tmpClass: TCustomFormClass; begin if FClassList.TryGetValue(AClassName, tmpClass) then Result := tmpClass.Create(AOwner) else raise Exception.Create('Unbekannte Formklasse'); end; procedure TFactory.RegisterForm(AClass: TCustomFormClass); begin FClassList.AddOrSetValue(AClass.ClassName, AClass); end; procedure TFactory.UnRegisterForm(AClass: TCustomFormClass); begin FClassList.Remove(AClass.ClassName); end;
Delphi-Quellcode:
Und der Aufruf wäre wie gehabt:
TMyForm = class(TForm)
class constructor ClassCreate; class destructor ClassDestroy; end; ... { TMyForm } class constructor TMyForm.ClassCreate; begin FormFactory.RegisterForm(TMyForm); end; class destructor TMyForm.ClassDestroy; begin FormFactory.UnRegisterForm(TMyForm); end;
Delphi-Quellcode:
FormFactory.GetInstance('TMyForm', Self).Show;
|
AW: Entkoppeln von Forms
Ja, das ist auch sehr cool. :-)
Ich glaub, da schau ich mir was ab. Aber - wenn du erlaubst - ich würde die Interfacedefinition in eine eigene Unit geben. Sonst bindet jede Verwendung des Interfaces die Klasse mit ein. Und auch die Registrierung direkt beim Form finde ich nicht so super, weil das ja erst wieder die Form an die Factory koppelt. Und ich wollte ja Kontrolle über die Registrierung. Bei "meiner" Variante sehe ich den Vorteil, dass ich Kontrolle über das Erzeugen des Forms habe - ich könnte hier auch so was machen: FormFactory.RegisterForm('Form01', function(Owner: TComponent): TForm var tmp:TForm1; begin tmp := TForm01.Create(Owner); tmp.XY:= .... tmp.DoSomething; Result:=tmp; end); |
AW: Entkoppeln von Forms
Zitat:
Zu den gezeigten Ansätzen kann ich nur sagen: das ist kein DI sondern Service Locator. Kann man machen, aber dass man sich damit wartbaren und testbaren durch entkoppelten Code erhält, ist ein Gerücht (Einkopplung von Code Teilen is ja schließlich kein Selbstzweck). Klar, man hat erstmal nur ein Interface und nicht die Klasse - augenscheinlich entkoppelt. Allerdings hat man nun überall die Abhängigkeit auf den Service Locator / Form Factory und auch implizite Abhängigkeiten (der Name). Wenn ich ein Form irgendwo wiederverwenden möchte, bin ich darauf angewiesen, dass ich auch das über die Form Factory angefragte Form da ist und kann nicht etwa einfach an dieser Stelle ein anderes Form reingeben. Implizite Abhängigkeiten führen meiner Erfahrung nach zu viel vertrackterem Codeklump als die klassischen expliziten "TForm1 erzeugt TForm2 und das erzeugt TForm3" Abhängigkeiten. Übrigens brauch man diese
Delphi-Quellcode:
nicht, dafür gibts ja in Delphi die wunderbaren Metaklassen. Also einfach die Forms als TComponentClass registrieren und das Create davon aufrufen.
TCreateFormFunction
|
AW: Entkoppeln von Forms
Hi Steve,
danke für die Hinweise. Ich lerne immer gern dazu - wie könnte das (in Spring4D?) aussehen? BTW: Ich fand gerade diese TCreateFormFunction charmant, weil so beliebige Konstruktoren möglich sind. |
AW: Entkoppeln von Forms
Zitat:
Beispiel: FormA ist eine Maske die mir ein Suchergebnis, ein Grid anzeigt. Außerdem gibt es eine Combobox in der Tabellen stehen (Kunden, Lieferanten, Artikel etc.) und ein Button. Beim Buttonklick wird ein FormB zum Filtern der Daten geöffnet. Wurde mir das FormB von außen vorgegeben, bleibt mir keine Wahl. Über eine Factory, die ja auch ein generisches Fallback bereithalten kann, besteht die Möglichkeit auf die Benutzereingaben zu reagieren und z.B. eine Kundensuchmaske anzuzeigen. Bei "Logikendpunkten" ist das keine Frage (alle Benutzereingaben verarbeitet => Datenklasse.Export(Exportklasse)). Aber ein Form in ein Form zu injizieren klingt nach Einschränkung und nach vorgelagerter Logik. Nach Funktionalität die aus der Klasse, in die sie Gehört, rausgeholt wurde. |
AW: Entkoppeln von Forms
Zitat:
Daher kann man nun Konstruktoren bauen, die alle ihre Abhängigkeiten injektet bekommen. Gut, im Beispiel hatte ich halt nur die typischen Aufrufe des standard TComponent.Create gesehen. Zitat:
1. es braucht keine Entscheidung getroffen werden -> die konkrete Abhängigkeit wird injektet. Hierbei ist die entscheidung bei der Komposition des Objektgraph im Komposition Root getroffen worden. 2. es wird anhand von Benutzereingaben eine Entscheidung getroffen, ob ich A oder B brauche. Entweder wurden mir A und B injektet oder die entsprechenden Factories bzw Lazy von A oder B um sie bei bedarf erst zu bekommen. Das Teil, was mir dann aber A oder B oder C gibt, kann mir nur A,B oder C geben und sollte deshalb nich einfach die Wundertüte Namens ServiceLocator, FormFactory oder sonstwas allgemeines sein. Denn das öffnet Tür und Tor, sich da einfach was raus zu angeln, was man nicht erwartet - einfach mal nach Misko Hevery Service Locator googeln und die Session schauen (englisch) |
AW: Entkoppeln von Forms
Weshalb solltest du Instanzen der Formulare zu Beginn erzeugen müssen?
|
AW: Entkoppeln von Forms
Zitat:
Für den weitaus üblicheren Fall des "wird erst erzeugt, wenn gebraucht" hab ich ja schon in beiden meiner vorherigen Kommentare das Schlüsselwort "lazy initialization" erwähnt. Lazy initialization heißt aber ebend nicht "statische Abhängigkeit, hol dir doch den Krams selbst ab", sondern: injection der Factory/einer möglichkeit, die Instanz beim ersten Zugriff erst erzeugen zu lassen. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 09:37 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-2025 by Thomas Breitkreuz