Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Austausch von Funktionalität , Interfaces ? (https://www.delphipraxis.net/191493-austausch-von-funktionalitaet-interfaces.html)

bernhard_LA 21. Jan 2017 15:40

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 ?

Fritzew 21. Jan 2017 17:15

AW: Austausch von Funktionalität , Interfaces ?
 
Also ich denke Du bist auf dem richtigen Weg.
schau Dir auf jeden Fall mal an was https://bitbucket.org/sglienke/spring4d
Dir zu diesem Thema bietet. Für mich ist das eine der besten Bibliotheken rund um Delphi.
An dieser Stelle noch mal: Danke Stevie!!!!!

bernhard_LA 21. Jan 2017 22:23

AW: Austausch von Funktionalität , Interfaces ?
 
ich möchte die
Delphi-Quellcode:
 Myregistry: IRegistryAccess;
nicht in meinen Mainform haben, geht dies technisch ?
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.

DeddyH 22. Jan 2017 09:34

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.

bernhard_LA 22. Jan 2017 12:05

AW: Austausch von Funktionalität , Interfaces ?
 
aktuell würde der Code wieder machen was er soll :
Delphi-Quellcode:
  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.
und die Ausgelagerte Klasse

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

DeddyH 22. Jan 2017 13:05

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:
unit TierIntf;

interface

type
  ITier = interface
    ['{443BCADC-CD02-4A2A-BB55-A9AEBE03D37D}']
    procedure GibLaut;
  end;

implementation

end.
Jetzt bauen wir uns 2 Klassen, die das Interface implementieren (der Einfachheit halber in derselben Unit):
Delphi-Quellcode:
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.
Nun eine Klassenfabrik (einen Singleton), die uns je nach Bedingung die eine oder andere Klasse instanziert.
Delphi-Quellcode:
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.
Das Frontend muss dann letztendlich nur noch das Interface und die Fabrik kennen.
Delphi-Quellcode:
procedure TFormTest.ButtonTierClick(Sender: TObject);
var
  Tier: ITier;
begin
  Tier := TTierFactory.GetTier(cbHund.Checked);
  Tier.GibLaut;
end;
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.

[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]

bernhard_LA 22. Jan 2017 13:30

AW: Austausch von Funktionalität , Interfaces ?
 
Danke für das Beispiel

freimatz 24. Jan 2017 16:28

AW: Austausch von Funktionalität , Interfaces ?
 
Zitat:

Zitat von DeddyH (Beitrag 1359593)
... 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.

Einspuch:!:
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 10:11 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