![]() |
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:
Und die Unit Family mit der Klasse:
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;
Delphi-Quellcode:
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.
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. Es wäre auch schön, wenn man die Operatoren as und is einbauen könnte. |
Re: Beispiel für Polymorphie gesucht
Ich würde deine beiden Klassen durch eine abstrakte Oberklasse erweitern:
Delphi-Quellcode:
Denn dein Beispiel würde ja bedeuten, dass der Sohn eine Art von Elternteil ist.
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. grüße, daniel |
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:
vermieden werden können...
var
myParent: TParent; begin myParent:= TChild.Create; 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:
Mit einer Hierarchie dieser Art ist die Verwendung etwas "natürlicher"
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;
Delphi-Quellcode:
und lässt ebenfalls "natürliche" Casts etc zu
type
myFruit: TFruit; begin myFruit:= TBanana.Create; myFruit.GetTaste;
Delphi-Quellcode:
Edit: Vorfahren der Klassen angegeben
if myFruit is TCitrusFruit then
with myFruit as TCitrusFruit do Squeeze; |
Re: Beispiel für Polymorphie gesucht
:thumb: Dann wäre auch das Schlüsselwort abstract drin.
@Choose: Da mus sich jetzt erstmal durchsteigen. :? :-/ |
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] |
Re: Beispiel für Polymorphie gesucht
Fehler von Choose, da fehlt überall die Angabe des Parents. ;)
|
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:
|
Re: Beispiel für Polymorphie gesucht
Zitat:
Habe tatsächlich in der Eile die Vorfahren vergessen, schön, dass das Beispiel trotzdem verstanden wurde! |
Re: Beispiel für Polymorphie gesucht
@Choose: was hast du denn jetzt genmacht? :shock:
Delphi-Quellcode:
Was gibt denn das, wenn es fertig wird? :gruebel:
TGrapefruit(TCitrusFruit) = class
|
Re: Beispiel für Polymorphie gesucht
Zitat:
|
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 ;) |
Re: Beispiel für Polymorphie gesucht
Jupp, läuft schon, gedae implementiert:
Delphi-Quellcode:
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?
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; |
Re: Beispiel für Polymorphie gesucht
@choose: Ich will ja nicht nerven, aber
Delphi-Quellcode:
sollte das type nicht besser ein var sein? :mrgreen:
type
myFruit: TFruit; begin myFruit:= TBanana.Create; myFruit.GetTaste; War wohl nicht dein Tag. :? |
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:
|
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:
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"..
if Sender is TButton then
with Sender as TButton do |
Re: Beispiel für Polymorphie gesucht
Habe schon was:
Delphi-Quellcode:
Banane kann man nicht ausdrücken. ;)
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; @choose: Ich hoffe mal, dass du an dem Tag keine Final für den Kunden zur Auslieferung fertig gemacht hast. :zwinker: |
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: |
Re: Beispiel für Polymorphie gesucht
Zitat:
|
Re: Beispiel für Polymorphie gesucht
Wie würdest du es besser machen?
|
Re: Beispiel für Polymorphie gesucht
Hallo Luckie,
ich denke Motzi meint
Delphi-Quellcode:
WITH (MyFruit AS TCitrusFruit) DO
BEGIN Squeeze ... |
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?
|
Re: Beispiel für Polymorphie gesucht
Zitat:
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'); |
Re: Beispiel für Polymorphie gesucht
Ich hab in meinem letzten Post geschrieben:
Zitat:
Delphi-Quellcode:
if MyFruit is TCitrusFruit then
TCitrusFruit(MyFruit).Squeeze |
Re: Beispiel für Polymorphie gesucht
OK, an den "normalen" Typecast hab ich hier nicht gedacht :wall:
|
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.
![]() Man liest sich, Stanlay :hi: |
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. ;) |
Re: Beispiel für Polymorphie gesucht
Hallo Motzi,
Zitat:
Für mich stellt sich, gerade bei einem Anfänger-Tutorial eher die Frage nach der Konsistenz. Sowohl bei Ereignisbehandlungsroutinen
Delphi-Quellcode:
,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.
procedure TForm1.Button1Click(Sender: TObject);
begin with Sender as TButton do Caption:= AMethod; 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:
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:
procedure SomeTest(const AReference: IInterface);
begin if Supports(AReference, IMyInterface) then IMyInterface(AReference).AMethod;
Delphi-Quellcode:
Eine Lösung mit as innerhalb von SomeTest hingegen arbeitet fehlerfrei.
type
TMyClass = class(TMyAncestor, IInterface, IMyInterface) end; //.. var myRef: IInterface; begin myRef:= TMyClass.Create; SomeTest(myRef); 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:
zu verwenden, um vermeintlich einiges an Performance herauszuholen, dabei aber Randbedingungen zu übersehen...
var
s: string[4]; begin Integer(s):= $badfood; |
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; |
Re: Beispiel für Polymorphie gesucht
Hm, hm, hm. Da bin ich gerade mehr oder weniger bei meinem Tutorial. Ist schon vorgemerkt. ;)
|
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; |
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. |
Re: Beispiel für Polymorphie gesucht
Thanke Chewie !
|
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* |
Re: Beispiel für Polymorphie gesucht
Ws ist fertig. :roll:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 17:59 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-2025 by Thomas Breitkreuz