![]() |
Austausch von Funktionalität , Interfaces ?
Ich habe einen Satz von ca. 20 Funktionen in diversen Projekten verwendet. So weit Ok. Nun möchte ich den Code in diesen Projekten nicht anfassen, auch die Funktionsnamen nicht. Allerdings soll durch eine Variable zukünfig gesteuert werden ob der alte Code oder neue Funtionen (selbe Funktionsnamen) aber andere Funktionalität augeführt wird.
aktuelle Idee : alles über Interfaces zu steuern
Delphi-Quellcode:
unit Unit_interface;
interface uses classes, types; type IRegistryAccess = interface ['{257E3C2E-3601-4FB3-B57C-A7E671EC1B7E}'] // eine Funktion, gleicher Name aber verschiedene Funktionalitäten function ReadInteger (IntegerName : Integer) : Integer ; end; type TRegistryReader = class (TInterfacedObject, IRegistryAccess) function ReadInteger (IntegerName : Integer) : Integer ; end; type TRegistryFileReader = class (TInterfacedObject, IRegistryAccess) function ReadInteger (IntegerName : Integer) : Integer ; end; implementation { TRegistryReader } function TRegistryReader.ReadInteger(IntegerName: Integer): Integer; begin Result := -1 ; // test , gebe -1 zurück end; { TRegistryFileReader } function TRegistryFileReader.ReadInteger(IntegerName: Integer): Integer; begin Result := 333 ; // und hier gebe einen anderen Wert zurück end; end. und hier der Aufruf :
Delphi-Quellcode:
unit UnitMain;
interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Unit_interface; type TForm1 = class(TForm) btn_GO: TButton; CheckBox1: TCheckBox; Memo1: TMemo; CheckBox2: TCheckBox; procedure btn_GOClick(Sender: TObject); private { Private-Deklarationen } public { Public-Deklarationen } Myregistry: IRegistryAccess; end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.btn_GOClick(Sender: TObject); begin if CheckBox2.Checked then begin Myregistry := TRegistryReader.Create; end else begin Myregistry := TRegistryFileReader.Create; end; memo1.lines.add ( IntToStr( Myregistry.ReadInteger(1 ))); end; end. Gibt es einen besseren Ansatz für diese Aufgabe ? |
AW: Austausch von Funktionalität , Interfaces ?
Also ich denke Du bist auf dem richtigen Weg.
schau Dir auf jeden Fall mal an was ![]() Dir zu diesem Thema bietet. Für mich ist das eine der besten Bibliotheken rund um Delphi. An dieser Stelle noch mal: Danke Stevie!!!!! |
AW: Austausch von Funktionalität , Interfaces ?
ich möchte die
Delphi-Quellcode:
nicht in meinen Mainform haben, geht dies technisch ?
Myregistry: IRegistryAccess;
Meine aktuelle Code Version funktioniert nicht mehr, ich müsste den Wert der Boolschen Variable in der initialization jeweils Berücksichtigen und neu Anpassen. Die erste Version oben hatte korrekt funktioniert.
Delphi-Quellcode:
.
type
TForm1 = class(TForm) btn_GO: TButton; CheckBox1: TCheckBox; Memo1: TMemo; CheckBox2: TCheckBox; procedure btn_GOClick(Sender: TObject); procedure CheckBox2Click(Sender: TObject); private { Private-Deklarationen } public { Public-Deklarationen } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.btn_GOClick(Sender: TObject); begin /// ich will die Klasse nicht im Hauptprogramm haben ! Memo1.lines.add(IntToStr(Myregistry.ReadInteger(1))); /// Zielversion -> im code sieht man nicht mehr welche implementierung /// ausgerufen wird Memo1.lines.add(IntToStr(ReadInteger(1))); end; procedure TForm1.CheckBox2Click(Sender: TObject); begin useregistryClass := CheckBox2.Checked; end; end
Delphi-Quellcode:
unit Unit_interface;
interface uses classes, types; type IRegistryAccess = interface ['{257E3C2E-3601-4FB3-B57C-A7E671EC1B7E}'] function ReadInteger(IntegerName: Integer): Integer; end; type TRegistryReader = class(TInterfacedObject, IRegistryAccess) function ReadInteger(IntegerName: Integer): Integer; end; type TRegistryFileReader = class(TInterfacedObject, IRegistryAccess) function ReadInteger(IntegerName: Integer): Integer; end; var Myregistry: IRegistryAccess; useregistryClass: Boolean; function ReadInteger(index: Integer): Integer; implementation function ReadInteger(index: Integer): Integer; begin Result := Myregistry.ReadInteger(1); end; { TRegistryReader } function TRegistryReader.ReadInteger(IntegerName: Integer): Integer; begin Result := -1; end; { TRegistryFileReader } function TRegistryFileReader.ReadInteger(IntegerName: Integer): Integer; begin Result := 333; end; initialization begin if useregistryClass then begin Myregistry := TRegistryReader.Create; end else begin Myregistry := TRegistryFileReader.Create; end; end; end. |
AW: Austausch von Funktionalität , Interfaces ?
Das zu benutzende Interface muss Dein Hauptformular schon kennen, wie soll es dieses auch sonst verwenden? Was es hingegen nicht kennen muss, ist die konkrete Klasse dahinter. Um wirklich flexibel zu sein bräuchtest Du aber eine ClassFactory, und das ist einiger Aufwand, wenn es denn zuverlässig funktionieren soll (ich hab das alles schon hinter mir). Willst Du diesen Aufwand nicht selber treiben, solltest Du Dir wirklich das weiter oben empfohlene Spring4D einmal anschauen, wenn ich mich recht erinnere ist da eine Projektgruppe als Demo dabei, wo die einzelnen Schritte exakt beschrieben und nachvollzogen werden.
|
AW: Austausch von Funktionalität , Interfaces ?
aktuell würde der Code wieder machen was er soll :
Delphi-Quellcode:
und die Ausgelagerte Klasse
TForm1 = class(TForm)
btn_GO: TButton; CheckBox1: TCheckBox; Memo1: TMemo; CheckBox2: TCheckBox; procedure btn_GOClick(Sender: TObject); procedure CheckBox2Click(Sender: TObject); private { Private-Deklarationen } public { Public-Deklarationen } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.btn_GOClick(Sender: TObject); begin /// ich will die Klasse nicht im Hauptprogramm haben ! /// wäre nicht nötig Memo1.lines.add(IntToStr(Myregistry.ReadInteger(1))); /// Zielversion -> im code sieht man nicht mehr welche implementierung /// ausgerufen wird Memo1.lines.add(IntToStr(ReadInteger(1))); end; procedure TForm1.CheckBox2Click(Sender: TObject); begin useregistryClass := CheckBox2.Checked; InitReader; end; end.
Delphi-Quellcode:
unit Unit_interface;
interface uses classes, types; type IRegistryAccess = interface ['{257E3C2E-3601-4FB3-B57C-A7E671EC1B7E}'] function ReadInteger(IntegerName: Integer): Integer; end; type TRegistryReader = class(TInterfacedObject, IRegistryAccess) function ReadInteger(IntegerName: Integer): Integer; end; type TRegistryFileReader = class(TInterfacedObject, IRegistryAccess) function ReadInteger(IntegerName: Integer): Integer; end; var Myregistry: IRegistryAccess; useregistryClass: Boolean; function ReadInteger(index: Integer): Integer; function InitReader : Boolean ; function FreeReader : Boolean; implementation function ReadInteger(index: Integer): Integer; begin Result := Myregistry.ReadInteger(1); end; { TRegistryReader } function TRegistryReader.ReadInteger(IntegerName: Integer): Integer; begin Result := -1; end; { TRegistryFileReader } function TRegistryFileReader.ReadInteger(IntegerName: Integer): Integer; begin Result := 333; end; function InitReader : Boolean ; begin if useregistryClass then begin Myregistry := TRegistryReader.Create; end else begin Myregistry := TRegistryFileReader.Create; end; end; function FreeReader : Boolean; begin // Myregistry.Free; end; meine Frage hierzu : laufe ich jetzt in weitere unbekannte Probleme, ist mein Ansatz so Ok? Die bisherigen Funktionen zum Lesen aus der Registry werden als Wrapper einer Klasse definiert. Welche Klasse verwendet wird definiere ich über eine Init Funktion in meiner unit. Der bisherige Code wandert in die Klasse , der neue Code entsteht in der weiteren neu zu implementierenden Klasse "TRegistryFileReader" . Was ist der Vorteil einer ClassFactory |
AW: Austausch von Funktionalität , Interfaces ?
Ein Minimalbeispiel (die "Factory" hier ist sehr simpel und unflexibel, soll aber auch nur das Prinzip verdeutlichen). Nehmen wir folgendes sehr simple Interface:
Delphi-Quellcode:
Jetzt bauen wir uns 2 Klassen, die das Interface implementieren (der Einfachheit halber in derselben Unit):
unit TierIntf;
interface type ITier = interface ['{443BCADC-CD02-4A2A-BB55-A9AEBE03D37D}'] procedure GibLaut; end; implementation end.
Delphi-Quellcode:
Nun eine Klassenfabrik (einen Singleton), die uns je nach Bedingung die eine oder andere Klasse instanziert.
unit TierClasses;
interface uses Dialogs, TierIntf; type THund = class(TInterfacedObject, ITier) public procedure GibLaut; end; TKatze = class(TInterfacedObject, ITier) public procedure GibLaut; end; implementation { THund } procedure THund.GibLaut; begin ShowMessage('Wuff'); end; { TKatze } procedure TKatze.GibLaut; begin ShowMessage('Miau'); end; end.
Delphi-Quellcode:
Das Frontend muss dann letztendlich nur noch das Interface und die Fabrik kennen.
unit TierFactory;
interface uses TierIntf, TierClasses; type TTierFactory = class abstract public class function GetTier(IstHund: Boolean): ITier; static; end; implementation { TTierFactory } class function TTierFactory.GetTier(IstHund: Boolean): ITier; begin if IstHund then Result := THund.Create else Result := TKatze.Create; end; end.
Delphi-Quellcode:
Um wirklich flexibel zu werden, wäre die Bedingung aber kein Boolean, sondern ein Datentyp mit nahezu unendlich vielen möglichen Werten, z.B. ein String. Die Factory würde dann mindestens über 2 Klassenmethoden verfügen: Registrierung einer Klasse mit einem der möglichen Werte und Rückgabe einer Instanz anhand eines registrierten Wertes (oder auch nil, wenn kein registrierter Wert vorhanden). Um das Programm um weitere Tiere zu erweitern müssten sich deren Klassen lediglich an der Factory registrieren, an der Factory selbst und erst recht am Frontend sind keine Änderungen nötig.
procedure TFormTest.ButtonTierClick(Sender: TObject);
var Tier: ITier; begin Tier := TTierFactory.GetTier(cbHund.Checked); Tier.GibLaut; end; [edit] Nachtrag: Bei der zuletzt angegebenen Vorgehensweise entfällt dann auch die Notwendigkeit, die Klassenunits in die uses-Klausel der Factory aufnehmen zu müssen. Allerdings ist das Instanzieren der entsprechenden Klasse dann nicht ganz trivial, ich habe das damals mit einer Menge RTTI realisieren müssen. [/edit] |
AW: Austausch von Funktionalität , Interfaces ?
Danke für das Beispiel
|
AW: Austausch von Funktionalität , Interfaces ?
Zitat:
Solange er die Grundlagen nicht verstanden hat sollte er sich nicht mit Spring4D beschäftigen. Lieber macht er selber mal eine kleine Factory bis das mal läuft. Bis dahin finde ich Spring4D recht abschreckend. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 12:01 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