AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Abgeleitete Klassen auf einzelne Units verteilen

Ein Thema von BastiFantasti · begonnen am 26. Jul 2022 · letzter Beitrag vom 28. Jul 2022
Antwort Antwort
Seite 1 von 2  1 2      
BastiFantasti

Registriert seit: 5. Nov 2014
Ort: Baden Württemberg
133 Beiträge
 
Delphi 11 Alexandria
 
#1

Abgeleitete Klassen auf einzelne Units verteilen

  Alt 26. Jul 2022, 08:56
Hallo zusammen,

ich bin gerade am überlegen, wie ich mehrere von einer Elternklasse abgeleitete Unterklassen in einzelne Units auslagern kann -
und vor allem welche Units dann nachher für die Verwendung implementiert werden müssen.

Meine angedachte Struktur sieht so aus:

Code:
class TParentDevice

class TDeviceType1(TParentDevice)

class TDeviceType2(TParentDevice)

class TDeviceType3(TParentDevice)
Aufgrund des Umfangs der jeweiligen Implementierungen möchte ich die abgeleiteten Klassen nun in separate Units stecken.

z.B.

Code:
uParentDevice -> TParentDevice
uDevType1 -> TDeviceType1
uDevType2 -> TDeviceType2
uDevType3 -> TDeviceType3
In den jeweiligen uDevType* Units muss die uParentDevice unit in die "uses" Liste aufgenommen werden
um die TParentDevice Klasse ableiten zu können.

Wie aber werden nun die abgeleiteten Klassen richtig in einer Anwendung implementiert bzw. importiert?
Ich könnte natürlich in einer Case Struktur oder über Defines die jeweiligen Konstruktoren in jeder Unit in der die Klassen benötigt werden implementieren.
Dies hätte aber zur Folge, dass ich alle neu hinzukommenden Implementierungen immer in den uses aller Units nachführen müsste.
Zudem wird der Code dadurch nicht leserlicher wenn immer noch mehr defines oder cases hinzu kommen.

Ich denke, ich werde eine weitere Unit mit einer weiteren Klasse für die Implementierung in die jeweilige Anwendung anlegen - so zu sagen als Wrapper.
So käme zu den 4 o.g. Units noch eine weitere dazu, die die direkte Schnittstelle zur Anwendung darstellt und dort in die uses aufgenommen wird.

z.B. ein TDeviceHandler der im Constructor den jeweiligen DeviceType als TypeDef übergeben bekommt und intern die jeweilige Implementierung instanziert.
Der Vorteil wäre, dass neu hinzukommende abgeleitete Klassen nur in dem TDeviceHandler bekannt gegeben und implementiert werden müssen.


evtl. so:
Code:
var
   dh : TDeviceHandler;

begin
  dh := TDeviceHandler.create(dtDeviceType1);
  dh.free;
end;
und intern dann in dem TDeviceHandler.constructor

Code:

var
  MyDevice : TParentDevice;
begin
  case dtDeviceType of:
    dtDeviceType1:
     begin
       myDevice := TDeviceType1.create;
     end;
    dtDeviceType2:
     begin
       myDevice := TDeviceType2.create;
     end;
end;
Ist der "Wrapper" Ansatz der richtige oder gibt es noch eine bessere Möglichkeit dieses Szenario zu implementieren?

Danke schon mal für eure Ideen und Anregungen

Viele Grüße
Bastian
  Mit Zitat antworten Zitat
Lemmy

Registriert seit: 8. Jun 2002
Ort: Berglen
2.381 Beiträge
 
Delphi 10.4 Sydney
 
#2

AW: Abgeleitete Klassen auf einzelne Units verteilen

  Alt 26. Jul 2022, 09:18
Servus,

ja ist er. Schau mal nach Factory-Pattern / Factory Method. Es kann auch sein, dass Du (je nach Art und Umfang der Implementierung der Elternklasse / Kindklassen) das ganze mit Interfaces noch "aufhübschen" kannst.
  Mit Zitat antworten Zitat
Benutzerbild von Gausi
Gausi

Registriert seit: 17. Jul 2005
885 Beiträge
 
Delphi 11 Alexandria
 
#3

AW: Abgeleitete Klassen auf einzelne Units verteilen

  Alt 26. Jul 2022, 09:27
Wenn ich das richtig sehe, wäre die übliche Bezeichnung für deine TDeviceHandler-Klasse etwas wie TDeviceFactory.

Du hast da im wesentlichen das Konzept "Factory Pattern" gebaut.

Kann man auch noch erweitern, so dass du beim erstellen neuer abgeleiteten Klassen den Enum-Typen und den Constructor nicht erweitern musst.

Stattdessen können sich die Subklassen im initilzation-Abschnitt ihrer Unit auch bei der Factory-Klasse "registieren". Statt einem enum übergibst du dann einen String, über den die Klasse identifiziert werden kann. Die Factory kann dann nachschauen, welche Klasse zu diesem ID-String gehört, und erstellt ein dazu passendes Objekt.

Ansatz z.B. hier: https://www.delphipraxis.net/191593-...ndesign-2.html
The angels have the phone box.
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.475 Beiträge
 
Delphi 12 Athens
 
#4

AW: Abgeleitete Klassen auf einzelne Units verteilen

  Alt 26. Jul 2022, 10:44
Man kann das sehr elegant auch ohne den DeviceHandler machen:

Delphi-Quellcode:
unit uParentDevice;

interface

type
  TDeviceType = (dtDeviceType1, dtDeviceType2, dtDeviceType3);

  TParentDeviceClass = class of TParentDevice;
  TParentDevice = class
  private
    class function GetDeviceClass(dtDeviceType: TDeviceType): TParentDeviceClass; static;
  public
    constructor Create; virtual;
    class property DeviceClass[dtDeviceType: TDeviceType]: TParentDeviceClass read GetDeviceClass; default;
  end;

implementation

type
  TDeviceType1 = class(TParentDevice);
  TDeviceType2 = class(TParentDevice);
  TDeviceType3 = class(TParentDevice);

constructor TParentDevice.Create;
begin
  inherited;
end;

class function TParentDevice.GetDeviceClass(dtDeviceType: TDeviceType): TParentDeviceClass;
begin
  case dtDeviceType of
    dtDeviceType1: Result := TDeviceType1;
    dtDeviceType2: Result := TDeviceType2;
    dtDeviceType3: Result := TDeviceType3;
  end;
end;
Zum Erzeugen einer passenden Instanz schreibt man dann einfach:
Delphi-Quellcode:
var
  myDevice: TParentDevice;
begin
  myDevice := TParentDevice[dtDeviceType1].Create;
end;
Werden die abgeleiteten Klassen in separate Units verteilt, entstehen natürlich zirkuläre Unit-Referenzen. Um die zu vermeiden, gibt es andere Mechanismen, über die wir dann bei Bedarf ja noch sprechen können.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
BastiFantasti

Registriert seit: 5. Nov 2014
Ort: Baden Württemberg
133 Beiträge
 
Delphi 11 Alexandria
 
#5

AW: Abgeleitete Klassen auf einzelne Units verteilen

  Alt 26. Jul 2022, 15:29
Hallo und danke für die Antworten.

@Uwe Raabe
der Ansatz sieht in der Tat sehr schick aus. Durch diesen Ansatz will ich aber gerade das Aufsplitten der Klassen auf unterschiedliche Units erreichen.

Aktuell habe ich eine große pas Datei mit der Elternklasse sowie mehreren abgeleiteten Klassen.
Das macht das Debuggen oder das externe Vergleichen der unterschiedlichen Implementierungen mittels diff oder beyond compare sehr mühsam.

@Lemmy und @Gausi,

ich werde mir das "Factory Pattern" mal genauer anschauen und ob es da Referenzimplementierungen gibt.
Die Übergabe des jeweiligen Typs als String klingt auch sehr gut. Macht das Handling wieder etwas einfacher.

Vielen Dank schon mal. Ich werde berichten.

Viele Grüße
Bastian
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.475 Beiträge
 
Delphi 12 Athens
 
#6

AW: Abgeleitete Klassen auf einzelne Units verteilen

  Alt 26. Jul 2022, 16:26
Durch diesen Ansatz will ich aber gerade das Aufsplitten der Klassen auf unterschiedliche Units erreichen.

Aktuell habe ich eine große pas Datei mit der Elternklasse sowie mehreren abgeleiteten Klassen.
Das macht das Debuggen oder das externe Vergleichen der unterschiedlichen Implementierungen mittels diff oder beyond compare sehr mühsam.
Ich hatte ja geschrieben, dass beim Verteilen der abgeleiteten Klassen in andere Units zirkuläre Referenzen entstehen. Das ist ja nicht per se verboten. Du kannst das ja erstmal machen und die jeweiligen Units in die implementation uses Anweisung von uParentDevice schreiben:
Delphi-Quellcode:
implementation

uses
  uDevType1, uDevType2, uDevType2;

constructor TParentDevice.Create;
begin
  inherited;
end;
Da die einzelnen Ableitungen an eine Enumeration gebunden sind, muss bei einer Erweiterung eh die uParentDevice angefasst werden. Die zirkulären Referenzen sind ja auch nur jeweils bidirektionaler Natur und das Ganze somit tolerierbar. Will man das Vermeiden muss man das Prinzip halt leicht abwandeln indem man z.B. deinen TDeviceHandler-Ansatz als Basis nimmt und das DeviceClass property dahin verlagert:
Delphi-Quellcode:
type
  TDeviceHandler = class
  private
    class function GetDeviceClass(dtDeviceType: TDeviceType): TParentDeviceClass; static;
  public
    class property DeviceClass[dtDeviceType: TDeviceType]: TParentDeviceClass read GetDeviceClass; default;
  end;

implementation

uses
  uDevType1, uDevType2, uDevType2;

class function TDeviceHandler.GetDeviceClass(dtDeviceType: TDeviceType): TParentDeviceClass;
begin
  case dtDeviceType of
    dtDeviceType1: Result := TDeviceType1;
    dtDeviceType2: Result := TDeviceType2;
    dtDeviceType3: Result := TDeviceType3;
  end;
end;
Der Aufruf ändert sich dann entsprechend:

Delphi-Quellcode:
var
  myDevice1: TParentDevice;
  myDevice2: TParentDevice;
begin
  myDevice1 := TDeviceHandler[dtDeviceType1].Create;
  ...
  myDevice2 := TDeviceHandler[dtDeviceType2].Create;
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
TurboMagic

Registriert seit: 28. Feb 2016
Ort: Nordost Baden-Württemberg
2.960 Beiträge
 
Delphi 12 Athens
 
#7

AW: Abgeleitete Klassen auf einzelne Units verteilen

  Alt 26. Jul 2022, 19:33
In der DEC (Delphi Encryption Compendium) gibt es für die Hashes und Verschlüsselungsalgorithmen auch so einen Klasse Registrierungsmechanismus. Über eine ID kann man da auch numerisch die passende Klasse finden.
  Mit Zitat antworten Zitat
BastiFantasti

Registriert seit: 5. Nov 2014
Ort: Baden Württemberg
133 Beiträge
 
Delphi 11 Alexandria
 
#8

AW: Abgeleitete Klassen auf einzelne Units verteilen

  Alt 27. Jul 2022, 10:25
@Uwe Raabe

Vielen Dank für deine detaillierte Antwort.
Ich werde versuchen deine Implementierung nachzuvollziehen, in einem Beispiel umzusetzen und zu debuggen um zu sehen wie das Handling am Ende abläuft.


Viele Grüße
Bastian

Geändert von TBx (27. Jul 2022 um 12:02 Uhr) Grund: FullQuote entfernt
  Mit Zitat antworten Zitat
pmuetze

Registriert seit: 24. Nov 2008
2 Beiträge
 
#9

AW: Abgeleitete Klassen auf einzelne Units verteilen

  Alt 28. Jul 2022, 09:44
Hi,
dass der TParentDevice die einzelnen abgeleiteten TDeviceClasses kennen (und deren Units per zirkulärer Unit-Referenz einbinden) muss ist meiner Meinung nach ein Anti-Pattern und schlechtes Design.

In so einem Fall lieber folgendes Pattern verwenden:
Spendiere der Unit mit dem TParentDevice noch eine statische TDeviceController Klasse.
In dieser Controller-Klasse hälst du intern eine Liste/Map mit Name/TParentDeviceClass Mapping und zwei public Methoden:

procedure TDeviceController.RegisterClass(const aName: String; const aClass: TParentDeviceClass);
und
TDeviceController.GetClass(const aName: String): TParentDeviceClass);

Im <initialization> Abschnitt der einzelnen Units registrierst du einfach mittels TDeviceController.RegisterClass('TypX', TDevicveClassX) die jeweilige Klasse.
Uwe's "class property DeviceClass" übergibst du dann den String unter dem die Klasse registriert wurde anstelle des Enums und holst dir im Getter die konkrete Klasse über TDeviceController.GetClass(aName).

Damit sparst du dir den Enum, die zirkulären Unit Referenzen und musst die TParentDevice Klasse nicht jedesmal erweitern wenn mal eine DeviceClass hinzu kommt.
VG
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.475 Beiträge
 
Delphi 12 Athens
 
#10

AW: Abgeleitete Klassen auf einzelne Units verteilen

  Alt 28. Jul 2022, 11:59
dass der TParentDevice die einzelnen abgeleiteten TDeviceClasses kennen (und deren Units per zirkulärer Unit-Referenz einbinden) muss ist meiner Meinung nach ein Anti-Pattern und schlechtes Design.
Anti-Pattern würde ich das nicht nennen. Ich hatte ganz bewusst zunächst auf die Auslagerung der Child-Klassen verzichtet und sie erstmal nur im Implementation Teil platziert. Damit sind sie von außen nicht sichtbar. Die Verlagerung in separate Units verläuft dann vollkommen transparent und unsichtbar für den Rest der Anwendung. Auf die Problematik der daraus entstehenden zirkulären Referenzen hatte ich bereits hingewiesen und diese auch bewertet. Hier muss man zwischen Pragma und Dogma abwägen.

Spendiere der Unit mit dem TParentDevice noch eine statische TDeviceController Klasse.
Das würde ich dann sogar gleich in TParentDevice mit class methods realisieren, anstatt den Umweg über eine zusätzliche Klasse zu nehmen.

Allerdings gibt es bei dem Registrierungs-Verfahren noch ein paar Fallstricke.

Zum einen finde ich die Zuordnung über den Typ als string nicht wirklich glücklich. Auf den Enum würde ich nur ungern verzichten, schon wegen der Kompatibilität zum aktuellen Code. Die möglichen Typ-Strings sollten dann ja auch zumindest als Konstanten deklariert werden und dann muss bei einer Erweitung eben doch diese oder eine anderen Unit mit diesen Konstanten angepasst werden. Das ist also kein wirklicher Vorteil von strings und dazu ist der Enum auch noch type-safe.

Zum anderen muss auch behandelt werden, wenn sich zwei Klassen auf denselben Typ oder auf einen Typ keine Klasse registriert. Das ist zwar alles machbar, aber von der Implementierung her doch etwas aufwändiger als eine simple case-Anweisung.

Als drittes muss man noch beachten, dass die Units mit den Child-Klassen ja von keiner anderen direkt verwendet werden und somit entweder explizit in das oder die jeweiligen (auch zukünftige) Projekte aufgenommen werden müssen oder in einer separaten Registrierungs-Unit in der Uses aufzulisten sind, damit sie auch am Ende in der EXE landen.

Und in einem halben Jahr kommt dann eine neue Child-Klasse/Unit dazu und alle Projekte müssen angepasst werden.

In der pragmatischen Variante mit den Mini-Zyklen muss lediglich die Enumeration erweitert werden, die implementation Uses-Anweisung ergänzt und die case-Anweisung erweitert werden. Damit profitieren automatisch alle Projekte die uParentDevice verwenden von der neuen Child-Klasse und die betreffenden Anwender-Units können den neuen Enum-Wert verwenden. Der erweiterte Enum ist übrigens die einzige Änderung, die von außen sichtbar ist - und das ist ja auch so gewollt.

Ich finde das schon eine signifikante Erleichterung zu dem Registrierungs-Ansatz. In anderen Szenarien hat dieser seine Berechtigung, aber hier sehe ich das nicht so.

Es ist sicher hinlänglich bekannt, dass ich ein erklärter Gegner von zirkulären Referenzen bin. Andererseits gilt aber auch: Keine Regel ohne Ausnahme!
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 16:45 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