Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Mal wieder Klassenvererbung (https://www.delphipraxis.net/24681-mal-wieder-klassenvererbung.html)

Jelly 24. Jun 2004 14:30


Mal wieder Klassenvererbung
 
Hallo,

ich habe mir eine Grundklasse TGeometrie erstellt und von dieser 2 weitere abgeleitet, nämlich TKugel und TQuader... Soweit kein Problem.

Desweiteren hab ich noch eine Klasse TGeometrien von TObject abgeleitet, und in dieser Klasse als Eigenschaft Items : array of TGeometrie, also ein dynamisches Array, welches all meine Geometrien enthalten soll...

Jetzt sollen aus einer Textdatei die Geometrien geladen werden, und in diesem Array gespeichert werden. In der Textdatei steht dann der Typ der Geometrie drin (Kugel oder Quader), und beim Erstellen der Geometrie benutz ich folgenden Code:
Delphi-Quellcode:
if GeomTyp is TKugel then Fitems[i] := TKugel.create ;
if GeomTyp is TQuader then Fitems[i] := TQuader.create ;
Das klappt auch soweit ganz gut. Allerdings ist diese Typabfrage sehr nervig wenn ich meine geometrischen Formen erweitern will (z.B. Tetraeder), weil ich dann auch in der klasse TGeometrien eine weitere if-Bedingung dazu brauch, damit ich richtig caste, und dies will ich irgendwie vermeiden, hab aber keine Ahnung wie?

Ich hoff ich drück mich klar genug aus... Ich will eben erreichen daß ich mich in der TGeometrien Klasse überhaupt nicht mehr um den wahren Typ von Items kümmern muß, sondern daß automatisch der richtige Typ genommen wird.

Gruß,
Tom

neolithos 24. Jun 2004 14:42

Re: Mal wieder Klassenvererbung
 
Schau die mal "class of" vielleicht hilft dir das weiter!

Delphi-Quellcode:
type
  TGeometryClass = class of TGeometry;

  TGeometryTyp = (tgKugel, tgQuader);

const
  cClass : array [TGeometryTyp] of TGeometryClass = (TKugel, TQuader);
Delphi-Quellcode:
fItem := cClass[ttKugel].Create;
Ich hoffe das hilft.

Stevie 24. Jun 2004 14:47

Re: Mal wieder Klassenvererbung
 
Eine kleine Abhandlung über virtuelle und abstrakte Methoden gibt's bei Delphi-Source.de!

Du musst also den Konstruktor von TGeometrie virtual abstract machen, dann klappt's folgendermaßen:
Delphi-Quellcode:
Fitems[i] := GeomTyp.Create;

Muetze1 24. Jun 2004 15:03

Re: Mal wieder Klassenvererbung
 
Moin!

Wenn man den Constructor virtuell abstrakt macht, dann weiss Delphi doch immernoch nicht für welche Klasse er sich entscheiden soll, weil das doch eigentlich von dem File abhängt aus dem geladen wird. Ich würde daher folgende Dinge vorschlagen:

1. Lege dir eine Metaklasse an für TGeometrie
2. Bau dir eine Funktion auf, die Klassen in einer Liste speichert. Übergeben wird eine Klasse die zu der Metaklasse passt (z.B. TQuader)
3. Speichere beim speichern den ClassName
4. beim Laden suche in der Liste von der Metaklassenliste nach einer Klasse mit dem Klassennamen und instanziiere diese über die MetaClass.

5. Nutze bei dir für deine TGeometries Klasse eine TObjectList als ein dynamisches Array, das macht die Verwaltung einfacher.

Beispiel zu dem Punkten 1. bis 4. kannst du dir z.B. in meiner XML Library auf meiner HP anschauen. Dort kann sich jeder einen eigenen Charset Encoding Filter schreiben und installieren. Dieses funktioniert genau nach dem Prinzip. Also schau dir mal wie ich die ISO8859-1 und UTF-8, etc registriere, verwalte und dann, wenn ich in der Datei ein charset encoding finde, instanziiere. (Die Registrierung wird bei den Charset Encodern im Initialize der Unit durchgeführt und damit die aufgerufen wird, muss die Unit einfach nur im Projektfile mit enthalten sein).

MfG
Muetze1

Stevie 24. Jun 2004 15:12

Re: Mal wieder Klassenvererbung
 
Zitat:

Zitat von Jelly
Delphi-Quellcode:
if GeomTyp is TKugel then Fitems[i] := TKugel.create;
if GeomTyp is TQuader then Fitems[i] := TQuader.create ;

Das heißt doch, dass GeomTyp bereits mit TKugel.Create oder TQuader.Create erstellt wurde, oooder?
Also kann man GeomTyp.Create aufrufen und erhält somit eine Instanz der richtige Klasse!? :roll:

Muetze1 24. Jun 2004 16:00

Re: Mal wieder Klassenvererbung
 
Moin!

Mal abgesehen davon das der Code so oder so sehr irreführend ist, schreibt er doch, wer die items alle in eine Textdatei schreiben und beim laden nicht immer eine If Zeile mehr einfügen für einen neuen Typ. So oder so muss er von TextInformationen aus der Datei wieder auf eine Klasse kommen bzw. eine Klasseninstanz erstellen. Somit hilft ihm der Code Schnipsel eher weniger - und vor allem weiss ich auch nicht wie/was er da macht. Zumindest hilft ihm ein virtueller abstrakter Constructor in dem Falle auch nicht soweit ich mir das so durchdenke.

Summa Sumarum: Ich bezog mich eher auf seine Beschreibung als auf seine Codeschnipsel.

MfG
Muetze1

Stevie 24. Jun 2004 16:14

Re: Mal wieder Klassenvererbung
 
So genehm? :drunken:
Delphi-Quellcode:
unit frVererbung;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TGeometrie = class(TPersistent)
    constructor Create; virtual; abstract;
  end;

  TKugel = class(TGeometrie)
    constructor Create; override;
  end;

  TQuader = class(TGeometrie)
    constructor Create; override;
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TQuader }

constructor TQuader.Create;
begin
  inherited;
  ShowMessage(ClassName);
end;

{ TKugel }

constructor TKugel.Create;
begin
  inherited;
  ShowMessage(ClassName);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  TGeometrie(FindClass('TKugel').Create).Create;
  TGeometrie(FindClass('TQuader').Create).Create;
end;

initialization
  RegisterClass(TKugel);
  RegisterClass(TQuader);

finalization
  UnRegisterClass(TKugel);
  UnRegisterClass(TQuader);

end.

Jelly 24. Jun 2004 16:23

Re: Mal wieder Klassenvererbung
 
Vielen Dank für die vielen Anregungen, womit ich schon mein beschränktes Wissen wesentlich auffrischen konnte. Es ist einiges Brauchbares dabei, aber wie Muetze1 bereits geschildert hab, muss ich immer irgendwie in meiner TGeometrien Klasse entscheiden, was passieren soll. Der Grund liegt darin, weil ich das Auslesen der Daten aus der Textdatei nicht in TKugel oder TQuader ablagern kann, da ich ja im Vorfeld die Klasse instanzieren muss... Ich weiss, mein Problem ist ziemlich verstrickt. Werd also nicht beim Definieren einer neuen Geometrieklasse drumrumkommen, auch in TGeometrien jeweils ne Zeile hinzuzufügen...

Aber nochmals danke... Eure Ansätze und Mühe sind Klasse.

Gruß,
Tom

Muetze1 24. Jun 2004 16:23

Re: Mal wieder Klassenvererbung
 
Moin!

Mir ist egal, ich mache nur Vorschläge, er muss es entscheiden.

Ich für meinen Teil würde dabei aber schon wieder sagen, das TPersistent für seinen Zweck zu viel/gross ist... Ein normales TObject Descandent hätte auch gereicht, dafür müsste man das mit der MetaClass selber machen und kann halt kein RegisterClass() und FindClass() nutzen. :roll:

MfG
Muetze1

Jens Schumann 24. Jun 2004 19:09

Re: Mal wieder Klassenvererbung
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

Zitat von Stevie
So genehm? :drunken:

Das überschreiben des constructors ist nicht nötig.
In der Anlage ist der Beweis.

Jelly 24. Jun 2004 19:39

Re: Mal wieder Klassenvererbung
 
Hallo,

das mit TPersistent als Vorfahre würde so funktionieren, allerdings will ich mein Objekt nicht dermaß aufbauschen, da ich bis zu mehreren hundert Instanzen erzeugen muss. Mit TPersistent würde das nur unnötig den Speicher vollschreiben.

Gruß,
Tom

Muetze1 24. Jun 2004 23:11

Re: Mal wieder Klassenvererbung
 
Moin!

Schonmal meinen Vorschlag überdacht bzw. den probiert?

MfG
Muetze1

Jelly 24. Jun 2004 23:48

Re: Mal wieder Klassenvererbung
 
Zitat:

Zitat von Muetze1
1. Lege dir eine Metaklasse an für TGeometrie

Hallo Muetzl1,

was genau verstehst du unter einer Metaklasse... So wie du es beschreibst soll ich über Classname auf die Klasse zugreifen... Ist es da aber nicht so, daß ich mindestens von TPersistent ableiten muß?

Gruß,
Tom

Muetze1 25. Jun 2004 02:21

Re: Mal wieder Klassenvererbung
 
Moin!

Delphi-Quellcode:
Unit Geometrie;

Interface

Type
  TGeometrieMetaClass = Class Of TGeometrie; // das ist die Metaklasse

  TGeometrie = Class
  Public
    Constructor Create; Virtual;
    Destructor Destroy; Override;

    Class Function ObjektID: String; Virtual; Abstract;
  End;
So, da hast du dann deine Basisklasse mit dem Constructor und einer Funktion die dir die ID für das Objekt gibt als String. Dies kann dann z.B. "Kreis" oder "Quadrat" sein. Diese Funktion kann als Class Funktion auch ohne eine Instanz aufgerufen werden. Darüber können wir dann später die richtige Klasse finden - wenn in deiner Textdatei denn drinne steht "Kreis" (z.B.).

Ok, nun brauchen wir nur noch eine Funktionalität, die die ganzen Typen annimmt und sie alle kennt:

Delphi-Quellcode:
Unit GeometrieRegSvr;

Interface

Uses
  Geometrie;

Function GetMetaClassForID(Const AID: String): TGeometrie;
Procedure RegisterGeometrieClass(AClass : TGeometrieMetaClass);

Implementation

Var
  GeometrieClasses : Array Of TGeometrieMetaClass;

Procedure RegisterGeometrieClass(AClass : TGeometrieMetaClass);
Var
  i : Integer;
Begin
    // already registered?
  For i := Low(GeometrieClasses ) To High(GeometrieClasses ) Do
    If ( GeometrieClasses [i] = AClass ) Then
      Exit;

  SetLength(GeometrieClasses , Length(GeometrieClasses )+1);
  GeometrieClasses [Length(GeometrieClasses )-1] := AClass ;
End;

Function GetMetaClassForID(Const AID: String): TGeometrie;
Var
  i         : Integer;
Begin
  For i := Low(GeometrieClasses) To High(GeometrieClasses) Do
    If ( UpperCase(GeometrieClasses[i].ObjektID) = AID) Then
    Begin
      Result := GeometrieClasses[i].Create;
      Exit;
    End;

  Raise Exception.Create('unbekannter Geometrietyp ' +  AID + '!');
End;
So, dann bekommst du mit GetMetaClassForID() schon die fertig angelegte Instanz der Klasse zurück die du suchst. Du musst nun nur noch 2 Dinge machen:

1. Alle deine TKreis, TQuadrat oder sonstige von TGeometrie ableiten und die ObjektID Procedure überschreiben und diese Kennung jeweils zurück geben. Diese Kennung ist gleich der die in die Datei geschrieben wird und gleich der die aus der Datei gelesen wird und die Klasse identifiziert.
2. In diesen Units die dann jeweils ein Objekt enthalten (also z.B: TKreis) musst du einmal diese Klasse hier registrieren. Dies machst du am besten im Initialize der Unit:

Delphi-Quellcode:
Initialization
    // registering the string adapter to the converter
  RegisterGeometrieClass(TKreis);

end.
Und dann klappt das auch schon.

MfG
Muetze1

SirThornberry 25. Jun 2004 09:14

Re: Mal wieder Klassenvererbung
 
Das ganze ist eigentlich ganz einfach. Du musst nur beim erstellen des Objectes prüfen von welchem Typ es ist und den entpsrechenden Konstructor des Nachfahren aufrufen. Anschließend kannst du über die gemeinsame Methode drauf zugreifen ohne dir gedanken drüber machen zu müssen ob es ein Quader oder eine Kugel ist.
Delphi-Quellcode:
type
  TGeometrie = class
  public
    function VolumenBerechnen: Extended; virtual; abstract;
  end;

  TKugel = class(TGeometrie)
  public
    function VolumenBerechnen: Extended; override;
  end;

  TQuader = class(TGeometrie)
  public
    function VolumenBerechnen: Extended; override;
  end;

[...]

function TKugel.VolumenBerechnen: Extended;
begin
  result := //Volumen für Kugel berechnen
end;

function TQuader.VolumenBerechnen: Extended;
begin
  result := //Volumen für Quader berechnen;
end;

///uns so geht man dann damit um
procedure Irgendwas;
var LObj: TGeometrie;
begin
  if ZeileAusDatei = Quader then //Hier entscheidet sich von welchem Typ das Object wird
     LObj := TQuader.Create
  else
     Lobj := TKugel.Create;

  //und dann einfach die gemeinsame Methode aufrufen
  showMessage(FloatToStr(LObj.VolumenBerechnen));
end;

Muetze1 25. Jun 2004 10:12

Re: Mal wieder Klassenvererbung
 
Moin!

Zitat:

Zitat von SirThornberry
Das ganze ist eigentlich ganz einfach. Du musst nur beim erstellen des Objectes prüfen von welchem Typ es ist und den entpsrechenden Konstructor des Nachfahren aufrufen. Anschließend kannst du über die gemeinsame Methode drauf zugreifen ohne dir gedanken drüber machen zu müssen ob es ein Quader oder eine Kugel ist.

Das habe ich mir auch so gedacht - aber bei meinem Code geht es doch um das automatische Create der richtigen Instanz abhängig von einem String und ohne Erweiterung des Codes um weitere IF Bedingungen für die unterschiedliche Typen. Das die grundlegenden Methoden immer nur überschrieben werden dachte ich eh - weil sonst nützt mein Code nix, wenn man nachher noch wieder IF Abfragen nehmen muss um mit dem Objekt zu arbeiten...

MfG
Muetze1

Jelly 25. Jun 2004 11:13

Re: Mal wieder Klassenvererbung
 
Hallo Muetzel1,

ich hab jetzt mal versucht deinen Code zu testen, und es klappt auch soweit.
Delphi-Quellcode:
var
 Geometrien : array of TGeometrie ;
...
  Setlength(Geometrien,2) ;
  Geometrien[0] := GetMetaClassForID ('QUADER').Create ;
  Geometrien[1] := GetMetaClassForID ('KUGEL').Create ;
probier ich dann meine Klassen zu erzeugen... In der GetMetaClassForID Funktion krieg ich dann aber einen abstrakten Fehler als Rückmeldung, wenn dort versucht wird auf GeometrieClasses[i].ObjektID zuzugreifen.

Ich hab womöglich noch einen Fehler drin, wahrscheinlich in der Überschreibung der ClassID Methoden... So siehts bei mir aus:
Delphi-Quellcode:
TKugel = class (TGeometrie)
  public
    class Function ObjectID : string ;
  end ;
...
class function TKugel.ObjectID: string;
begin
     Result := 'KUGEL' ;
end;
Oder ich hab alles durcheinander geworfen...

Aber Hut ab, wenn ich das am Laufen hab, ist es genau das was ich gesucht habe :firejump: Ech cool.

Gruß,
Tom

Muetze1 25. Jun 2004 11:20

Re: Mal wieder Klassenvererbung
 
Moin!

Jo, 2 Dinge noch dazu:

1.
Delphi-Quellcode:
  Geometrien[0] := GetMetaClassForID ('QUADER').Create ;
  Geometrien[1] := GetMetaClassForID ('KUGEL').Create ;
Da brauchst du kein Create mehr, die GetMetaClassForID() macht das schon intern und gibt dir schon das Ergebnis von dem Constructor-Aufruf zurück. Also du bekommst mit jedem Aufruf von GetMetaClassForID() eine ordentliche neue Instanz geliefert.

2. Ja, bei der abstrakten Methode ist das soweit ok, du musst nur noch oben in der Klasse ein Override hinten mit angeben:
Delphi-Quellcode:
TKugel = class (TGeometrie)
  public
    class Function ObjectID : string; Override;
  end ;
...
class function TKugel.ObjectID: string;
begin
     Result := 'KUGEL' ;
end;
Und dann sollte das klappen!

Ach ja: du gibst ja jetzt noch das "Kugel" und "Quadrat" direkt an - aber später liest du die Strings doch aus der Textdatei, oder? Weil so hatte ich es mir ja gedacht, dann brauchst du später auch nicht mehr angeben, das du jetzt eine Kugel brauchst sondern du liest einfach nur den Typ aus der Datei...
Ansonsten solltest du vielleicht noch eine Fehlerbehandlung mit einbauen, weil wenn du ihm einen unbekannten Typstring übergibst (also einen String wozu keine Klasse registriert ist), dann bekommst du eine Exception. Diese solltest du abfangen und dem entsprechend reagieren.
Und nochwas: wenn du in der GetMetaClassForID Funktion die AID mit einem UpperCase() umschliesst, dann kannst du auch ruhig "Kugel" oder so schreiben und musst somit nicht mehr auf Gross- und Kleinschreibung achten. Auch die Klassen können bei ObjektID dat ganze klein geschrieben - oder halt normal geschrieben zurück geben.

MfG
Muetze1

Jelly 25. Jun 2004 11:20

Re: Mal wieder Klassenvererbung
 
Ups, Nachtrag,

Hab mal wieder zu fix gehackt... Hatte in TGeometrie ObjektID definiert, und in TKreis, TQuader jedoch ObjectID... Also kein Wunder, daß da ein abstrakter Fehler kommt.

Jetzt klappts, helau...
Besten Dank für die Mühe.

Gruß,
Tom

Jelly 25. Jun 2004 11:44

Re: Mal wieder Klassenvererbung
 
Zitat:

Zitat von Muetze1
Ach ja: du gibst ja jetzt noch das "Kugel" und "Quadrat" direkt an - aber später liest du die Strings doch aus der Textdatei, oder? Weil so hatte ich es mir ja gedacht, dann brauchst du später auch nicht mehr angeben, das du jetzt eine Kugel brauchst sondern du liest einfach nur den Typ aus der Datei...

Das ist mir klar... Ich habs aber einfach nur mal so getestet. Nachher steht da natürlich das was ich aus der Textdatei les. Die Fehlerbehandlung ist schon soweit eingebaut.

Gruß,
Tom


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