Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi [OOP]: sinnvolle Vererbung (https://www.delphipraxis.net/29054-%5Boop%5D-sinnvolle-vererbung.html)

mytar 3. Sep 2004 10:49


[OOP]: sinnvolle Vererbung
 
Schon seit längerer Zeit versuche ich mich in die OOP einzuarbeiten.

Nun hab ich aber oft Probleme mit der Vererbung. Ich kenne die meisten Compilerdirektiven (virtual, override usw.), hab aber trotzdem Probleme.

Neuerdings hab ich mir eine Liste zusammengeproggt, und das meiner Klasse TMyList implementiert.
Dort brauche ich natürtlich auch eine Wurzel:

Delphi-Quellcode:
TMyList = class
private
 Wurzel: TKnoten;
public
 procedure Hinzufuegen;
 {...}
end;
Nun hab ich aber eine Weiterentwicklung von TKnoten gemacht, natürlich durch Vererbung.
Diese Weiterentwicklung von TKnoten möchte ich nun in eine weitere Listenklasse implementieren.

Delphi-Quellcode:
TMyNewList = class(TMyList)
private
 Wurzel: TNewKnoten;
public
 {...}
end;
Ihr sehr jetzt mein Problem mit der Wurzel, und ich habe ja auch Hinzufuegen von TMyList geerbt.
Leider arbeitet die Prozedur Hinzufügen mit TKnoten. Muss ich jetzt die Methode Hinzufügen in TMyNewList neu implentieren, oder kann ich mit inherited auf Hinzufügen zugreifen, allerdings mit Instanzen von TNewKnoten?

Stevie 3. Sep 2004 11:16

Re: [OOP]: sinnvolle Vererbung
 
Da TMyList nur TKnoten kennt, musst du der Prozedur eine "runtergecastete" Instanz von TMyKnoten übergeben -->
Delphi-Quellcode:
TMyList = class
private
Wurzel: TKnoten;
public
procedure Hinzufuegen(var Neu: TKnoten);
{...}
end;

TMyNewList = class(TMyList)
private
Wurzel: TNewKnoten;
public
{...}
end;

...

//Aufruf
var
  Liste: TMyNewList;
  NeuerKnoten: TNewKnoten;
begin
  ...
  NeuerKnoten := TNewKnoten.Create;
  Liste.Hinzufügen(NeuerKnoten); //von Typ TNewKnoten, der von TKnoten abstammt
  ...
Wenn du auf irgendwas zugreifen musst, was in TNewKnoten enthalten ist, dann musst du die Proezdur neu implementieren, vielleicht geht es sogar so:
Delphi-Quellcode:
procedure TMyNewList.Hinzufuegen((var Neu: TNewKnoten);
begin
  inherited Hinzufuegen; // weiß nicht genau, vielleicht auch so: inherited Hinzufuegen(Neu);
  //Mach irgendwas mit Neu als TNewKnoten
end;

mytar 3. Sep 2004 11:19

Re: [OOP]: sinnvolle Vererbung
 
Muss ich Wurzel: TNewKnoten üverhaupt in TMyNewList neu implementieren, oder kann ich Wurzel von TMyList irgendwie manipulieren?

Bowler 3. Sep 2004 11:25

Re: [OOP]: sinnvolle Vererbung
 
Müsstest Du können. Allerdings hast du Wurzel:TKnoten als private deklariert, deswegen ist das nur in dieser Klasse sichtbar. Damit die Ableitungen (TMyNewList) auf das Wurzel-Attribut aus der Mutterklasse zugreifen kann, musst du es als protected oder public deklarieren:

Delphi-Quellcode:
TMyList = class
protected
Wurzel: TKnoten;
public
procedure Hinzufuegen(var Neu: TKnoten);
{...}
end;
nun ist Wurzel vom Typ TKnoten auch in den abgeleiteten Klassen sichtbar.

Stevie 3. Sep 2004 11:34

Re: [OOP]: sinnvolle Vererbung
 
Zitat:

Zitat von Bowler
nun ist Wurzel vom Typ TKnoten auch in den abgeleiteten Klassen sichtbar.

Ja, aber immer nur noch als TKnoten-Objekt!!

Zitat:

Zitat von mytar
Muss ich Wurzel: TNewKnoten üverhaupt in TMyNewList neu implementieren, oder kann ich Wurzel von TMyList irgendwie manipulieren?

Du musst folgendes bedenken, wenn eine Methode aus TMyList aufgerufen wird und auf Wurzel zugegriffen wird, dann ist das ein TKnoten-Objekt, ist die Methode aber in der TMyNewList, dann ist Wurzel ein TNewKnoten-Objekt, beides sind unterschiedliche und unabhängige Objekte. Du könntest natürlich versuchen, das neue Wurzel-Objekt über eine Property auf das alte zeigen zu lassen (vorrausgesetzt, du beachtest den Rat von Bowler):
Delphi-Quellcode:
function TMyNewList.GetWurzel: TNewKnoten;
begin
  Result := Wurzel;
  // Eigenschaften kopieren...
  Result.Irgendwas := TMyList(Self).Wurzel.Irgendwas;
  // vielleicht geht sogar Result.Irgendwas := inherited Wurzel.Irgendwas oder so ähnlich??? 
end;

mytar 3. Sep 2004 11:58

Re: [OOP]: sinnvolle Vererbung
 
Zitat:

Zitat von Stevie
Da TMyList nur TKnoten kennt, musst du der Prozedur eine "runtergecastete" Instanz von TMyKnoten übergeben

Wie meinst du das mit runtercasten!

Was ist der Unterschied zwischen
Delphi-Quellcode:
TMyNewList(MyList)
und
Delphi-Quellcode:
MyList as TMyNewList
?

Stevie 3. Sep 2004 12:06

Re: [OOP]: sinnvolle Vererbung
 
Zitat:

Zitat von mytar
Zitat:

Zitat von Stevie
Da TMyList nur TKnoten kennt, musst du der Prozedur eine "runtergecastete" Instanz von TMyKnoten übergeben

Wie meinst du das mit runtercasten!

Was ist der Unterschied zwischen
Delphi-Quellcode:
TMyNewList(MyList)
und
Delphi-Quellcode:
MyList as TMyNewList
?

Mit runtercasten meinte ich auf den Vorfahren casten. :)

Zitat:

Zitat von Die OH
Der Operator as führt eine Typumwandlung mit Laufzeitprüfung durch.

Wenn du wie im ersten Beispiel castest, dann gibt's ne Zugriffsverletztung, wenn die Klassen nicht stimmen, aber bei unserem Beispiel braucht ja nix überprüft werden, denn wir wissen ja, was für Klassen wir haben.

Phoenix 3. Sep 2004 12:20

Re: [OOP]: sinnvolle Vererbung
 
Schau Dir dazu doch auch mal in der Unit Classes.pas der VCL (source liegt Deiner Enterprise ja bei) die beiden Klassen TCollection und TCollectionItem an.

Dort ist das so implemetiert, das Du beim erzeugen der (abgeleiteten) Collection/Liste den Typ (also die Klasse) der Items mitgibst. Diese werden dann durch eine Classfactory als entsprechende Objekte hinzugefügrt / created.

Bowler 3. Sep 2004 12:40

Re: [OOP]: sinnvolle Vererbung
 
Zitat:

Zitat von mytar
Muss ich Wurzel: TNewKnoten üverhaupt in TMyNewList neu implementieren, oder kann ich Wurzel von TMyList irgendwie manipulieren?

Ja, daraus habe ich gelesen, dass er sich das ableiten der Klasse TNewKnoten sparen will, und direkt auf der TKnoten-Klasse arbeiten will.

Wobei es mittels Polymorphie auch funktionieren müsste, wenn TNewKnoten von TKnoten abgeleitet wird. Dann müsste er in der TMyNewList ein Objekt von TNewKnoten in dem Wurzel-Attribut von TKnoten instanzieren können.

mytar 3. Sep 2004 12:43

Re: [OOP]: sinnvolle Vererbung
 
@Bowler: Wie bitte? Könntest du das etwas einfacher erklären?

Danke!

P.S.: Bitte nicht im Konjunktiv schreiben! :-D

Stevie 3. Sep 2004 12:45

Re: [OOP]: sinnvolle Vererbung
 
Zitat:

Zitat von mytar
@Bowler: Wie bitte? Könntest du das etwas einfacher erklären?

:gruebel: *sichanschließ*

Bowler 3. Sep 2004 13:01

Re: [OOP]: sinnvolle Vererbung
 
Hm..ok, ich versuchs.

Also, Polymorphie bedeutet, dass Du ein Attribut mit dem Typ Deiner Basis-Klasse anlegst, und dann - wenn Du das Objekt letztendlich mit .Create erzeugst - nichtmehr die Basis-Klasse erstellst, sondern eine davon abgeleitete Klasse.

Hier das ganze mal als Code:

Delphi-Quellcode:
type
  TBasisKlasse = class(TObject)
  public
     iErgebnis:integer;
     procedure Berechne(a,b:integer);
  end;

  TAddierenKlasse = class(TBasisKlasse)
  public
    procedure Berechne(a,b:integer); override;
  end;

  TSubtrahierenKlasse = class(TBasisKlasse)
  public
    procedure Berechne(a,b:integer); override;
  end;
Dann die Implementierung:

Delphi-Quellcode:
procedure TBasisKlasse.Berechne(a,b:integer);
begin
  // Dummy-Funktion
end;

procedure TAddierenKlasse .Berechne(a,b:integer);
begin
  self.Ergebnis=a+b;
end;

procedure TSubtrahierenKlasse .Berechne(a,b:integer);
begin
  self.Ergebnis=a-b;
end;
Dadurch hast Du eine Basisklasse, und 2 davon abgeleitete Klassen, mit der gleichen Funktion. Beide funktionen machen etwas unterschiedliches. In der Funktion der Basisklasse steht nichts, da es sich um eine leere Dummy-Funktion handelt.

Im Deinem Programm dann:

Delphi-Quellcode:
var
  meinMatheObejkt:TBasisKlasse;

function Addieren;
begin
  meinMatheObjekt:=TAddierenKlasse.Create;
  meinMatheObjekt.Berechne(10,5);
  ShowMessage(IntToStr(meinMatheObjekt.Ergebnis)); // = 15
  FreeAndNil(meinMatheObjekt);
end;

function Subtrahieren;
begin
  meinMatheObjekt:=TSubtrahierenKlasse.Create;
  meinMatheObjekt.Berechne(10,5);
  ShowMessage(IntToStr(meinMatheObjekt.Ergebnis)); // = 5
  FreeAndNil(meinMatheObjekt);
end;
Das mal zum Thema Polymorphie (ich hoffe, es ist soweit richtig @all :) ).

Das würde in Deinem Konkreten Fall bedeuten, dass Du in Deiner TMyNewList-Klasse kein Attribut für einen Knoten einbinden musst, sondern das Attribut aus der Mutterklasse lassen kannst (TKnoten). Wenn Du nun die TKnoten-Klasse erweitern willst, dass z.B. im Kontruktor noch etwas mehr passiert, als in TKnoten, dann leitest Du dir von TKnoten eine neue Klasse ab (TNewKnoten).
In den Methoden Deiner TMyNewListe kannst Du dann in Dein Wurzel-Attribut, welches Du aus der Mutterklasse hast und von Typ TKnoten ist, ein Objekt vom Typ TNewKnoten erzeugen.

D.h, Du kannst zu Laufzeit entscheiden, welches Objekt letzten Endes in dem Attribut vorhanden ist.


Hui, ganz schön viel stoff. Ich hoffe, es hilft dir. :-D

Gruß
Christian

Stevie 3. Sep 2004 13:13

Re: [OOP]: sinnvolle Vererbung
 
Zitat:

Zitat von Bowler
In den Methoden Deiner TMyNewListe kannst Du dann in Dein Wurzel-Attribut, welches Du aus der Mutterklasse hast und von Typ TKnoten ist, ein Objekt vom Typ TNewKnoten erzeugen.

WAAAS??? Das Wurzel-Objekt ist doch bereits erstellt und du kannst es doch nicht einfach überschreiben!!! Was passiert denn dann, wenn die Mutterklasse auf das Teil zugreift? Deshalb hab ich doch gesagt, man muss die alten Werte erst retten!

P.S.: Ein virtual abstract in der abstrakten Klasse erspart dir das schreiben einer Dummy-Methode! ;-)

maximov 3. Sep 2004 13:14

Re: [OOP]: sinnvolle Vererbung
 
Zitat:

Zitat von Bowler
Hm..ok, ich versuchs.
...
Delphi-Quellcode:
type
  TBasisKlasse = class(TObject)
  public
     iErgebnis:integer;
     procedure Berechne(a,b:integer);
  end;

 ...

Da fehlt wohl ein virtual. Und da diese klasse ja eh nix kann, macht es sinn wenn man diese als auch abstract deklariert. Dh. in der basis klasse sind nur (oder fast nur) methoden ohne implementierung deklariert - was dazu führt das die ableitungen die funktionalität, mittels override, zwingend implementieren müssen. Dann kann man immer von fähigkeiten der basis-klasse ausgehen - diese spezielle implementierung kann in den unterklassen stark abweichen :-D

Delphi-Quellcode:
procedure Berechne(a,b:integer); virtual; abstract;
:-D

Stevie 3. Sep 2004 13:16

Re: [OOP]: sinnvolle Vererbung
 
Zitat:

Zitat von maximov
Delphi-Quellcode:
procedure Berechne(a,b:integer); virtual; abstract;

Zitat:

Zitat von Stevie
P.S.: Ein virtual abstract in der abstrakten Klasse erspart dir das schreiben einer Dummy-Methode! ;-)

:mrgreen:

Bowler 3. Sep 2004 13:20

Re: [OOP]: sinnvolle Vererbung
 
Sorry, wusste nicht, dass das Wurzel-Objekt schon erstellt ist.

Phoenix 3. Sep 2004 13:31

Re: [OOP]: sinnvolle Vererbung
 
Zitat:

Zitat von Stevie
WAAAS??? Das Wurzel-Objekt ist doch bereits erstellt und du kannst es doch nicht einfach überschreiben!!! Was passiert denn dann, wenn die Mutterklasse auf das Teil zugreift? Deshalb hab ich doch gesagt, man muss die alten Werte erst retten!

Erm. Nee, das wäre ein Designfehler.

Das Wurzelobjekt wird doch bereits als abgeleitete Klasse erzeugt, damit kann die Mutter demnach auf alle Basismethoden wie gehabt darauf zugreifen.

Beispiel:
Delphi-Quellcode:
TListe = class
   wurzel: TItem;
   wurzelklasse: TItemClass;
   function Hinzufuegen: TItem;
   constructor Create(itemklasse: TItemClass);
end;

TItem = class
end;

TItemClass = Class of TItem;

TMyItem = class
end;

TMyItemClass = class of TMyItem;

constructor TListe.Create(itemklasse: TItemClass);
begin
   wurzelklasse := itemklasse;
end;

function TListe.Hinzufuegen: TItem;
begin
   result := wurzelklasse.Create;
end;


Nun kann man mit
Delphi-Quellcode:
var
   l_liste: TLIste;
   l_item: TItem;
   l_myItem: TMyItem;
begin
   l_liste := TListe.Create(TItemClass);
   l_item := l_liste.Hinzufuegen;

   l_liste.Free;
   l_liste := TLIste.Create(TMyItemClass);
   l_myItem := l_liste.Hinzufzuegen as TMyItem;
end;
alle beliebigen abgeleiteten Items direkt durch die Basisliste instanzieren lassen.

Stevie 3. Sep 2004 13:39

Re: [OOP]: sinnvolle Vererbung
 
Ja, sicher, ich bin aber davon ausgegangen, dass er von TMyList ableiten möchte ohne dass er dort irgendwas ändert und trotzdem auch das Wurzel-Objekt eine vom alten Wurzel-Objekt abgeleitete Klasse hat!

maximov 3. Sep 2004 15:11

Re: [OOP]: sinnvolle Vererbung
 
Zitat:

Zitat von Stevie
Zitat:

Zitat von maximov
Delphi-Quellcode:
procedure Berechne(a,b:integer); virtual; abstract;

Zitat:

Zitat von Stevie
P.S.: Ein virtual abstract in der abstrakten Klasse erspart dir das schreiben einer Dummy-Methode! ;-)

:mrgreen:

Ja toll :? Du warst schnell - du bist der sieger :stupid:

// edit: OK. Ich verzeihe dir :mrgreen:

Stevie 3. Sep 2004 15:16

Re: [OOP]: sinnvolle Vererbung
 
Zitat:

Zitat von maximov
Ja toll :? Du warst schnell - du bist der sieger :stupid:

Nein, so war das nicht gemeint!!! :oops: Ich fand's nur witzig, weil mir das noch nachträglich eingefallen ist und ich dann den Beitrag nochmal editiert hab. Und in diesem Moment kam dein Post. :wink:

mytar 4. Sep 2004 11:07

Re: [OOP]: sinnvolle Vererbung
 
Vielen Danke für die Posts! :-D

Bedanke mich bei (alphabetisch):
  • Bowler
  • maximov
  • Phoenix
  • Stevie


Zitat:

Zitat von Stevie
Ja, sicher, ich bin aber davon ausgegangen, dass er von TMyList ableiten möchte ohne dass er dort irgendwas ändert und trotzdem auch das Wurzel-Objekt eine vom alten Wurzel-Objekt abgeleitete Klasse hat!

Bitte helft mir nochmal! :-D

Danke

Stevie 6. Sep 2004 07:27

Re: [OOP]: sinnvolle Vererbung
 
Zitat:

Zitat von mytar
Zitat:

Zitat von Stevie
Ja, sicher, ich bin aber davon ausgegangen, dass er von TMyList ableiten möchte ohne dass er dort irgendwas ändert und trotzdem auch das Wurzel-Objekt eine vom alten Wurzel-Objekt abgeleitete Klasse hat!

Bitte helft mir nochmal! :-D

Wenn du das auf meine Aussage beziehst - ich meinte damit, bzw ich ging von Folgendem aus:
Du hast die Klasse TMyList mit Wurzel als TKnoten vorgegeben und kannst oder darfst sie nicht mehr im Design verändern.
Du möchtest davon eine Klasse ableiten, die aber Knoten als TNewKnoten implementiert.

mytar 6. Sep 2004 08:40

Re: [OOP]: sinnvolle Vererbung
 
Zitat:

Zitat von Stevie
Wenn du das auf meine Aussage beziehst - ich meinte damit, bzw ich ging von Folgendem aus:
Du hast die Klasse TMyList mit Wurzel als TKnoten vorgegeben und kannst oder darfst sie nicht mehr im Design verändern.
Du möchtest davon eine Klasse ableiten, die aber Knoten als TNewKnoten implementiert.

Genau, allerdings hab ich dann nocht TMyNewList. Jezt möchte ich die implementierten Methoden von der Mutterklasse TMyList verwenden, diese sind aber alle mit TKnoten implementiert, ich möchte nun eben die Methoen der Mutterklasse vont TMyList auf TNewKnoten anwenden.

Ich hoffe es sich verständlich. :?

Stevie 6. Sep 2004 09:32

Re: [OOP]: sinnvolle Vererbung
 
Ich hab dir mal ein kleines Beispiel zusammengebastelt:
Delphi-Quellcode:
{...}

type
  TKnoten = class
    FZahl: Integer;
  public
    property Zahl: Integer read FZahl write FZahl;
  end;

  TMyList = class
    FWurzel: TKnoten;
  public
    constructor Create;
    destructor Destroy; override;
    function Verdopple: Integer;
    property Wurzel: TKnoten read FWurzel write FWurzel;
  end;

  // WICHTIG: Jeder Zugriff auf ererbte Eigenschaften muss über das LinkedObject laufen
  TNewKnoten = class(TKnoten)
    FText: string;
    FLinkedObject: TKnoten;
  private
    function GetZahl: Integer;
    procedure SetZahl(const Value: Integer);
  public
    constructor Create(LinkedObject: TKnoten = nil);
    destructor Destroy; override;
    property Text: string read FText write FText;
    property Zahl: Integer read GetZahl write SetZahl;
  end;

  TMyNewList = class(TMyList)
    FWurzel: TNewKnoten;
  public
    constructor Create;
    destructor Destroy; override;
    property Wurzel: TNewKnoten read FWurzel write FWurzel;
  end;

{...}

implementation

{ TMyList }

constructor TMyList.Create;
begin
  FWurzel := TKnoten.Create;
end;

destructor TMyList.Destroy;
begin
  FreeAndNil(FWurzel);
  inherited;
end;

function TMyList.Verdopple: Integer;
begin
  Result := FWurzel.Zahl * 2;
end;

{ TNewKnoten }

constructor TNewKnoten.Create(LinkedObject: TKnoten = nil);
begin
  FLinkedObject := LinkedObject;
end;

destructor TNewKnoten.Destroy;
begin
  FLinkedObject := nil;
end;

function TNewKnoten.GetZahl: Integer;
begin
  if Assigned(FLinkedObject) then
    Result := FLinkedObject.Zahl
  else
    Result := FZahl;
end;

procedure TNewKnoten.SetZahl(const Value: Integer);
begin
  if Assigned(FLinkedObject) then
    FLinkedObject.Zahl := Value
  else
    FZahl := Value;
end;

{ TMyNewList }

constructor TMyNewList.Create;
begin
  inherited;
  FWurzel := TNewKnoten.Create(TMyList(Self).Wurzel);
end;

destructor TMyNewList.Destroy;
begin
  FreeAndNil(FWurzel);
  inherited;
end;

{...}
Somit hast du dann eigentlich zwei Objekte (beide jeweils private und somit gekapselt und nur über properties ansprechbar!), wobei das zweite die ererbten Methoden, Eigenschaften von dem gelinkten Objekt verwendet. Sommit kannst du Wurzel in TMyNewList verändern und dann eine aus TMyList ererbte Funktion aufrufen, die irgendwas mit Wurzel macht. Anschließend kannst du die veränderten Werte von Wurzel in TMyNewList wieder abrufen.

mytar 6. Sep 2004 09:56

Re: [OOP]: sinnvolle Vererbung
 
Danke, Stevie! :-D

Phoenix hat mir folgende PN geschickt:
Zitat:

Zitat von Phoenix
Binde mal die Unit Classes in eine Deiner units ein, setze den Cursor in die Unit und drücke Strg + Enter. Damit macht Delphi dann den Sourcecode der VCL auf.

Dort Such mal nach den beiden Klassen TCollection und TCollectionItem.

Schau Dir mal die Methoden TCollection.Create und TCollection.Add an.

Dort wird nämlich eigentlich genau das gemacht, was Du willst. Bessere Beispiele kannst Du eigentlich nicht finden.

Das werd ich auch mal versuchen, Danke! :lol:

Stevie 6. Sep 2004 10:12

Re: [OOP]: sinnvolle Vererbung
 
Klar, wenn man beide Klassen neu erstellt, ist das der beste Weg.
Allerdings geht das eben nicht, wenn man eine schon bestehende Klasse hat, die man erweitern will.

mytar 8. Sep 2004 10:52

Re: [OOP]: sinnvolle Vererbung
 
Ich danke allen Postern, für die vielen Antworten. Danke :-D

In meinem Falls werde ich Klassenreferenztypen (class of) verwenden, aber auch der Vorschlag von Stevie werd ich mir merken.

TCollection und TCollectionItem sind ideale Beispiele für Klassenreferenztypen (Phoenix).

Hier noch ein alter Thread, bezüglich "class of":
Was bedeutet "class of"

Der Thread ist [erledigt], Danke :-D


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