![]() |
Designfrage: Liste selber schreiben? Oder von TList oder TList<T> ableiten?
Hallo Leute,
ich bin im Moment gerade dabei für ein Projekt meine Klassen zu konzipieren und stehe dabei vor einer vermutlich eher trivialen Entscheidung. Dennoch würde ich gern eure Meinung dazu hören, ob ihr Argumente Für oder Wider habt — oder ob ihr der Meinung seid, dass es hier auf den persönlichen Geschmack ankommt. Kurz zur Situation: ich habe verschiedene Klassen, für die ich jeweils Listen-Klassen erstellen möchte. Mal als Konzept in Pseudo-Code:
Delphi-Quellcode:
Das soll jetzt mal als Beispiel dienen. Worum es mir jetzt geht: ich habe einige andere Klassen, die wie TEmployee im Beispiel, jeweils eine Listen-Klasse erhalten sollen. Im Großen und Ganzen unterscheiden diese sich jeweils nur durch diverse Properties und die Implementierung der Funktion LoadFromXML().
type
TESPBase = class constructor Create; destructor Destroy; function LoadFromXML(xml: TJclSimpleXMLElem): Boolean; virtual; function DumpToJSON: String; virtual; end; TEmployee = class(TESPBase) constructor Create; destructor Destroy; function LoadFromXML(xml: TJclSimpleXMLElem): Boolean; function DumpToJSON: String; property ID: String; property FirstName: String; property LastName: String; end; TEmployeeList = class(TESPBase) constructor Create; destructor Destroy; function LoadFromXML(xml: TJclSimpleXMLElem): Boolean; function DumpToJSON: String; procedure Add(Elem: TEmployee); procedure Clear; procedure Delete(Index: Integer); procedure IndexOfID(SearchID: String); property Items[Index: Integer]: TEmployee; property ItemsByID[Index: String]: TEmployee; end; Ich würde an der Stelle jetzt eine Klasse TEspBaseList erstellen, von der ich dann sowohl TEmployeeList als auch alle anderen Listen ableiten kann. Dafür sehe ich jetzt 3 Möglichkeiten:
Was würdet ihr an meiner Stelle tun? Gibt es noch Vor- oder Nachteile an die ich nicht gedacht habe? Oder ist das wirklich eine reine Geschmackssache? Vielen Dank schon mal! Gruß, mh166 |
AW: Designfrage: Liste selber schreiben? Oder von TList oder TList<T> ableiten?
Hast du schon mal daran gedacht, die Funktionalität von TESPBase in zwei Interfaces zu verlagern? Damit brauchst du nicht mehr die gemeinsame Basisklasse. Das befreit ungemein...
|
AW: Designfrage: Liste selber schreiben? Oder von TList oder TList<T> ableiten?
Kleine Randbemerkung:
die Basisklasse
Delphi-Quellcode:
ist fest an eine andere Klasse TJclSimpleXMLElem gebunden. Und alle abgeleiteten Klassen ebenfalls. Was wäre, wenn statt Jcl einmal eine andere XML Bibliothek verwendet werden soll?
TESPBase = class
constructor Create; destructor Destroy; function LoadFromXML(xml: TJclSimpleXMLElem): Boolean; virtual; function DumpToJSON: String; virtual; end; Die Basisklasse sollte von der Konvertierung des Objekts nach/von JSON/XML unabhängig sein. Lesetipp: ![]() |
AW: Designfrage: Liste selber schreiben? Oder von TList oder TList<T> ableiten?
und dann noch die TList<> verwenden und nicht davon ableiten - dann hast Du noch weitere Freiheit dazu gewonnen...
|
AW: Designfrage: Liste selber schreiben? Oder von TList oder TList<T> ableiten?
Ich mache selber immer noch zu viele Anfänger- und Deppenfehler um zu laut Ratschläge geben zu dürfen, aber:
|
AW: Designfrage: Liste selber schreiben? Oder von TList oder TList<T> ableiten?
Zitat:
Ja, TList<> verwenden ist sicher richtig, wie Lemmy sagte allerdings nicht davon ableiten sondern sie private Variable intern als Datenspeicher nutzen, und nach aussen nur fachlich notwendige Methoden veröffentlichen (Hinzufügen/Entfernen/Suchen). Genau das: Zitat:
|
AW: Designfrage: Liste selber schreiben? Oder von TList oder TList<T> ableiten?
Zitat:
Delphi-Quellcode:
type
TESPBase = class constructor Create; destructor Destroy; function LoadFromXML(xml: TJclSimpleXMLElem): Boolean; virtual; function DumpToJSON: String; virtual; end; TEmployee = class(TESPBase) constructor Create; destructor Destroy; function LoadFromXML(xml: TJclSimpleXMLElem): Boolean; function DumpToJSON: String; property ID: String; property FirstName: String; property LastName: String; end; TESPBaseList<T: TESPBase> = class(TESPBase) constructor Create; destructor Destroy; function LoadFromXML(xml: TJclSimpleXMLElem): Boolean; function DumpToJSON: String; procedure Add(Elem: T); procedure Clear; procedure Delete(Index: Integer); procedure IndexOfID(SearchID: String); property Items[Index: Integer]: T; property ItemsByID[Index: String]: T; end; TEmployeeList = TESPBaseList<TEmployee>; |
AW: Designfrage: Liste selber schreiben? Oder von TList oder TList<T> ableiten?
Zitat:
Zitat:
Zitat:
[/quote] Zitat:
Zitat:
|
AW: Designfrage: Liste selber schreiben? Oder von TList oder TList<T> ableiten?
Zitat:
BTW, könnte es sein, daß du in deinen abgeleiteten Klassen das override unterschlagen hast? |
AW: Designfrage: Liste selber schreiben? Oder von TList oder TList<T> ableiten?
Zitat:
Es ist, wie der Name schon sagt, eine Liste. Kein Webserver-Response-Ausleser. Irgendwann hast du einen Webserver, der dir die Daten anders formatiert. Dann musst du deine Liste von Arbeitnehmern anpassen, nur weil sich ein Webserver geändert hat? Grade wenn man einmal damit angefangen hat verleitet es immer weiter, die Klasse mit Dingen aufzublähen die sie nicht haben sollte. Irgendwann kommt eine Methode
Delphi-Quellcode:
welche anhand von irgendwelchen Kriterien Arbeitnehmer auswählt und sie entfernt. Sie stecken zwar in der Liste, aber die Liste selbst hat auch nicht die Filterung zu treffen, wer entfernt wird. Das nur als Beispiel. Schuster, bleib bei deinen Listen (höhö).
massenentlassung()
|
AW: Designfrage: Liste selber schreiben? Oder von TList oder TList<T> ableiten?
Günther hat am Samstag aufgepasst ;)
|
AW: Designfrage: Liste selber schreiben? Oder von TList oder TList<T> ableiten?
Oh ja. Jetzt brauche ich nur noch diese Clean Code-Armbändchen. Oder ich lasse mir CLEAN CODE so auf die Finger tätowieren.
|
AW: Designfrage: Liste selber schreiben? Oder von TList oder TList<T> ableiten?
Zitat:
![]() Zitat:
Zitat:
Zitat:
Zitat:
|
AW: Designfrage: Liste selber schreiben? Oder von TList oder TList<T> ableiten?
Zitat:
![]() PS: Die beiden mittleren Zitate waren von mir, werden aber Uwe zugeschrieben. Verrückte Welt. Wenigstens haftet er jetzt juristisch für alle daraus entstehenden Schäden. |
AW: Designfrage: Liste selber schreiben? Oder von TList oder TList<T> ableiten?
Zitat:
Zitat:
|
AW: Designfrage: Liste selber schreiben? Oder von TList oder TList<T> ableiten?
Zitat:
|
AW: Designfrage: Liste selber schreiben? Oder von TList oder TList<T> ableiten?
Das Buch 'Clean Code' ist wirklich zum empfehlen. Die deutsche Version ist sehr gut geschrieben und verständlich. Gehört ins Regal wie früher Donald Knuth.
|
AW: Designfrage: Liste selber schreiben? Oder von TList oder TList<T> ableiten?
Zitat:
|
AW: Designfrage: Liste selber schreiben? Oder von TList oder TList<T> ableiten?
Zitat:
Dürfte ich dich eventuell aber noch einmal um ein Beispiel für diese Delegation der Implementation von Interfaces bitten? :) Ich find irgendwie nix, dass ich so wirklich verstehe. :oops: |
AW: Designfrage: Liste selber schreiben? Oder von TList oder TList<T> ableiten?
Kurzfassung:
Delphi-Quellcode:
Langfassung:
// Delegiert das Krachmachen an einen TKrachmacher
TWurstfabrik = class(TInterfacedObject, IKrachmacher) private var myKrachmacher: IKrachmacher; protected property krachDelegate: IKrachmacher read myKrachmacher implements IKrachmacher; public constructor Create(); end;
Delphi-Quellcode:
Syntaxmäßig ein bisschen lang.
program Project2;
{$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils; type IKrachmacher = interface ['{CB618B3C-8057-4349-8CA3-8047907671A8}'] procedure macheKrach(); end; TKrachmacher = class(TInterfacedObject, IKrachmacher) public procedure macheKrach(); end; // Delegiert das Krachmachen an einen TKrachmacher TWurstfabrik = class(TInterfacedObject, IKrachmacher) private var myKrachmacher: IKrachmacher; protected property krachDelegate: IKrachmacher read myKrachmacher implements IKrachmacher; public constructor Create(); end; { TKrachmacher } procedure TKrachmacher.macheKrach; begin case Random(3) of 0: WriteLn('<Ratter, ratter, quietsch>'); 1: WriteLn('*entweichender Dampf*'); 2: WriteLn('*heulende Sirene*'); end; end; { TWurstfabrik } constructor TWurstfabrik.Create(); begin inherited Create(); myKrachmacher := TKrachmacher.Create(); end; var myKrachmacher: IKrachmacher; begin try myKrachmacher := TWurstFabrik.Create(); myKrachmacher.macheKrach(); myKrachmacher.macheKrach(); myKrachmacher.macheKrach(); except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; ReadLn; end.
Delphi-Quellcode:
kann man nur auf (eigentlich überflüssige) Properties anwenden, nicht direkt auf Felder.
implements
|
AW: Designfrage: Liste selber schreiben? Oder von TList oder TList<T> ableiten?
Zitat:
Delphi-Quellcode:
type
TDumpToJSON = class private FInstance: TObject; protected function DumpToJSON: string; property Instance: TObject read FInstance; public constructor Create(AInstance: TObject); end; IDumpToJSON = interface function DumpToJSON: string; end; TMyClass = class(TInterfacedPersistent, IDumpToJSON) private FDumpToJSON: TDumpToJSON; function GetDumpToJSON: TDumpToJSON; protected property DumpToJSON: TDumpToJSON read GetDumpToJSON implements IDumpToJSON; public destructor Destroy; override; end; TMyOtherClass = class(TInterfacedPersistent, IDumpToJSON) private FDumpToJSON: TDumpToJSON; function GetDumpToJSON: TDumpToJSON; protected property DumpToJSON: TDumpToJSON read GetDumpToJSON implements IDumpToJSON; public destructor Destroy; override; end; constructor TDumpToJSON.Create(AInstance: TObject); begin inherited Create; FInstance := AInstance; end; function TDumpToJSON.DumpToJSON: string; begin result := TJson.ObjectToJsonString(Instance); end; destructor TMyClass.Destroy; begin FDumpToJSON.Free; inherited Destroy; end; function TMyClass.GetDumpToJSON: TDumpToJSON; begin if FDumpToJSON = nil then begin FDumpToJSON := TDumpToJSON.Create(Self); end; result := FDumpToJSON; end; destructor TMyOtherClass.Destroy; begin FDumpToJSON.Free; inherited Destroy; end; function TMyOtherClass.GetDumpToJSON: TDumpToJSON; begin if FDumpToJSON = nil then begin FDumpToJSON := TDumpToJSON.Create(Self); end; result := FDumpToJSON; end; Sowohl TMyClass als auch TMyOtherClass leiten die Implementierung des Interfaces an die Instanz einer anderen Klasse TDumpToJSON weiter, die interessanterweise über IDumpToJSON gar nichts weiß. |
AW: Designfrage: Liste selber schreiben? Oder von TList oder TList<T> ableiten?
Zitat:
Und außerdem muss das TDumpToJSON nun für alle Klassen wissen, wie sie zu dumpen sind (wohlmöglich über RTTI, klar das geht). Aber dann würde ich fragen, warum muss die dann in dem Objekt drin sein und warum muss son Objekt das Interface implementieren? Bei nem bisschen Stringgematsche für JSON mag das noch harmlos sein, aber dann gehts los und jemand möchte die Instanzen in ner Datenbank speichern und schon haste irgendnen SQL oder Datenbank Zeugs an allen deinen Objekten kleben. Andere Ansätze sind oft, dass die Klasse, die persistierbar ist, nur die Informationen ausspuckt, die so ein Persistierer benötigt (z.B. RTTI oder was selbstgebautes). Oder dass es ein Interface zum Persistierer übergeben bekommt und dann selbst seine Infos da rein schreibt. Dann hast du maximal entkoppelte Klassen, so ne Liste oder Objekte wissen gar nix von ihrem Glück, persistierbar zu sein (möglicherweise nur über ihre RTTI/Attribute) und du kannst sie dann an die jeweiligen Instanzen geben, die diesen Job übernehmen. Misko geht ![]() |
AW: Designfrage: Liste selber schreiben? Oder von TList oder TList<T> ableiten?
Vielen Dank an Günther und Uwe: ich glaub ich habs jetzt verstanden. Wobei ich ehrlich gesagt den Umweg über eine extra Property dafür vergleichsweise hacky finde. :? Aber gut, wenn Delphi es so will ... Zumindest weiß ich jetzt, wies funktioniert.
Für den Moment hab ich aber erstmal ![]() Vielen Dank euch allen! Hab wieder ne ganze Menge hierbei gelernt. :thumb: |
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:17 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 by Thomas Breitkreuz