Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   TList<>.OnBefore? (https://www.delphipraxis.net/188008-tlist-onbefore.html)

himitsu 23. Jan 2016 13:17


TList<>.OnBefore?
 
Moin,

warum kann man bei diesen komischen Listen nicht vorher prüfen, ob etwas da rein/raus darf?

Dejan Vu 23. Jan 2016 19:57

AW: TList<>.OnBefore?
 
Hä? Programmier es doch einfach. Nennt sich Wrapper. Aber Du kannst so einem Ding auch einen anderen Namen geben.

Sir Rufo 23. Jan 2016 21:27

AW: TList<>.OnBefore?
 
Sehe ich auch so. So eine Liste ist wie ein Eimer. Der soll robust sein und zuverlässig funktionieren.

Wenn ich statt dem Eimer eine selektive Reuse brauche dann baue ich mir das aus dem Eimer (Composition)

himitsu 25. Jan 2016 09:34

AW: TList<>.OnBefore?
 
In den ersten TList<T> ginge das mit dem Selbermachen noch (halbwegs), aber ich hab jetzt den Code von XE8 gesehn und muß sagen "vergesst" es, denn jede Prozedur einzelne Prozedur zum Hinzufügen, Löschen oder Entfernen separat auf eine eigene TListHelper-Prozedur geht und die wiederzum alle weder in TList noch im Helper überschreibbar (virtual) sind.

Auch werden alle Events und virtuellen Methoden erst nach den Operationen aufgerufen, wo es dann schon zu spät ist.

Es gibt einfach keine Orte, wo man sich vorher rein hängen kann, um z.B. eine "nee, das darfst du nicht"-Exception auszulösen.
Ich kann also nur hinterher ein "das hättest du jetzt tun sollen" werfen.



Gut, man könnte jetzt in der TList nocheine TList/Array ablegen, welches nach den Operationen eine Kopie macht, über die ich es dann wieder rückgangig machen kann, aber das ist absolut keine Lösung.

Der schöne Günther 25. Jan 2016 09:58

AW: TList<>.OnBefore?
 
"Komposition statt Vererbung" war eins der ersten Dinge die mir eingetrichtert wurden :-)

Ich glaube Vererbung passt hier echt nicht.

Dejan Vu 25. Jan 2016 10:19

AW: TList<>.OnBefore?
 
Einfach ausgedrückt: Du schreibst Dir deinen Container, indem Du *nicht* von TList ableitest, sondern (heute) intern eine TList verwendest. Das macht deinen Container flexibler, weil so eine TList ist ja nun auch nicht das Gelbe vom Ei. Morgen gibt es vielleicht eine "TSuperDuperList" und dann änderst Du die Implementierung deines Containers und keine Sau merkt, das Du die TSuperDuperList mit der neuen TRaumZeitFalte verwendest, wodurch die Elemente schon gefunden werden, *bevor* überhaupt danach gesucht wurde.

himitsu 25. Jan 2016 10:21

AW: TList<>.OnBefore?
 
Wäre halt zu einfach, wenn jemand an die Before-Events gedacht hätte.

Jetzt ist das so, daß man praktisch jede Methode (und die TList<> hat seeeeehhhhrrr Viele davon) überschrieben überdeckt werden müsste.
Und es kommen ständig neue Methoden dazu, die dann deine Überladungen umgehen.

Fazit: Man kann das vergessen und müsste eigentlich selber eine eigene TList schreiben, wo Emba nicht drin rumpfuschen kann. [edit] oder der Container

bernau 25. Jan 2016 10:46

AW: TList<>.OnBefore?
 
Ich muss Himitsu zustimmen. Eine OnBefore-Funktion ist m.E. mit Funktionen wie Sort gleichzusetzen und hat durchaus eine Berechtigung zur TList zu gehören. Grade bei der Generics-Version

Ansonsten bin ich wie Günther ein Fan von Komposition.

Dejan Vu 25. Jan 2016 12:38

AW: TList<>.OnBefore?
 
TList ist keine Komponente, sondern eine Klasse. Und so eine Klasse hat genau eine einzige Aufgabe: Implementierung einer Liste.

Gute Software zeichnet sich übrigens nicht dadurch aus, das man -wuppdiwupp- auf schnellstmöglichste Weise irgend ein Ding umsetzt, sondern eher dadurch, das das, was man umsetzt, nachhaltig ist, d.h. leicht zu verstehen, zu warten und zu erweitern ist.

Deine SW wäre mit TListen übersäht, wobei das OnBefore... ständig umgebogen würde. Hmmm....

himitsu 25. Jan 2016 12:58

AW: TList<>.OnBefore?
 
Wozu soll man erst einen Wrapper bauen, wo doch das OnBefore die einzige wichtige Funktion ist, die man wirklich in der Liste gebrauchen kann?
Soll ich jetzt dutzende Methoden verpacken und jeweils mit der Check-Methode davor zur Liste umleiten, anstatt nur eine Stelle in der Liste zu haben?

Gerade die generische TList<> hat rein garnichts mehr mit der alten TList zu zun.
Alleine das Interface der TList<> ist knapp 400 Zeilen lang, mit unmassen an Methoden, die man fast alle Wrappen müsste, wenn man dem Endnutzer meiner Komponente auch alle Möglichkeiten offen lassen will. (TList als Child-Liste in einer Komponente, also für einen schönen Baum an Komponenten)

TiGü 25. Jan 2016 13:13

AW: TList<>.OnBefore?
 
Zitat:

Zitat von himitsu (Beitrag 1328105)
Alleine das Interface der TList<> ist knapp 400 Zeilen lang, mit unmassen an Methoden, die man fast alle Wrappen müsste...

Reden wir wirklich von System.Generics.Collections.TList<T>?
In Delphi Seattle geht die gesamte Klassendefinition von Zeile 340 bis Zeile 450.
Die öffentlichen Methoden von 369 bis 425 mit vielen Leerzeilen dazwischen, die fünf Propertys und ggf. der Enumerator kommen noch dazu.
Aber soooviel zu wrappen ist es auch nicht, wenn man unbedingt noch die Prüfung im Add, Insert und AddRange braucht.

himitsu 25. Jan 2016 13:33

AW: TList<>.OnBefore?
 
Mit dem Helper und allem drum und dran geht es bei Zeile 20 los, aber OK, nur TList<> sind dennoch 100 Zeilen (330 bis 440)
Alleine der ListHelper von 70 bis 330 und die Events werden in jeder dessen Methode einzeln aufgerufen.

bernau 25. Jan 2016 13:46

AW: TList<>.OnBefore?
 
Zitat:

Zitat von Dejan Vu (Beitrag 1328102)
TList ist keine Komponente, sondern eine Klasse. Und so eine Klasse hat genau eine einzige Aufgabe: Implementierung einer Liste.

Wo wurde behauptet, daß TList eine Komponente ist?

Zitat:

Zitat von Dejan Vu (Beitrag 1328102)
Deine SW wäre mit TListen übersäht, wobei das OnBefore... ständig umgebogen würde. Hmmm....

Wiso? Eine virtuelle "DoBefore" wäre doch hilfreich. Wenn ich von TList<> eine Ableitung schreibe, dann muss ich diese virtuelle Methode einfach überschreiben und schon habe ich eine Klasse, die genau auf die Anforderung angepasst ist.

TiGü 25. Jan 2016 16:25

AW: TList<>.OnBefore?
 
Muss man denn auch den ganzen ListHelper nachstellen?
Ich dachte eher an sowas:
Delphi-Quellcode:
program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  System.Generics.Collections;

type
  THimiBeforeNotifyEvent<T> = reference to function(Sender: TObject; const Item: T): Boolean;

  THimiList<T> = class
  strict private
    FList: TList<T>;
    FOnBeforeNotify: THimiBeforeNotifyEvent<T>;
  public
    constructor Create;
    destructor Destroy; override;

    function Add(const Value: T): Integer; {$IFDEF Release} inline; {$ENDIF}
    property OnBeforeNotify: THimiBeforeNotifyEvent<T> read FOnBeforeNotify write FOnBeforeNotify;
  end;

constructor THimiList<T>.Create;
begin
  inherited;
  FList := TList<T>.Create;
end;

destructor THimiList<T>.Destroy;
begin
  FList.Free;
  inherited;
end;

function THimiList<T>.Add(const Value: T): Integer;
begin
  Result := 0;
  if Assigned(FOnBeforeNotify) then
  begin
    if FOnBeforeNotify(Self, Value) then
    begin
      Result := FList.Add(Value);
    end;
  end;
end;

procedure Main;
var
  HimiList: THimiList<Integer>;
begin
  HimiList := THimiList<Integer>.Create;
  HimiList.OnBeforeNotify := function(Sender: TObject; const Item: Integer): Boolean
    begin
      Result := (Item mod 2) = 0;
    end;
  try
    HimiList.Add(1);
    HimiList.Add(2);
    HimiList.Add(3);
    HimiList.Add(4);
  finally
    HimiList.Free;
  end;
end;

begin
  try
    Main;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;

end.
Zugegeben, das wird bei Insert/AddRange() etwas fricklig, aber sonst?

Dejan Vu 25. Jan 2016 18:38

AW: TList<>.OnBefore?
 
Zitat:

Zitat von bernau (Beitrag 1328116)
Zitat:

Zitat von Dejan Vu (Beitrag 1328102)
TList ist keine Komponente, sondern eine Klasse. Und so eine Klasse hat genau eine einzige Aufgabe: Implementierung einer Liste.

Wo wurde behauptet, daß TList eine Komponente ist?

Muss denn irgendwo stehen, A ist ein B, um zu betonen, das A eben *kein* B ist? Komponenten haben Events ohne Ende, um das Verhalten zu modifizieren, kleine Klassen nicht. :roll:
Zitat:

Zitat von Dejan Vu (Beitrag 1328102)
Deine SW wäre mit TListen übersäht, wobei das OnBefore... ständig umgebogen würde. Hmmm....

Wieso? Eine virtuelle "DoBefore" wäre doch hilfreich. [/QUOTE] Klar und dann? Schreibe ich mir lauter Ableitungen (composition over inheritance).
Wenn ich eine Liste benötige, bei der ich kontrollieren muss, was rein darf und was nicht, schreibe ich mir eine entsprechende Klasse.
Die Liste (bzw. allgemein: eine Klasse) mit virtuellen Methoden zu überladen und damit komplex und schwerfällig zu machen, kann es doch nicht sein.
Denn es bliebe ja nicht beim OnBefore, sondern... OnAfter? OnDelete? OAfterDelete? etc. Was ist, wenn ich ein Listenelement einfach update und damit die Validierumg im 'OnBefore' aushebeln kann?

himitsu 25. Jan 2016 21:32

AW: TList<>.OnBefore?
 
Zitat:

Delphi-Quellcode:
  TList<T> = class(TEnumerable<T>)
    function Add(const Value: T): Integer; inline;

    //procedure AddRange(const Values: array of T); overload;
    //procedure AddRange(const Collection: IEnumerable<T>); overload; inline;
    //procedure AddRange(const Collection: TEnumerable<T>); overload; inline;

    procedure Insert(Index: Integer; const Value: T); inline;

    procedure InsertRange(Index: Integer; const Values: array of T); overload;
    procedure InsertRange(Index: Integer; const Collection: IEnumerable<T>); overload;
    procedure InsertRange(Index: Integer; const Collection: TEnumerable<T>); overload;

    //function Remove(const Value: T): Integer; inline;
    function RemoveItem(const Value: T; Direction: TDirection): Integer; inline;
    procedure Delete(Index: Integer); inline;
    procedure DeleteRange(AIndex, ACount: Integer); inline;
    function ExtractItem(const Value: T; Direction: TDirection): T; inline;
    //function Extract(const Value: T): T; inline;

    procedure Exchange(Index1, Index2: Integer); inline;
    procedure Move(CurIndex, NewIndex: Integer); inline;

    procedure Clear; inline;

    property Capacity: Integer read GetCapacity write SetCapacity;
    property Count: Integer read FListHelper.FCount write SetCount;
    property Items[Index: Integer]: T read GetItem write SetItem; default;
    property List: arrayofT read FItems;
  end;

Alleine das müsste man in XE8 überschreiben verdecken und wer weiß was zukünftig noch dazu kommt.

Man hat nur Add erweitert (siehe oben), dann benutzt wer Insert und schon war es das.
Selbt List ist geil, da dynamische Arrays ein (defektes) Speichermanagement besitzen und dieses Property somit nicht ReadOnly ist.

Sir Rufo 25. Jan 2016 22:00

AW: TList<>.OnBefore?
 
Jetzt weißt du auch warum Composition und nicht Inheritence :stupid:

Dejan Vu 26. Jan 2016 06:32

AW: TList<>.OnBefore?
 
Wieso gibt es eigentlich kein
Delphi-Quellcode:
IList<T>
? Das ist doch eigentlich die Grundvoraussetzung für die erfolgreiche Verwendung bzw. nachträglichen Einsatz eines Composite? Kein Wunder, das so viele Delphianer immer noch auf Ableitungen setzen, man wird ja förmlich dazu gezwungen.

Der schöne Günther 26. Jan 2016 09:16

AW: TList<>.OnBefore?
 
Eben darüber habe ich mich in den ersten Delphi-Tagen auch gewundert. Nicht nur ich, sondern da wundern sich bis heute glaube ich eine Menge Leute drüber.

Man muss sich dann eben nicht an das mitgelieferte, sondern irgendein 3rd Party Framework für IEnumerable<T> <-- IList<T> usw. verwenden.

himitsu 26. Jan 2016 09:43

AW: TList<>.OnBefore?
 
Warum?
Weil sich Generics und Interfaces nicht sonderlich doll mögen.


Composition: Aber dann beschneide ich doch den Endbenutzer, außer ich kaufe mir jedes Jahr das neue Bugfix (Delphi) und erweitere mühsam den Code, der eigentlich aus nur einer überschriebenen Methode (oder Event) bestanden hätte.

Stevie 26. Jan 2016 10:18

AW: TList<>.OnBefore?
 
Dass ein solches OnBefore Event eine krasse Verletzung des LSP wäre, ist noch keinem aufgefallen, oder?

himitsu 26. Jan 2016 11:05

AW: TList<>.OnBefore?
 
Zitat:

Zitat von Stevie (Beitrag 1328274)
Dass ein solches OnBefore Event eine krasse Verletzung des LSP wäre, ist noch keinem aufgefallen, oder?

Wieso?

Wenn das direkt im Basistypen vorhanden wäre, würde es doch dem LSP entsprechen. :stupid:

Stevie 26. Jan 2016 11:17

AW: TList<>.OnBefore?
 
Zitat:

Zitat von himitsu (Beitrag 1328281)
Zitat:

Zitat von Stevie (Beitrag 1328274)
Dass ein solches OnBefore Event eine krasse Verletzung des LSP wäre, ist noch keinem aufgefallen, oder?

Wieso?

Weil es das Verhalten von Add und dergleichen ändert.

Übrigens würdest du selbst mit Vererbung von TList<T> nichts gewinnen, da du durch das Fehlen von virtual auf den veränderten Methoden eine solche - ich nenn sie mal TFilteredList<T> - nicht als TList<T> übergeben kannst, da die sich wieder wie eine TList<T> verhalten würde. Das heißt du müsstest immernoch alle betroffenen Methoden von TList<T> auf TFilteredList<T> ändern. Und dann kannst du auch den Ansatz mit der Komposition gehen.


Alle Zeitangaben in WEZ +1. Es ist jetzt 14:00 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