Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Beispiel für Polymorphie gesucht (https://www.delphipraxis.net/15405-beispiel-fuer-polymorphie-gesucht.html)

Luckie 28. Jan 2004 08:13


Beispiel für Polymorphie gesucht
 
Ich arbeite gerade an einem Tutorial über Klassen in Delphi. Zum Thema Polymorphie habe ich mir folgendes Demo geschrieben:
Unit1:
Delphi-Quellcode:
uses
  Family;

procedure TForm1.btnParentsClick(Sender: TObject);
var
  Parents: TParent;
begin
  Parents := TParent.Create;
  try
    ShowMessage(Parents.GetType);
  finally
    FreeAndNil(Parents);
  end;
end;

procedure TForm1.btnSonClick(Sender: TObject);
var
  Son: TParent;
begin
  Son := TSon.Create;
  try
    ShowMessage(Son.GetType);
  finally
    FreeAndNil(Son);
  end;
end;
Und die Unit Family mit der Klasse:
Delphi-Quellcode:
unit Family;

interface

type
  TParent = class(TObject)
  public
    function GetType: String; virtual; // zum Überschreiben markieren
  end;

type
  TSon = class(TParent)
  public
    function GetType: String; override; // Eltern-Methode überschrieben
  end;

implementation

////////////////////////////////////////////////////////////////////////////////
//
//  TParent.GetType
//
//    Verwandtschaftsbeziehung.

function TParent.GetType: String;
begin
  result := 'Eltern';
end;

////////////////////////////////////////////////////////////////////////////////
//
//  TSon.GetType
//
//    Verwandtschaftsbeziehung.

function TSon.GetType: String;
begin
  result := 'Sohn';
end;

end.
Ich bin damit allerdings noch nicht so ganz glücklich. Hat eventuell jemand eine bessere Idee? Es sollte möglichst einfach und übersichtlich sein. Eventuell reicht es ja schon die Methoden besser zu benennen oder nur etwas zu ergänzen.

Es wäre auch schön, wenn man die Operatoren as und is einbauen könnte.

Sanchez 28. Jan 2004 08:27

Re: Beispiel für Polymorphie gesucht
 
Ich würde deine beiden Klassen durch eine abstrakte Oberklasse erweitern:

Delphi-Quellcode:
unit Family;

interface

type
  TFamilyMember = class(TObject)
  public
    function GetType: String; virtual; abstract; // muss Überschrieben werden
  end;

type
  TParent = class(TFamilyMember)
  public
    function GetType: String; override; // FamilyMember-Methode überschrieben
  end;

type
  TSon = class(TFamilyMember)
  public
    function GetType: String; override; // FamilyMember-Methode überschrieben
  end;

implementation

////////////////////////////////////////////////////////////////////////////////
//
//  TParent.GetType
//
//    Verwandtschaftsbeziehung.

function TParent.GetType: String;
begin
  result := 'Eltern';
end;

////////////////////////////////////////////////////////////////////////////////
//
//  TSon.GetType
//
//    Verwandtschaftsbeziehung.

function TSon.GetType: String;
begin
  result := 'Sohn';
end;

end.
Denn dein Beispiel würde ja bedeuten, dass der Sohn eine Art von Elternteil ist.

grüße, daniel

choose 28. Jan 2004 08:33

Re: Beispiel für Polymorphie gesucht
 
Hallo Luckie,

schön, dass Du Dir die Mühe machen willst. Vielleicht kannst Du bei der Gelegenheit auch gleich den Begriff der abstrakten Klasse einführen? Oft wird die Eleganz von Lösungen dieser Art verkannt, obgleich mit ihnen etwas verwirrende Zuweisungen der Art
Delphi-Quellcode:
var
  myParent: TParent;
begin
  myParent:= TChild.Create;
vermieden werden können...

Ich dachte an eine Klassifizierungslösung (ala Aristoteles), also eine Form der Spezialisierung, wobei ich aus Vereinfachungsgründen die Spezialisierung selbst (zB in Form neuer Operationen) nur geringfügig (Im Fall der Zitrusfrucht) beachte:
Delphi-Quellcode:
type
  TFruit = class //pure abstract class
  public
    function GetTaste: string; virtual; abstract;
  end;

  TCitrusFruit = class(TFruit) //pure abstract class introduces new ops
  public
    procedure Squeeze; virtual; abstract;
  end;

  TLemon = class(TCitrusFruit) // same taste as citrus fruit
  public
    function GetTaste: string; override; // acerbic
    procedure Squeeze; override;
  end;
   
  TGrapefruit(TCitrusFruit) = class
  public
    function GetTaste: string; override; // disgustful ;)
    procedure Squeeze; override;
  end;
 
  TBanana(TFruit) = class
  public
    function GetTaste: string; override; // sugary
  end;
Mit einer Hierarchie dieser Art ist die Verwendung etwas "natürlicher"
Delphi-Quellcode:
type
  myFruit: TFruit;
begin
  myFruit:= TBanana.Create;
  myFruit.GetTaste;
und lässt ebenfalls "natürliche" Casts etc zu
Delphi-Quellcode:
if myFruit is TCitrusFruit then
  with myFruit as TCitrusFruit do
    Squeeze;
Edit: Vorfahren der Klassen angegeben

Luckie 28. Jan 2004 08:33

Re: Beispiel für Polymorphie gesucht
 
:thumb: Dann wäre auch das Schlüsselwort abstract drin.

@Choose: Da mus sich jetzt erstmal durchsteigen. :? :-/

Treffnix 28. Jan 2004 08:42

Re: Beispiel für Polymorphie gesucht
 
@ choose: Und das klappt so? Woher weiss Delphi denn, dass TLemon eine TCitrusfrucht ist? :gruebel:

[ot] Irgendwie hab ich jetzt Lust auf frischgepressten O-Saft :stupid: [/ot]

Luckie 28. Jan 2004 08:45

Re: Beispiel für Polymorphie gesucht
 
Fehler von Choose, da fehlt überall die Angabe des Parents. ;)

Treffnix 28. Jan 2004 08:57

Re: Beispiel für Polymorphie gesucht
 
Hab ich mir auch gedacht. Aber ich war am grübeln, ob er das vielleicht mit der Eleganz meinte. Bei choose weiss man ja nie... :wink:

choose 28. Jan 2004 08:59

Re: Beispiel für Polymorphie gesucht
 
Zitat:

Zitat von Treffnix
Bei choose weiss man ja nie...

Das denke ich mir auch manchmal ;)

Habe tatsächlich in der Eile die Vorfahren vergessen, schön, dass das Beispiel trotzdem verstanden wurde!

Luckie 29. Jan 2004 11:10

Re: Beispiel für Polymorphie gesucht
 
@Choose: was hast du denn jetzt genmacht? :shock:
Delphi-Quellcode:
TGrapefruit(TCitrusFruit) = class
Was gibt denn das, wenn es fertig wird? :gruebel:

Treffnix 29. Jan 2004 11:16

Re: Beispiel für Polymorphie gesucht
 
Zitat:

Was gibt denn das, wenn es fertig wird?
Obstalat? :angle2:

choose 29. Jan 2004 11:18

Re: Beispiel für Polymorphie gesucht
 
Jammer... Die 1440 Minuten sind um und nun werden die offensichtlichen Folgen meiner Konzentrationsschwierigkeiten, bedingt duch eine ungemütliche Erkältung, bis zum Ende der Welt verspottet werden...

Ich denke, Du weißt wie's gemeint war, Luckie, und ich bin überzeugt davon, dass Du, solltest Du das Beispiel tatsächlich für Dein Tutorial verwenden, etwaige Unstimmigkeiten bis zur Veröffentlichung beheben kannst ;)

Luckie 29. Jan 2004 11:21

Re: Beispiel für Polymorphie gesucht
 
Jupp, läuft schon, gedae implementiert:
Delphi-Quellcode:
function TLemon.GetTaste: String;
begin
  result := 'sauer';
end;

procedure TLemon.Squeeze;
begin
  ShowMessage('Von Hand ausdrücken.');
end;

function TGrapefruit.GetTaste: String;
begin
  result := 'süß';
end;

procedure TGrapefruit.Squeeze;
begin
  ShowMessage('Mit der Maschine ausdrücken.');
end;

function TBanana.GetTaste: String;
begin
  result := 'fruchtig';
end;
Das ShowMessage in den Prozeduren ist Absicht. Funktionen wären logischerweise geeigneter, aber man soll ja sehen, dass es auch mit Prozeduren geht. Jetzt muss ich mir nur noch einBeispiel zur Verwendung dafür ausdenken und irgendwo is und as unterbringen. :gruebel: Hat da jemand spontan einenen Geistesblitz?

Luckie 29. Jan 2004 11:25

Re: Beispiel für Polymorphie gesucht
 
@choose: Ich will ja nicht nerven, aber
Delphi-Quellcode:
type
  myFruit: TFruit;
begin
  myFruit:= TBanana.Create;
  myFruit.GetTaste;
sollte das type nicht besser ein var sein? :mrgreen:

War wohl nicht dein Tag. :?

choose 29. Jan 2004 11:30

Re: Beispiel für Polymorphie gesucht
 
Es beeindruckt mich zwar, dass doch einige Personen meine Beiträge etwas intensiver betrachten zu scheinen, aber vielleicht sollte ich für diese Woche erst einmal aufhören zu posten, sonst blamiere ich mich noch vollends :oops:

Motzi 29. Jan 2004 11:36

Re: Beispiel für Polymorphie gesucht
 
Wegen dem is.. eine klassischer Einsatzfall ist es in einem Event-Handler den Parameter Sender per is auf eine bestimmte Klasse zu testen.. aber wie kann man das am besten in einem leicht verständlichen Beispiel verpacken..? :?

Nur eins bitte - bau nicht so einen Mischmasch aus is und as zusammen wie in viele verwenden:
Delphi-Quellcode:
if Sender is TButton then
  with Sender as TButton do
oder sowas in der Art.. as prüft ja intern nochmal per is ob der Cast korrekt ist und löst gegebenenfalls eine Exception aus.. ein Kombination wie oben per is und as wäre also "doppelt gemoppelt"..

Luckie 29. Jan 2004 11:46

Re: Beispiel für Polymorphie gesucht
 
Habe schon was:
Delphi-Quellcode:
uses
  Fruits;

procedure TForm1.Button1Click(Sender: TObject);
var
  MyFruit: TFruit;
  idx: Integer;
begin
  idx := RadioGroup1.ItemIndex;
  case idx of
    0: MyFruit := TLemon.Create;
    1: MyFruit := TGrapefruit.Create;
    2: MyFruit := TBanana.Create;
  end;
  if Assigned(MyFruit) then
  begin
    try
      if MyFruit is TCitrusFruit then
        (MyFruit as TCitrusFruit).Squeeze
      else
        ShowMessage('Keine Zitrusfrucht.');
    finally
      FreeAndNil(MyFruit);
    end;
  end;
end;
Banane kann man nicht ausdrücken. ;)

@choose: Ich hoffe mal, dass du an dem Tag keine Final für den Kunden zur Auslieferung fertig gemacht hast. :zwinker:

Treffnix 29. Jan 2004 11:48

Re: Beispiel für Polymorphie gesucht
 
Jetzt hack doch nicht so auf dem armen choose rum, Luckie! :warn:

Bist doch Mod. Bügel doch eben die Fehler aus, bevor er vor lauter Scham unter falschem Namen nach Timbuktu gezogen ist. Du siehst doch, dass er leidet :spin2:

Dafür hatter ja auch immerhin ein is - as Beispiel in seinem ersten post... :chat:

Motzi 29. Jan 2004 12:31

Re: Beispiel für Polymorphie gesucht
 
Zitat:

Zitat von Luckie
Delphi-Quellcode:
      if MyFruit is TCitrusFruit then
        (MyFruit as TCitrusFruit).Squeeze

Genau sowas hab ich gemeint sollst du vermeiden..! :? :roll:

Luckie 29. Jan 2004 12:36

Re: Beispiel für Polymorphie gesucht
 
Wie würdest du es besser machen?

APP 29. Jan 2004 13:01

Re: Beispiel für Polymorphie gesucht
 
Hallo Luckie,
ich denke Motzi meint

Delphi-Quellcode:
WITH (MyFruit AS TCitrusFruit) DO
   BEGIN
      Squeeze
      ...

Luckie 29. Jan 2004 13:03

Re: Beispiel für Polymorphie gesucht
 
Und wenn es eine Banane ist, dann passiert nichts? Gut, wäre eine Möglichkeit. Und wie bringe ich jetzt noch den is Operator unter?

Chewie 29. Jan 2004 13:31

Re: Beispiel für Polymorphie gesucht
 
Zitat:

Zitat von Motzi
Zitat:

Zitat von Luckie
Delphi-Quellcode:
      if MyFruit is TCitrusFruit then
        (MyFruit as TCitrusFruit).Squeeze

Genau sowas hab ich gemeint sollst du vermeiden..! :? :roll:

Na ja, aber wenn man dem Benutzer keine Exception präsentieren will, soll man dann einen try..except-Block nehmen :gruebel:

Also ich finde obiges Konstrukt besser als

Delphi-Quellcode:
try
  with (MyFruit as TCitrusFruit) do
    Squeeze;
except
  ShowMessage('Das hier soll durch eine gescheite Fehlerbehandlung ersetzt werden');

Motzi 29. Jan 2004 14:47

Re: Beispiel für Polymorphie gesucht
 
Ich hab in meinem letzten Post geschrieben:
Zitat:

Zitat von Motzi
as prüft ja intern nochmal per is ob der Cast korrekt ist und löst gegebenenfalls eine Exception aus.. ein Kombination wie oben per is und as wäre also "doppelt gemoppelt"..

Sobald man also selbst per is geprüft hat braucht man nicht mehr per as casten sondern kann dies gleicht direkt tun:
Delphi-Quellcode:
if MyFruit is TCitrusFruit then
        TCitrusFruit(MyFruit).Squeeze

Chewie 29. Jan 2004 14:55

Re: Beispiel für Polymorphie gesucht
 
OK, an den "normalen" Typecast hab ich hier nicht gedacht :wall:

Stanlay Hanks 29. Jan 2004 14:58

Re: Beispiel für Polymorphie gesucht
 
Hi. Ich muss gestehen, ich werde aus dem Ganzen hier nicht so richtig schlau :oops: Ich entnehme dem Begriff "Polymorhie" und einigen DP Threads (z.B. hier) eigentlich nur, dass es irgendwie darum geht, sein Programm zu verändern...Könnte mich mal eben jemand kurz aufklähren, damit ich Dummbeutel das auch verstehe :oops: Also nur, was damit genau erreich werden soll.

Man liest sich, Stanlay :hi:

Luckie 29. Jan 2004 23:55

Re: Beispiel für Polymorphie gesucht
 
Es geht darum, dass du eine Klasse von einer anderen ableiten kannst. Somit erbt der Nachfolger alles vom Vorfahren. Jetzt kannst der neuen Klasse neue Methoden / Properties hinzufügen und somit die alte Klasse erweitern. Das wäre der erste Schritt, die Vererbung. Somit kannst du schon vorhandenen Code als Grundlage nehmen und musst nichts alles noch nal neu erfinden. Zum Beispiel, wenn du dir einen neuen Button programmieren willst, mit neuen Fähigkeiten usw.

Jetzt hast du aber auch die Möglichkeit geerbte Methoden zu überschreiben und zu verändern und kannst sie so deinen Erfodernissen anpassen. Das nennt man dann Polymorphie. Dies setzt allerdings voraus, dass die ursprungs Klasse dies zu läßt. Sprich Methoden müssen mit dem Schlüsselwort virtual zumindest im protected-Abschnitt der ursprungs Klasse deklariert sein. Dann kannst du sie in deiner neuen Klasse mit override überschreiben. Ein anderes Stichwort für die Hilfe wäre reintroduce.

Achte mal in der kommenden Woche etwas auf die Tutorial-Sparte, da stelle ich dann mein Klassen-Tutorial vor. Dies sollte das eigentlich alles erklären und etwaige Klarheiten beseitigen. ;)

choose 3. Feb 2004 08:57

Re: Beispiel für Polymorphie gesucht
 
Hallo Motzi,

Zitat:

Zitat von Motzi
as prüft [..] nochmal [..] ob der Cast korrekt ist und löst gegebenenfalls eine Exception aus

Das ist wahr sollte aber idR nicht ins Gewicht fallen: Zwar hängt die Ausführungszeit stark von der Tiefe der Hierarchie ab, aber bei einem ~1.2GHz Rechner konnte ich mit einer Tiefe von 8 Ebenen knapp 17 Millionen Ergebnisse pro Sekunde vom is Operator verzeichnen.

Für mich stellt sich, gerade bei einem Anfänger-Tutorial eher die Frage nach der Konsistenz. Sowohl bei Ereignisbehandlungsroutinen
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
begin
  with Sender as TButton do
    Caption:= AMethod;
,die "eigentlich nur von einem Button" aufgerufen werden sollten, als auch bei sonstigen Parametern kann eine zusätzliche und dabei so effiziente Form der Überprüfung nicht schaden.
Häufig schon habe ich gesehen, dass zu einem späteren Zeitpunkt solche Routinen auch mit einem NotifyEvent einer anderen Klasse (zB dem OnChange-Event eines Edits) verknüpft werden und dann- im Fall eines nicht abgesicherten Casts zu Problemen führen.

Bei Interfaces hingegen ist die Problematik noch schwerwiegender: Ein Konstrukt der Art
Delphi-Quellcode:
procedure SomeTest(const AReference: IInterface);
begin
  if Supports(AReference, IMyInterface) then
    IMyInterface(AReference).AMethod;
wird zwar kompiliert werden, kann aber trotz des erfolgreichen Tests (Supports) zu Fehlern führen, wenn in diesem Fall die Routine SomeTest in dieser (nicht seltenen) Form aufgerufen wird, die sich mit allen Deklarationen und Typen deckt, also keinen Fehler zur Compilierzeit hervorruft:
Delphi-Quellcode:
type
  TMyClass = class(TMyAncestor, IInterface, IMyInterface)
  end;
//..
var
  myRef: IInterface;
begin
  myRef:= TMyClass.Create;
  SomeTest(myRef);
Eine Lösung mit as innerhalb von SomeTest hingegen arbeitet fehlerfrei.

Weil einem Anfänger so gesagt werden kann: "Verwende immer as und nur as, um einen Cast durchzuführen" wird er den direkten Cast meiden und kann auch nicht in Versuchung geführt Konstrukte der Art
Delphi-Quellcode:
var
  s: string[4];
begin
  Integer(s):= $badfood;
zu verwenden, um vermeintlich einiges an Performance herauszuholen, dabei aber Randbedingungen zu übersehen...

choose 3. Feb 2004 09:13

Re: Beispiel für Polymorphie gesucht
 
Hallo Luckie,
habe mir gerade mal Deinen Entwurf von der Methode Button1Click angesehen. Was hältst Du davon, wenn Du auch gleich das Schlüsselwort class, also das Konzept der Klassenreferenzen aka Metaklassen einführt (muss ja nicht unbedingt mit virtuellen Konstruktoren sein)? Dann ließe sich Dein Code wesentlich eleganter (<- da haben wir's wieder, Achtung, jetzt keine Tippfehler ;)) lösen:
Delphi-Quellcode:
type
  TFruitClass = class of TFruit;

function GetFruitClassByInt(const AnInt: Integer): TFruitClass;
begin
  case AnInt of
    0: Result:= TLemon;
    1: Result:= TGrapeFruit;
    2: Result:= TBanana;
  else
    raise EInvalidClassSelector.CreateFmt(..., [AnInt]);
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  myFruit: TFruit;
begin
  myFruit:= GetFruitClassByInt(RadioGroup1.ItemIndex).Create;
  try
    ShowMessage(Format('Tastes really %s', [myFruit.GetTaste]);
    if myFruit is TCitrusFruit then
      (MyFruit as TCitrusFruit).Squeeze;
  finally
    myFruit.Free;
  end;
end;

Luckie 3. Feb 2004 09:16

Re: Beispiel für Polymorphie gesucht
 
Hm, hm, hm. Da bin ich gerade mehr oder weniger bei meinem Tutorial. Ist schon vorgemerkt. ;)

stoxx 22. Feb 2004 04:25

Re: Beispiel für Polymorphie gesucht
 
mal eine Frage ( nich übel nehmen *g*)

was ist der Unterschied zwischen "out" und "var" ? "out" habe ich in meinem ganzen Leben noch nie gesehen :gruebel:

Code:
class function TFrmClassMethod.GetData (out Data : String): Boolean;

class function TFrmClassMethod.GetData (var Data : String): Boolean;

Chewie 22. Feb 2004 13:28

Re: Beispiel für Polymorphie gesucht
 
out ist eine Spezialart von var. Mit var wird ein Parameter als Referenz übergeben, allerdings muss der Wert des Parameters bereits bei dem Übergeben an die Funktion definiert sein.
Mit der Verwendung von out signalisiert man dem Compiler, dass dieser Parameter in der Funktion gesetzt wird, er wird aber nicht gelesen (zumindest nicht vor dem Setzen). Deshalb muss der Parameter vor Funktionsaufruf nicht initialisiert sein.

stoxx 22. Feb 2004 19:13

Re: Beispiel für Polymorphie gesucht
 
Thanke Chewie !

H4ndy 22. Feb 2004 20:39

Re: Beispiel für Polymorphie gesucht
 
Out könnte mir gefallen =)
Wieder was gelernt dank DP :thuimb:

[OT]
Ich glaub dieses Tut werden ich
mir garantiert zu Gemüte führen wenn es fertig ist!
[/OT]

OT = On-Topic *g*

Luckie 22. Feb 2004 20:41

Re: Beispiel für Polymorphie gesucht
 
Ws ist fertig. :roll:


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