AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen Object-Pascal / Delphi-Language Delphi Methodenhierarchie in der Vererbung umgehen/überspringen
Thema durchsuchen
Ansicht
Themen-Optionen

Methodenhierarchie in der Vererbung umgehen/überspringen

Ein Thema von scouty · begonnen am 10. Jan 2004 · letzter Beitrag vom 13. Jan 2004
Antwort Antwort
scouty

Registriert seit: 27. Nov 2003
Ort: Dresden
47 Beiträge
 
#1

Methodenhierarchie in der Vererbung umgehen/überspringen

  Alt 10. Jan 2004, 20:29
Frage an das Form zur Vererbung, die mich schon lange rum treibt, da ich immer wieder darüber beim Programmieren gestolpert bin. Wenn z.b. Die Klasse C von B aberbt, welche von A eine Erweiterung ist (A<B<C oder A = Großvater, B = Vater, C=Sohn). Wie kann ich dann in C eine Methode von A aufrufen und praktisch die Methode in B überspringen. Der Aufruf „inherited Methode()“ ruft ja immer nur die jeweils höhere Methode (in diesem Fall in B) auf. Ich bräuchte eine Möglichkeit z.B. der Form: „inherited.inherited Methode()“, um mit dem Aufruf direkt auf die Methode des Großvater zugreifen und somit die Methode des Vater zu umgehen. Hat hier jemand eine Idee???

Greetings Scouty
  Mit Zitat antworten Zitat
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#2

Re: Methodenhierarchie in der Vererbung umgehen/überspringen

  Alt 10. Jan 2004, 20:42
Das ist in Delphi eigentlich nicht vorgesehen, man kann aber tricksen sodas dies trotzdem geht.

Delphi-Quellcode:

type
  TA = class
    procedure CommonMethod(Dummy: Integer);

    procedure Method(Dummy: Integer); virtual;
  end;

  TB = class(TA)
    procedure Method(Dummy: Integer); override;
  end;

  TC = class(TB)
    procedure Method(Dummy: Integer); override;
  end;

procedure TA.CommonMethod(Dummy: Integer);
begin
  ShowMessage('TA');
end;

procedure TA.Method(Dummy: Integer);
begin
  CommonMethod(Dummy);
end;

procedure TB.Method(Dummy: Integer);
begin
  inherited;
  ShowMessage('TB');
end;

procedure TC.Method(Dummy: Integer);
var
  N: procedure(Dummy: Integer); of object;
begin
  inherited;
  ShowMessage('TC');

// nun rufen wir inherited TA.Method(Dummy) auf
  TMethod(N).Data := Self;
  TMethod(N).Code := @TA.Method;
  
  N(Dummy);

// nun so wie es auf herkömmliche OOP Weise funktioniert
  CommonMethod(Dummy);
end;
Gruß Hagen
  Mit Zitat antworten Zitat
scouty

Registriert seit: 27. Nov 2003
Ort: Dresden
47 Beiträge
 
#3

Re: Methodenhierarchie in der Vererbung umgehen/überspringen

  Alt 12. Jan 2004, 12:37
Also, alle Achtung die Lösung gefällt mir. Ok, ich halte Sie zwar schon für etwas umständlich – ehrlich gesagt, musste ich sie mir mehrmals durchlesen, um sie mit der Delphi-Hilfe zu dechiffrieren. Ich habe diese Funktionalität bisher noch nie verwendet, ich wusste noch gar nicht, dass es sie überhaupt gibt. Aber als ich mir das so angeschaut hatte, kam mir noch eine andere Idee. Ich habe gelernt, dass wenn ich über den Konstruktor ein Objekt erzeuge, dass dann automatisch auch die Vorläufer-Objekte erzeugt werden müssen/sollten. Deshalb sollte ja auch der Aufruf inherited immer zuerst im Methodenteil eines Konstruktors stehen. Folglich ist das Objekt des Vorläufers vorhanden in unserm Falle, wird zuerst TA erzeugt dann TB und so weiter. Theoretisch könnte ich ja auch folgendes in die Methode „Method“ von TC schreiben:

Code:
procedure TC.Method(Dummy: Integer);
begin
 TA(self).Method(Dummy);
end;
Das heißt ich sage einfach, dass ich TC als TA verwenden will. Nach meiner Logik müsste jetzt auch die Methode von TA aufgerufen werden. Natürlich habe ich das ausprobiert und es hat natürlich nicht bei mir funktioniert. Aber vielleicht kannst Du / Ihr mir sagen, wo hier mein Denkfehler liegt. Also, mir erscheint dieser Ansatz zumindest auch als eine Lösung dieses Problem. Bisher leider jedoch nur theoretisch ;o(

Noch eine Anmerkung: Er ist bei mir trotz Typumwandlung immer in die Methode von TC rein gegangen. Ich hatte quasi eine Endlosschleife programmiert, so dass ich Delphi abschießen musst, um diese zu beenden.
  Mit Zitat antworten Zitat
Benutzerbild von Phoenix
Phoenix
(Moderator)

Registriert seit: 25. Jun 2002
Ort: Hausach
7.606 Beiträge
 
#4

Re: Methodenhierarchie in der Vererbung umgehen/überspringen

  Alt 12. Jan 2004, 13:02
Das liegt daran, das Dein Objekt nunmal ein TC ist, und die Methode überschrieben wurde.

Das hat zur Folge, daß an der Speicheradresse der Methode Method() nunmal der Codeblock von TC liegt, bzw. ein Pointer dahin. Wenn Du nun einen Typecast auf TC als TA machst, geht der Compiler hin, und guckt nach, an welcher Speicheradresse von dem Objekt nun der Pointer zur Methode Method() liegt. Die Position der Adresse ist bei TA und TC identisch, und durch den TypeCast wird der WERT des Pointers eben nicht verändert: Er zeigt weiterhin auf die implementierung von TC.

Deswegen ist der Umweg den Hagen aufgezeigt hat leider unumgänglich. Du musst über @TA.Method den Codeblock der Implementierung von TA herausfinden.

Zur Verdeutlichung vergleiche mal folgende Werte:
@TC.Method;
@(TA(self)).Method;
@TA.Method;
Sebastian Gingter
Phoenix - 不死鳥, Microsoft MVP, Rettungshundeführer
Über mich: Sebastian Gingter @ Thinktecture Mein Blog: https://gingter.org
  Mit Zitat antworten Zitat
scouty

Registriert seit: 27. Nov 2003
Ort: Dresden
47 Beiträge
 
#5

Re: Methodenhierarchie in der Vererbung umgehen/überspringen

  Alt 12. Jan 2004, 13:20
Das leuchtet mir ein. Danke für den Tipp. Jetzt habe ich ehrlich gesagt, das erste Mal so richtig verstanden, was die Begriffe „virtuel“ und „override“ so richtig bedeuten. Bisher habe ich diese Befehle immer mehr so nebenbei benutzt. Da ich von Java komme, konnte ich mit diesen Begriffen richt so viel anfangen. Ich geh’ mal davon aus, dass wenn ich die Methode „Method“ nicht in TA virtuel deklarieren bzw. anstelle von „override“ „reintroduce“ verwenden würde, dass ich dann noch rankommen müsste, oder?!?


Greetings Scouty…
  Mit Zitat antworten Zitat
choose

Registriert seit: 2. Nov 2003
Ort: Bei Kiel, SH
729 Beiträge
 
Delphi 2006 Architect
 
#6

Re: Methodenhierarchie in der Vererbung umgehen/überspringen

  Alt 12. Jan 2004, 13:35
Hallo scouty,

schön, dass Du durch diesen Thread etwas mehr über die interne Arbeitsweise der bindung von virtuellen Methoden in Delphi lernen konntest. Aus rein designtechnischer Sicht würde mich aber trotzdem interessieren, ob sich Dein angestrebtest Ziel nicht auch ohne die "dreckigen Tricks", zB unter verwendung von "echten" OOP-Mustern, lösen lässt (hier zB mit dem Bei Google suchendecorator pattern, dem Bei Google suchenstrategy pattern oder dem Bei Google suchenstate pattern).

Könntest Du einmal Dein konkretes Problem skizzieren?
gruß, choose
  Mit Zitat antworten Zitat
scouty

Registriert seit: 27. Nov 2003
Ort: Dresden
47 Beiträge
 
#7

Re: Methodenhierarchie in der Vererbung umgehen/überspringen

  Alt 12. Jan 2004, 14:53
Ich fürchte, wir reden hier etwas aneinander vorbei. Also, kann zwar leidlich programmieren, aber ich bin kein richtiger Programmierer oder Informatiker. Meine Kenntnisse über OOP gehen nicht sehr tief. Daher sagten mir die von Dir genannten Muster erstmal gar nix. Ich habe mich jedoch gerade informiert und werde mich demnächst mit diesen Muster beschäftigen. Vielen Dank erstmal für die Anregung.

Nun zu meinem konkretem Problem. Ich geh’, mal davon aus, da zum einem Delphi keinen Hierarchiesprung vorsieht und aufgrund der Reaktion zu meinem Thread, dass solcher Art Hierarchiesprünge (das Umgehen einer Methode in der Vaterklasse, indem man die Methode direkt beim Großvater aufruft) in OOP generell nicht vorgesehen/erwünscht sind. Das heißt, dass man die Objektstruktur von vornherein so entwerfen sollte, dass solche Probleme gar nicht auftreten. Bei mir ist dieses Problem doch schon des Öfteren aufgetreten, ich habe es dann jedes Mal mehr elegant oder weniger elegant lösen können.

Die aktuelle Version ist folgende:

Ich habe ein Formular TDBEdtFrm, welches von TBscFrm erbt und auf TDBEdtFrmChild erweitert wird (TBscFrm < TDBEdtFrm < TDBEdtFrmChild).

TBscFrm enthält die Grundformen einer Hauptformularsteuerung wie Anwendung schließen, Neue Anwendung usw. Wird hier die Methode Close() aktiviert, so führt diese nur grundlegende Operationen aus, die bei allen Anwendungen gleich sind: Wie z.B. Abfrage, ob wirklich geschlossen werden soll und die visuellen Änderungen am Hauptformular.

TDBEdtFrm ist ein Formular, welches zum Bearbeiten einer DB-Datenbank eingesetzt werden kann. Wenn hier die Methode Close() aufgerufen wird, dann schließt diese u.a. alle offenen Datenressourcen und ruft dann Close() bei der Superklasse auf.

TDBEdtFrmChild erweitert TDBEdtFrm, damit diese als MDIChild in einer MDI-Anwendung eingesetzt werden kann. Wird hier jedoch die Methode Close() aufgerufen, so darf diese nicht die Ressourcen schließen, da diese ja noch von anderen MDIChilds der Anwendung verwendet werden. Folglich darf die Methode Close() von TDBEdtFrm nicht aufgerufen werden. Mir wäre es daher am liebsten gewesen, hier meine eigene Methode Close() zu schreiben, die hier ihre Routine für das Formular TDBEdtFrmChild ausführt und dann den Rest wieder an die übergeordnete Methode von TBscFrm übergibt, dabei aber Close() in TDBEdtFrm übergeht.


Ich hoffe, mir ist es gelungen, hier mein kleines Problem kurz aber auch verständlich darzustellen. Also, ich habe es zumindest verstanden, sogar besser als zuvor; denn als ich das hier schrieb, kam mir die Idee einfach die Vererbungshierarchie zu ändern (TBscFrm < TDBEdtFrmChild < TDBEdtFrm), das könnte zumindest auf den ersten Blick dieses Problem lösen oder besser gesagt vermeiden. Während TDBEdtFrmChild lediglich als MDIChild verwendet werden soll, soll TDBEdtFrm das Hauptformular einer eigenen Anwendung bilden, folglich ist der Funktionsumfang hier höher. Es wäre daher wohl besser TDBEdtFrm als eine Erweiterung von TDBEdtFrmChild zu programmieren. Jedenfalls sind das so die Probleme, mit denen ich mich so rumschlagen muss ;o))

Vielen Dank für Eure Antworten zu diesem Thread

Greetings Scouty
  Mit Zitat antworten Zitat
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#8

Re: Methodenhierarchie in der Vererbung umgehen/überspringen

  Alt 12. Jan 2004, 14:56
@Choose: die .CommonMethod() in meinem Beispiel zeigt wie es OOP konform zu lösen ist.

@scouty: Es sind virtuelle/Dynamische Methoden und exakt weil deine TypCast Methode nicht funktionieren kann, habe ich dir die beiden Wege aufgezeigt.

Nun, warum kann ein TypCast nicht funktionieren ?
Jedes allozierte Object hat intern einen Zeiger auf seine Klasse. Eine Klasse ist nichts anderes als eine Speicherstruktur die im Codesegement gespeichert wurde. D.h. werden zB. 100 Objecte vom Typ TMyObject erzeugt so zeigen alle diese 100 Objecte auf ein und dieselbe Klassendefinition ins Codesegement. In dieser Klassendefinition sind zB. der ClassName, ParentClass, ClassType, die VMT = Virtual Method Table, die DMT = Dynamic Method Table usw. usw. gespeichert. Wird nun eine Virtuelle Methode aufgerufen so wird aus der Klassendefinition in der VMT die realle Addresse der Methode die aufgerufen werden soll ausgelesen. Der Code verzeigt nach diesem Auslesen also an diese Methodenadresse. Nun da Self.Classtype = TC ist wird somit immer auf die VMT von TC zugegriffen. In dieser VMT steht ein Zeiger auf @TC.Method und nicht TA.Method. Somit wird auch bei einem TypCast trotzdem TC.Method aufgerufen. So und nicht anders SOLLEN virtuelle methoden auch funktionieren.

Um aber wiederum zu tricken, müssen wir also den Klassentyp von Self= TC in den Klassentyp TA ändern. Nachdem dies passiert ist würde man mit TC(Self).Method() nun tatsächlich TA.Method aufrufen. Dies geht dann so:

Delphi-Quellcode:
procedure TC.Method(Dummy: Integer);
type
  PPointer = ^Pointer;
var
  N: procedure(Dummy: Integer) of object;
begin
  inherited;
  ShowMessage('TC');

// nun rufen wir inherited TA.Method(Dummy) auf
  TMethod(N).Data := Self;
  TMethod(N).Code := @TA.Method;

  N(Dummy);

// nun so wie es auf herkömmliche OOP Weise funktioniert
  CommonMethod(Dummy);

// hier wird die Klasse TC von Self^ in Klasse TA gepatcht, und wieder zurück
  ShowMessage('Klassenname ist ' + ClassName);
  PPointer(Self)^ := TA;
  ShowMessage('Klassenname ist ' + ClassName);
  Method(Dummy);
  PPointer(Self)^ := TC;
end;
Im letzten Code siehtst du also das der erste Zeiger im Speicher von Self^ ein Zeiger auf den Klassentyp von Self ist. Jedes Object speichert also als erstes unsichtbares Feld seinen eigenen Klassentyp. Der Klassentyp selber ist nichts anderes als ein Zeiger in das Codesegment wo der Compiler die Klassendefinition abgelegt hat. Biegen wir diesen Zeiger um so können wir den Klasentyp eines beliebigen Objectes auf einen anderen Klassentyp patchen. Aber Vorsicht dies hat gravierende Konsequenzen.
In unserem Fall hat dies zu Konsequenz das nun die VMT=Virtuelle Methoden Tabelle von Self nicht mehr die originale von TC ist sondern die von TA. Somit wird der nachfolgende Aufruf von Self.Method() nicht wie erwartet eine Rekursion hervorufen, sondern eben TA.Method() aufrufen.

Gruß Hagen
  Mit Zitat antworten Zitat
choose

Registriert seit: 2. Nov 2003
Ort: Bei Kiel, SH
729 Beiträge
 
Delphi 2006 Architect
 
#9

Re: Methodenhierarchie in der Vererbung umgehen/überspringen

  Alt 12. Jan 2004, 15:39
Hallo Hagen, hallo scouty,
Zitat:
@choose: die .CommonMethod() in meinem Beispiel zeigt wie es OOP konform zu lösen ist.
Hier wird aber leider kein unterschiedliches Verhalten in Abhängigkeit der konkreten Klasse erreicht (wie zB bei der Polymorphie).

@scouty: Es ist ein typisches Verhalten von "Neuligen" der OOP, jede Problemstellung über Vererbung lösen zu wollen. Zwar ist diese Technik ein herausragendes Kriterium der OOP, trotzdem ist sie statisch und in hohem Maße unflexibel.
Für Deine konkrete Anforderung, sollte sie, sofern wirklich nur das das Schließen gemeint ist, jedoch mithilfe des Bei Google suchentemplate method pattern vollkommen ausreichen:
Zitat von GoF:
[..]Use a skeleton of the algorithm in the operation. Subclasses can redefine the certain steps in the algorithm without changing the algorithm's structure.
Zunächst einmal ändere ich Deine Hierarchie
Code:
TBscFrm
  \- TDBEdtFrm
      \- TDBEdtFrmChild
nach
Code:
TBscFrm
  \- TCustomDBEdtFrm (abstract)
      \- TDBEdtFrm
      \- TDBEdtFrmChild
so dass in TCustomDBEdtFrm das von dir beschriebene in TDBEdtFrm und TDBEdtFrmChild verwendete DB-Verhalten implementier wird.
Anschließend verwende ich in FormClose, dem Eventhandlet von OnClose das oben erwähnte Template Method-Pattern wie folgt
Delphi-Quellcode:
type
  TCustomDBEdtFrm = //..
    procedure FormClose(Sender: TObject);
  protected
    procedure DoCustomBeforeClose; virtual;
    procedure DoCustomAfterClose; virtual;
    //..
  end;

procedure TCustomDBEdtFrm.DoCustomBeforeClose;
begin
  //do nothing (template method pattern)
end;

procedure TCustomDBEdtFrm.DoCustomAfterClose;
begine
  //do nothing (template method pattern)
end;

procedure TCustomDBEdtFrm.FormClose(Sender: TObject);
begin
  // call redefined code (template method pattern)
  DoCustomBeforeClose;

  // do some generic close behavior
  //..

  // call redefined code (template method pattern)
  DoCustomAfterClose;
end;
so dass ein spezifischer Teil in einem der konkreten Erben angepasst werden kann
Delphi-Quellcode:
type
  TDBEdtFrm = class(TCustomDBEdtFrm)
  protected
    procedure DoCustomAfterClose; override;
  end;

procedure TDBEdtFrm.DoCustomAfterClose;
begin
  // free allocated ressources
end;
Die entsprechenden Teile der Logik werden somit an die Unterklasse delegiert...

Es fällt auf, dass sich diese Lösung nur eignet, um triviale Probleme zu lösen: Müssen DoCustomAfterClose und DoCustomBeforeClose unterschiedlich implementiert werden, obgleich die übrige Logik der Klasse unverändert bleibt, könnte es mit diesem Muster zu einer "Explosion" von Unterklassen kommen.
Eine gängige Lösung bietet hier das Bei Google suchenstrategy pattern, das in Delphi auch über Events gelöst werden kann. Stelle Dir diesen Ansatz vereinfacht so vor, als wenn Du ein nur für die Klasse und alle Erben sichtbares Eregnis (ähnlich wie OnClick) mit einer optionalen Ereignisbehandlungsroutine (vergleichbar mit Button1Click) verknüpfst. Statt also in der oben skizzierten Version von FormClose direkt DoCustoAfterClose aufzurufen, sähe dieser Ansatz eine weitere Delegation vor...
gruß, choose
  Mit Zitat antworten Zitat
scouty

Registriert seit: 27. Nov 2003
Ort: Dresden
47 Beiträge
 
#10

Re: Methodenhierarchie in der Vererbung umgehen/überspringen

  Alt 13. Jan 2004, 18:22
Na ja, das ist zweifellos ein besser Ansatz. Ich gebe zu, dass man mich durchaus als Anfänger betrachten kann, und dass ich ebenfalls dazu neige, vieles über die Vererbung lösen zu wollen. Auf der anderen Seite geben ich mir immer Mühe, möglichst wenige Methoden, welche überdies möglichst kurz gehalten sind, zu erstellen. Das sieht nicht nur meiner Meinung nach besser aus, mir fällt es dann auch leichter, den Überblick zu behalten. Dein Einsatz leuchtet ein, und ist natürlich die flexibelste Lösung. Nur, dass ich dann mehr Methoden habe. Ich habe meinen Königsweg noch nicht gefunden, ich bin noch um Studieren der verschiedenen Stile und Ansätze. Eure Anregungen haben mir auf jeden Fall sehr geholfen. Mit Sicherheit werde ich mir demnächst mal diese Patterns reinziehen. Vielen Dank für Eure Beiträge und hoffentlich auf bald… Scouty
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 07:33 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