Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Delphi (Abstract)Factory Pattern und Datentransfer (https://www.delphipraxis.net/186465-abstract-factory-pattern-und-datentransfer.html)

Lemmy 4. Sep 2015 10:34

(Abstract)Factory Pattern und Datentransfer
 
Hallo zusammen,

sorry für das lange Posting.... Und gleich der Hinweis: Ich stecke noch in Delphi 7 fest - ich denke mit Generics wäre das einfach zu lösen..

ich versuch eine AbstractFactory zu implementieren, scheitere jedoch an den Details...

Ausgangslage:

Ich habe div. unterschiedliche PDF-Formulare zu füllen, als Beispiel:

* Bestellformular Firma A
* Bestellformular Firma B
* Reklamationsformular Firma C

jedes dieser Formulare liegt zudem in einer bestimmten Version vor, die ab einem bestimmten Datum eingesetzt werden muss, Beispiel:

Bestellformular Firma A
* Version 1 gültig ab 1.1.2014
* Version 2 gültig ab 1.8.2014
....

Bislang steckt der Code dafür in einer Klasse (je Formular) drin, die dann je nach Version unterschiedlichen Code ausführt, Beispiel:

Bestellformular Firma A

Delphi-Quellcode:
   ...
  if DruckDatum >= 1.8.2014 then
  begin
    ....
  end else if Druckdatum >= 1.1.2014 then
  begin
   ...
  end
  ..
Das schreit ja quasi nach dem Factory-Pattern. Also mal flugs umgesetzt. Damit das auch schön wird, gleich mit Interfaces gebastelt:

Delphi-Quellcode:

  IFiller = Interface
  ['{7F30E379-DC1F-433B-9C56-B79D5BDEC356}']
    procedure FillPDF;
  end;

  TFillerClass = class of TPDFFiller;

  TPDFFiller = class(TInterfacedObject, IFiller)
  public
    procedure FillPDF; virtual; abstract;
  end;

  TBestellformularFirmaA = class(TPDFFiller)
  public
  end;

  TBestellformularFirmaA_201401 = class(TBestellformularFirmaA)
  public
    procedure FillPDF; override;
  end;

  TBestellformularFirmaA_201408 = class(TBestellformularFirmaA)
  public
    procedure FillPDF; override;
  end;

  TFillerFactory = class(TObject)
  ....
  public
    procedure RegisterFiller(AClass: TFillerClass; AValidSince, AValidUntil: TDateTime);
    function GetFiller(AFiller: TFillerClass; ADate: TDateTime): IFiller;
  end;

Bei der Factory kann ich dann konkrete Implementierungen hinterlegen und dann in Abhängigkeit von einem Datum anfordern:

Delphi-Quellcode:
  FFactory.RegisterFiller(TBestellformularFirmaA_201401, StrToDate('1.1.2014'), StrToDate('31.7.2014'));
  FFactory.RegisterFiller(TBestellformularFirmaA_201408, StrToDate('1.8.2014'), StrToDate('31.12.2099'));

  ...

  Filler := FFactory.GetFiller(TBestellformularFirmaA, now);
So weit sind im Grunde auch alle Beispiele die ich zu dem Thema bisher gefunden habe :-)


Mein Problem: Wie bekomme ich jetzt Daten in den "Filler"?

Lösung 1:
in die Basisklasse TBestellformularFirmaA kommen alle notwendigen Felder. Der Rückgabewert bleibt jedoch ein IPDFFiller, ein direkter Cast geht in Delphi 7 noch nicht. Hier müsste ich dann eine Funktion einbauen, die mir die Instanz der konkreten Implementierung zurück gibt, und dann nach der Abfrage einen Cast machen:

Delphi-Quellcode:
var PDF: TBestellformularFirmaA;
  Filler := FFactory.GetFiller(TBestellformularFirmaA, now);
  PDF := TBestellformularFirmaA(Filler.GetInstance);
"fühlt" sich aber irgend wie nicht so richtig an...

Lösung 2:
man könnte natürlich argumentieren, dass der PDFFiller eigentlich nichts mit den Nutzdaten zu tun haben sollte, d.h. dass es hier eine Datenhaltungsklasse geben müsste, die Datenfelder also nicht in TBestellformularFirmaA definieren sondern z.B. in TBestellformularFirmaA_Data - nur dann müsste ich im PDFFiller wieder eine Möglichkeit haben bel. Daten zu übergeben, dazu fällt mir aber auch wieder nur ein Typecast ein:

Delphi-Quellcode:
 
  IFiller = Interface
  ['{7F30E379-DC1F-433B-9C56-B79D5BDEC356}']
    procedure FillPDF(IData: Interface);
  end;
um das dann wieder in der konkreten Implementierung zu casten. Sieht mir aber auch nicht "richtig" aus.

Lösung 3: Für jedes Formular eine eigene Factory bauen - das wäre mit Generics nach meinem Empfinden recht einfach zu realisieren, in Delphi7 aber mit deutlich mehr Code und dem Aufwand in jeder Factory dann wieder casten zu müssen


Und jetzt meine Fragen:

* gibt es noch ALternativen bzw. was mich eigentlich bewegt: Was habe ich am Factory-Pattern nicht verstanden?

Grüße

frankyboy1974 4. Sep 2015 11:45

AW: (Abstract)Factory Pattern und Datentransfer
 
Zitat:

Was habe ich am Factory-Pattern nicht verstanden?
:shock:

Sir Rufo 4. Sep 2015 11:48

AW: (Abstract)Factory Pattern und Datentransfer
 
Wenn es immer die gleichen Daten sind, dann benötigst du so etwas
Delphi-Quellcode:
TData = class
  // alle benötigten Eigenschaften
end;

IFilledForm = interface
['{GUID}']
// was soll man mit dem ausgefüllten Formular machen können?
  procedure SendAsEmail( ... );
end;

IFillData = interface
['{GUID}']
  function WithThis( AData : TData ) : IFilledForm;
end;
Die Factory würde ich eher so aufbauen
Delphi-Quellcode:
TFillDataFactory = class
public
  procedure RegisterForm( AFillData : IFillData; const AAspect : string; AValidSince : TDateTime );

  function GetFillData( const AAspect : string; ADate : TDateTime ) : IFillData;
end;
Und aufgerufen wird das dann mit
Delphi-Quellcode:
MyFillDataFactory
  .GetFillData( CurrentCompany.Id, Now() )
  .FillData( TheData )
  .SendAsEmail( ... );

Lemmy 4. Sep 2015 11:59

AW: (Abstract)Factory Pattern und Datentransfer
 
Zitat:

Zitat von Sir Rufo (Beitrag 1314695)
Wenn es immer die gleichen Daten sind, dann benötigst du so etwas


sind sie leider nicht. Also pro Formular schon, nur möchte ich in einer Factory eben die komplette Bandbreite an Formularen erschlagen, so kann es anstelle eines Bestellformulars halt auch mal (um extrem zu werden) ein Antrag auf Harz IV sein ;-)


Zitat:

Zitat von Sir Rufo (Beitrag 1314695)
Die Factory würde ich eher so aufbauen
Delphi-Quellcode:
TFillDataFactory = class
public
  procedure RegisterForm( AFillData : IFillData; const AAspect : string; AValidSince : TDateTime );

  function GetFillData( const AAspect : string; ADate : TDateTime ) : IFillData;
end;

hmmm.. warum einen String für die Unterscheidung verwenden? Eine Elternklasse von der konkrete Implementierungen abgeleitet sind erscheinen mir an der Stelle zweckmäßiger...

alda 4. Sep 2015 12:11

AW: (Abstract)Factory Pattern und Datentransfer
 
Zitat:

Zitat von Lemmy (Beitrag 1314700)
hmmm.. warum einen String für die Unterscheidung verwenden? Eine Elternklasse von der konkrete Implementierungen abgeleitet sind erscheinen mir an der Stelle zweckmäßiger...

Worauf Dich SirRufo hinweisen will ist, dass Du eine harte Kopplung zwischen Implementierung und Factory hast (in dem GetFillData eine konkrete Klasse benötigt). Das Ziel ist ja, dass der Factory-Nutzer nicht wissen muss welche Implementierung hinter dem Interface IFillData steckt.
Des Weiteren macht eine Basisklasse TPDFFillData, die lediglich das Interface IFillData implementiert und die zu implementierenden Methoden als abstract an seine Spezialisierungen weitergibt, so auch keinen Sinn. Ein Interface definiert eine gemeinsame Schnittstelle und eine Klasse eine gemeinsame Vorgehensweise. Die gemeinsame Vorgehensweise ist hier m.E. nicht gegeben und so sollte jede Deiner Implementierungen selbst das Interface IFillData implementieren - eine Vererbungshierarchie ist hier Fehl am Platz.

Sir Rufo 4. Sep 2015 12:13

AW: (Abstract)Factory Pattern und Datentransfer
 
Nein, das ist definitiv nicht zweckmäßiger ... dann wirst du wohl eher so etwas benötigen
Delphi-Quellcode:
IFillData = interface
end;

IFillConcreteData = interface( IFillData )
  function WithThis( AData : TConcreteData ) : IFilledForm;
end;

TFillDataFactory = class

function GetFillData( IID : TGUID; ADate: TDateTime; out Intf: IInterface ) : Boolean;
end;
Und der Aufruf dann
Delphi-Quellcode:
var
  Fill : IFillConcreteData;
  ConcreteData: TConcreteData;

if MyFactory.GetFillData( IFillConcreteData, Now(), Fill ) then
  Fill.WithThis( ConcreteData )
    .SendAsEmail();
Im Prinzip ist es eine erweiterte
Delphi-Quellcode:
Supports
-Funktion
http://docwiki.embarcadero.com/RADSt...ekt-Interfaces

Lemmy 4. Sep 2015 12:19

AW: (Abstract)Factory Pattern und Datentransfer
 
Zitat:

Zitat von alda (Beitrag 1314703)
Worauf Dich SirRufo hinweisen will ist, dass Du eine harte Kopplung zwischen Implementierung und Factory hast

aaahh... das ist natürlich richtig... OK, dann werde ich das umbauen.

Danke euch beiden... wenn ich wieder stolpere, dann schrei ich nochmal laut :-)

Lemmy 4. Sep 2015 13:56

AW: (Abstract)Factory Pattern und Datentransfer
 
nein, da fehlt bei mir noch ein Baustein:

Wo bekommt denn die Methode GetFillData() dann die Implementierung für ein Interface her? Irgend wo in GetFillData() muss doch dann ein TConcrete.Create() erfolgen...?

Grüße

Sir Rufo 4. Sep 2015 14:32

AW: (Abstract)Factory Pattern und Datentransfer
 
Liste der Anhänge anzeigen (Anzahl: 1)
Schau dir mal das Projekt im Anhang an ;)

Lemmy 4. Sep 2015 21:33

AW: (Abstract)Factory Pattern und Datentransfer
 
nochmals Danke...

aber dann sprechen wir doch streng genommen nicht mehr von einer Factory?!? denn sie verwaltet doch letztlich nur Instanzen die ich nach bestimmten Kriterien abfragen kann....


Alle Zeitangaben in WEZ +1. Es ist jetzt 08:58 Uhr.
Seite 1 von 2  1 2      

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