Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Baumstruktur mit ObjectList (https://www.delphipraxis.net/168719-baumstruktur-mit-objectlist.html)

flosoft 6. Jun 2012 14:48

Delphi-Version: 2007

Baumstruktur mit ObjectList
 
Hallo,

bin gerade dabei eine "baumartige" Struktur (3 Ebenen) aus Objekten aufzubauen.
Im Prinzip wie eine TTreeView-Struktur - nur eben mit Objekten.

Einen ersten Ansatz habe ich hier http://www.delphipraxis.net/768187-post12.html bei Angos (s. Quellcode unten) gefunden.
Delphi-Quellcode:
type
  TMyObject = class(TObject)
  private
    FNAme: String;
  public
    property Name: String read FName write FName;
  end;

type
  TMyObjectList = class(TObjectList)
  private
    function GetItem(ndx: Integer): TMyObject;
    procedure SetItem(ndx: Integer; AValue: TMyObject);
  public
    property items[ndx: Integer]:TMyObject read GetItem write SetItem;
   
  end;
 
 
implementation

function TMyObjectList.GetItem(ndx: Integer): TMyObject;
begin
  Result := TMyObject(inherited items[ndx]);
end;

procedure TMyObjectList.SetItem(ndx: Integer; AValue: TMyObject);
begin
  items[ndx] := AValue;
end;
Erste Frage:
Sehe ich den Wald vor Bäumen nicht und in Delphi 2007 gibt es bereits eine "fertige" Lösung (TTreeView??)? :oops:

Zweite Frage:
Kann ich einfach den Ansatz von oben erweiten?
Etwa so:
Delphi-Quellcode:
type
  TMyObject = class(TObject)
  private
    FNAme: String;
  public
    property Name: String read FName write FName;
  end;

type
  TMyObjectList1 = class(TObjectList)
  private
    function GetItem(ndx: Integer): TMyObject;
    procedure SetItem(ndx: Integer; AValue: TMyObject);
  public
    property items[ndx: Integer]:TMyObject read GetItem write SetItem;
   
  end;

type
  TMyObjectList2 = class(TObjectList)
  private
    function GetItem(ndx: Integer): TMyObjectList1;
    procedure SetItem(ndx: Integer; AValue: TMyObjectList1);
  public
    property items[ndx: Integer]:TMyObjectList1 read GetItem write SetItem;
   
  end;
 
 
implementation

Danke für Eure Hilfe...

spaxxn 6. Jun 2012 15:16

AW: Baumstruktur mit ObjectList
 
Kann es sein, dass du etwas in dieser Richtung suchst?

Delphi-Quellcode:
interface
uses
  SysUtils,Contnrs;

type
  TMyObjectList = class;

  TMyObject = class(TObject)
  private
    FList: TMyObjectList;
  public
    constructor Create;
    destructor Destroy; override;
    property Items: TMyObjectList read FList;
  end;

  TMyObjectList = class(TObject)
  private
    FList: TObjectList;
    function GetItem(Index: Integer): TMyObject;
  public
    constructor Create;
    destructor Destroy; override;
    function Add(aItem: TMyObject): integer;
    procedure Delete(aIndex: Integer);
    procedure Clear;
    function Count: integer;
    function IndexOf(aItem: TMyObject): integer;
    property Items[Index: Integer]:TMyObject read GetItem; default;
  end;

implementation

{ TMyObjectList }

function TMyObjectList.Add(aItem: TMyObject): integer;
begin
  Result := FList.Add(aItem);
end;

procedure TMyObjectList.Clear;
begin
  while Count > 0 do
    Delete(0);
end;

function TMyObjectList.Count: integer;
begin
  Result:= FList.Count;
end;

constructor TMyObjectList.Create;
begin
  inherited Create;
  FList := TObjectList.Create(false);
end;

procedure TMyObjectList.Delete(aIndex: Integer);
var o: TMyObject;
begin
  o := FList[aIndex] as TMyObject;
  FList.Delete(aIndex);
  o.Free;
end;

destructor TMyObjectList.Destroy;
begin
  Clear;
  FreeAndNil(FList);
  inherited Destroy;
end;

function TMyObjectList.GetItem(Index: Integer): TMyObject;
begin
  Result := FList[Index] as TMyObject;
end;

function TMyObjectList.IndexOf(aItem: TMyObject): integer;
begin
  for Result := 0 to Count -1 do begin
    if aItem = Items[Result] then
      Exit;
  end;
  Result := -1;
end;

{ TMyObject }

constructor TMyObject.Create;
begin
  inherited Create;
  FList := TMyObjectList.Create;
end;

destructor TMyObject.Destroy;
begin
  FreeAndNil(FList);
  inherited Destroy;
end;
Aber kein Gewähr, ist nur runtergeschrieben...

Du kannst dir eine Liste erzeugen und ihr Objekte hinzufügen. Jedes Objekt hat selbst wiederrum eine Liste, der du wieder Objekte hinzufügen kannst. usw usw.

Popov 6. Jun 2012 15:23

AW: Baumstruktur mit ObjectList
 
Also ich würde einen simple Klasse erstellen (vereinfacht)

Delphi-Quellcode:
type
  TMyObject = class
    Obj: TObject;
    constructor Create;
    destructor Destroy;
  end;
Nach meiner Ansicht kannst du es dann unendlich zusammenfügen.

spaxxn 6. Jun 2012 15:26

AW: Baumstruktur mit ObjectList
 
Zitat:

Zitat von Popov (Beitrag 1169714)
Also ich würde einen simple Klasse erstellen (vereinfacht)

Delphi-Quellcode:
type
  TMyObject = class
    Obj: TObject;
    constructor Create;
    destructor Destroy;
  end;
Nach meiner Ansicht kannst du es dann unendlich zusammenfügen.

Dann müsste er aber schon Ahnung vom Prinzip der doppelt verketteten Liste haben...

shmia 6. Jun 2012 15:40

AW: Baumstruktur mit ObjectList
 
Um Baumstrukturen aufzubauen kann man das Komposite Design Pattern verwenden.

Was aber kaum bekannt ist: mit der Klasse
Delphi-Quellcode:
TComponent
kann man mit wenig Aufwand eine Baumstruktur aufbauen:

Delphi-Quellcode:
TDatenObjekt = class(TComponent)
public
  Bezeichnung : string;
end;

var
  wurzel : TComponent;
  t : TDatenObjekt;
begin
  wurzel := TComponent.Create(nil);
  t := TDatenObjekt.Create(wurzel);
  t.Bezeichnung := 'Zweig A';
  t := TDatenObjekt.Create(wurzel);
  t.Bezeichnung := 'Zweig B';
 
  t := TDatenObjekt.Create(t);
  t.Bezeichnung := 'Blatt 1 an Zweig B';

  t := TDatenObjekt.Create(t.Owner);
  t.Bezeichnung := 'Blatt 2 an Zweig B';
In dem man an der Wurzel einsteigt und über das Array
Delphi-Quellcode:
Components[]
iteriert, kann man auf alle Knoten der Baumstruktur zugreifen.
Die Anzahl der Objekte bekommt man über das Property
Delphi-Quellcode:
ComponentCount
.

Delphi-Quellcode:
// Alle Knoten rekursiv besuchen
procedure BaumAnzeigen(c:TComponent);
var
  i : Integer;
begin
  if not Assigned(c) then
    exit;
  if c is TDataObjekt then
     writeln(TDataObjekt(c).Bezeichnung)
  else
     writeln('?');
  for i := 0 to c.ComponentCount-1 do
    BaumAnzeigen(c.Components[i]);
end;

...
BaumAnzeigen(wurzel);

flosoft 7. Jun 2012 18:46

AW: Baumstruktur mit ObjectList
 
Hallo,

danke für die Tips.

@spaxxn
Das sieht schon sehr gut aus. Werde ich mir anschauen...

@spaxxn und Popov
Ich wollte eben keine Listen benutzen - die kenne ich schon...
Habe den Ehrgeiz das vollständig mit Objekten zu machen. :roll:
Vielleicht nicht clever, aber ...

@shmia
Auch das sieht gut aus!


So, mit den beiden Ansätzen komme ich sicher weiter!
Werde die heute mal testen.

Ziel des Ganzen ist es übrigens eine Fuzzy-Regelung-Klasse zu erstellen. :oops:
Ja, ja nicht mehr hip, aber ...

Danke

flosoft 7. Jun 2012 22:59

AW: Baumstruktur mit ObjectList
 
Hallo,

so, habe die Lösung von spaxxn genommen.
Copy&Paste...und funktioniert.

Chapeau! :thumb:

Habe wieder viel gelernt...

Grüße
flosoft


Hier meine ganz, ganz leicht modifizierte Version von spaxxn:
Delphi-Quellcode:
unit Fuzzy;

interface

uses SysUtils, Contnrs;

type
  TMyObjectList = class;

  TMyObject = class(TObject)
   private
     FName: String;
     FList: TMyObjectList;
     procedure SetName(Value: String);
   public
     constructor Create;
     destructor Destroy; override;
     property Name: String read FName write SetName;
     property Items: TMyObjectList read FList;
   end;

   TMyObjectList = class(TObject)
   private
     FName: String;
     FList: TObjectList;
     procedure SetName(Value: String);
     function GetItem(Index: Integer): TMyObject;
   public
     constructor Create;
     destructor Destroy; override;
     function Add(aItem: TMyObject): Integer;
     procedure Delete(aIndex: Integer);
     function Count: Integer;
     procedure Clear;
     function IndexOf(aItem: TMyObject): Integer;
     property Name: String read FName write SetName;
     property Items[Index: Integer]: TMyObject read GetItem; default;
   end;

implementation


//// TMyObjectList -------------------------------------------------------------
//-------------------------------
constructor TMyObjectList.Create;
//-------------------------------
begin
  inherited Create;
  FList:=TObjectList.Create(false);
end;

//-------------------------------
destructor TMyObjectList.Destroy;
//-------------------------------
begin
   Clear;
   FreeAndNil(FList);
   inherited Destroy;
end;

//----------------------------------------------
procedure TMyObjectList.SetName(Value: String);
//----------------------------------------------
begin
  FName:=Value;
end;

//----------------------------------------------------
function TMyObjectList.Add(aItem: TMyObject): Integer;
//----------------------------------------------------
begin
  Result:=FList.Add(aItem);
end;

//----------------------------------------------
procedure TMyObjectList.Delete(aIndex: Integer);
//----------------------------------------------
var o: TMyObject;
begin
   o:=FList[aIndex] as TMyObject;
   FList.Delete(aIndex);
   o.Free;
end;

//------------------------------------
function TMyObjectList.Count: Integer;
//------------------------------------
begin
  Result:=FList.Count;
end;

//----------------------------
procedure TMyObjectList.Clear;
//----------------------------
begin
  while Count > 0 do Delete(0);
end;

//--------------------------------------------------------
function TMyObjectList.GetItem(Index: Integer): TMyObject;
//--------------------------------------------------------
begin
  Result:=FList[Index] as TMyObject;
end;

//--------------------------------------------------------
function TMyObjectList.IndexOf(aItem: TMyObject): Integer;
//--------------------------------------------------------
begin
  for Result:=0 to Count - 1 do
  begin
    if aItem = Items[Result] then Exit;
  end;
  Result:=-1;
end;


//// TMyObject -----------------------------------------------------------------
//---------------------------
constructor TMyObject.Create;
//---------------------------
begin
   inherited Create;
   FList:=TMyObjectList.Create;
end;

//---------------------------
destructor TMyObject.Destroy;
//---------------------------
begin
  FreeAndNil(FList);
  inherited Destroy;
end;

//------------------------------------------
procedure TMyObject.SetName(Value: String);
//------------------------------------------
begin
  FName:=Value;
end;

end.
... und mein Test (sollte so zu verstehen sein :-D):
Delphi-Quellcode:
procedure TForm1.Button2Click(Sender: TObject);
var
  i: Integer;
  aObj: TMyObject;
begin
  aObj:=TMyObject.Create;
  aObj.Name:='Heizung';

  aObj.Items.Add(TMyObject.Create);
  aObj.Items[0].Name:='Fuzzy Variable 0(Temperatur)';
  aObj.Items.Add(TMyObject.Create);
  aObj.Items[1].Name:='Fuzzy Variable 1(Druck)';

  Label5.Caption:='Fuzzy Regelung: ' + aObj.Name;
  Label6.Caption:='Anzahl der Fuzzy Variablen: ' + IntToStr(aObj.Items.Count);
  for i:=0 to aObj.Items.Count - 1 do ListBox1.Items.Add(aObj.Items[i].Name);

  aObj.Items[0].Items.Add(TMyObject.Create);
  aObj.Items[0].Items[0].Name:='Fuzzy Term 0(kalt)';
  aObj.Items[0].Items.Add(TMyObject.Create);
  aObj.Items[0].Items[1].Name:='Fuzzy Term 1(lau)';
  aObj.Items[0].Items.Add(TMyObject.Create);
  aObj.Items[0].Items[2].Name:='Fuzzy Term 2(heiss)';

  Label8.Caption:='Anzahl der Fuzzy Terme (hier: Temperatur): ' + IntToStr(aObj.Items[0].Items.Count);
  for i:=0 to aObj.Items[0].Items.Count - 1 do ListBox2.Items.Add(aObj.Items[0].Items[i].Name);

  aObj.Items[0].Items[1].Items.Add(TMyObject.Create);
  aObj.Items[0].Items[1].Items[0].Name:='Eigenschaften/Methoden 0 von Fuzzy Term 1(lau)';
  aObj.Items[0].Items[1].Items.Add(TMyObject.Create);
  aObj.Items[0].Items[1].Items[1].Name:='Eigenschaften/Methoden 1 von Fuzzy Term 1(lau)';
  aObj.Items[0].Items[1].Items.Add(TMyObject.Create);
  aObj.Items[0].Items[1].Items[2].Name:='Eigenschaften/Methoden 2 von Fuzzy Term 1(lau)';
  aObj.Items[0].Items[1].Items.Add(TMyObject.Create);
  aObj.Items[0].Items[1].Items[3].Name:='Eigenschaften/Methoden 3 von Fuzzy Term 1(lau)';

  Label10.Caption:='Anzahl der E/M der Terme (hier: lau): ' + IntToStr(aObj.Items[0].Items[1].Items.Count);
  for i:=0 to aObj.Items[0].Items[1].Items.Count - 1 do ListBox3.Items.Add(aObj.Items[0].Items[1].Items[i].Name);
end;

DeddyH 8. Jun 2012 08:04

AW: Baumstruktur mit ObjectList
 
Setz doch mal testhalber ReportMemoryLeaksOnShutdown auf true.

spaxxn 8. Jun 2012 10:37

AW: Baumstruktur mit ObjectList
 
DeddyH, ich bin mir zwar ziemlich sicher, dass das leckfrei ist, aber wie oben beschrieben, ist das nur runtergeschrieben gewesen. Habs nicht ein mal getestet, von daher kann ich es nicht ausschliessen...

Oder spielst du darauf an, dass "aObj" nicht wieder freigegeben wird in Button2Click?

DeddyH 8. Jun 2012 10:48

AW: Baumstruktur mit ObjectList
 
Zum Einen das und zum Anderen: wieso wird OwnsObjects auf false gesetzt beim Erzeugen der Objektliste. Hab ich etwas übersehen, was das erforderlich macht?

spaxxn 8. Jun 2012 10:50

AW: Baumstruktur mit ObjectList
 
Nö :) hab ich mir irgendwann angewöhnt, dass selbst zu machen...

DeddyH 8. Jun 2012 10:55

AW: Baumstruktur mit ObjectList
 
OK, ich hatte das Clear im Destruktor übersehen. Trotzdem erschließt sich mir persönlich der Sinn nicht ganz, ohnehin vorhandene Funktionalitäten abzuschalten und dann selbst zu implementieren. Aber Du wirst schon wissen, was Du tust :).

spaxxn 8. Jun 2012 11:00

AW: Baumstruktur mit ObjectList
 
Naja, wenn er eine spezialisierte Struktur haben will, finde ich es persönlich schöner die Casts so zu verarbeiten. Geschmackssache würde ich sagen.

DeddyH 8. Jun 2012 11:04

AW: Baumstruktur mit ObjectList
 
Bloß gut, dass es mittlerweile generische Listen gibt (die der TE mit seinem D2007 aber leider nicht verwenden kann), die sind automatisch typsicher.

flosoft 8. Jun 2012 15:32

AW: Baumstruktur mit ObjectList
 
Hallo,

@DeddyH
Habe mit ReportMemoryLeaksOnShutdown getestet.
Keine Löcher da!
Nachdem ich natürlich aObj am Ende von Button2Click wieder freigegben hatte!
(War ja nur ein Test, ob es prinzipiell geht. Dachte das sieht man dem Quellcode an :lol:)

Was haben den generische Listen hier für einen Vorteil?
Und wenn Du sie schon ins Spiel bringst, wäre ich Dir für einen kleinen Hinweis dankbar, wie das in meinem Fall ausehen könnte.

Grüße

DeddyH 8. Jun 2012 15:48

AW: Baumstruktur mit ObjectList
 
Das ganze Geraffel mit eigenen Add-/Delete-/usw. Methoden entfällt, man deklariert sich einfach eine
TObjectlist<TMyObject>, schon geht da nichts anderes als TMyObject rein. Versucht man es trotzdem, bekommt man schon zur Entwurfszeit Compilerfehler. Leider gibt es Generics aber erst seit Delphi 2009, wird Dir also wohl nichts nützen, sofern die Angabe in Deinem Profil stimmt.

haentschman 8. Jun 2012 15:54

AW: Baumstruktur mit ObjectList
 
Nur mal so am Rande hätte ich gern eine Erklärung. Ich verstehe nicht was eine List (ob normal oder generisch) mit einer Baumstruktur zu tun hat.
A: sollten die Daten dargestellt werden ist es der List wurscht wie
B: nicht die Liste sondern das Objekt hat zu wissen wo es hingehört

Danke für Aufklärung... 8-)

flosoft 8. Jun 2012 15:56

AW: Baumstruktur mit ObjectList
 
Okay,
jetzt kann ich was damit anfangen. Delphi 2007 stimmt schon - aber das wäre vielleicht ein Grund "upzudaten".

DeddyH 8. Jun 2012 16:00

AW: Baumstruktur mit ObjectList
 
Zitat:

Zitat von haentschman (Beitrag 1170069)
Nur mal so am Rande hätte ich gern eine Erklärung. Ich verstehe nicht was eine List (ob normal oder generisch) mit einer Baumstruktur zu tun hat.
A: sollten die Daten dargestellt werden ist es der List wurscht wie
B: nicht die Liste sondern das Objekt hat zu wissen wo es hingehört

Danke für Aufklärung... 8-)

Wenn ich mir so ein Treeview anschaue, dann gibt es da Knoten, die Unterknoten von anderen sind, selbst aber auch wieder Unterknoten enthalten können. Wo siehst Du denn da einen großen Unterschied zu den Objekten, die in diesem Thread entworfen wurden?

haentschman 8. Jun 2012 16:06

AW: Baumstruktur mit ObjectList
 
schon klar... 8-)

Ich verstehe nicht ganz den Sinn der Sache sich Objekte "baumartig" abzulegen. Für z.B. spätere Anzeige in einem Treeview muß das Objekt auch nur wissen wo es dranzuhängen hat. In der Liste (Speicher) ist diese baumartige Struktur nicht nötig.
Die Frage war eigentlich:
- Warum muß die baumartige Struktur im Speicher sein ?
- Kann die Struktur über Properties der Objekte abgebildet werden (Parent etc.)


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