![]() |
Übung Polymorphie
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo, ich wage mich mal wieder ins Forum mit der Bitte, über meine kleine Übung zur Polymorphie zu sehen und mir ggf. einen Tip zu geben ob und wo ich was nicht richtig gemacht habe.
Auf einige Kleinigkeiten, wie der Prüfung ob die Werte größer 0 bzw. nur Zahlen sein dürfen habe ich hier verzichtet. Meine Frage ist: habe ich das Prinzip der Polymorphie in Form der Klassen (TFigur, TRechteck, TDreieck) so richtig verstanden? Vielen Dank im voraus. Programm anbei
Delphi-Quellcode:
und die Form:
unit uBerechnung;
interface uses Vcl.Dialogs; type TFigur = class(TObject) private FSeiteA:Double; FSeiteB:Double; procedure SetSeiteA(A:Double);virtual;abstract; procedure SetSeiteB(B:Double);virtual;abstract; function getSeiteA:Double;virtual;abstract; function getSeiteB:Double;virtual;abstract; function getBerechneFl:Double;virtual;abstract; end; type TRechteck = class(TFigur) private procedure SetSeiteA(A:Double);override; procedure SetSeiteB(B:Double);override; function getSeiteA:Double;override; function getSeiteB:Double;override; function getBerechneFl:Double; override; public property SeiteA:Double read FSeiteA write FSeiteA; property SeiteB:Double read FSeiteB write FSeiteB; property BerechneFl:Double read getBerechneFl; end; type TDreieck = class(TRechteck) private procedure SetSeiteA(A:Double);override; procedure SetSeiteB(B:Double);override; function getSeiteA:Double;override; function getSeiteB:Double;override; function getBerechneFl:Double;override; public property SeiteA:Double read FSeiteA write FSeiteA; property SeiteB:Double read FSeiteB write FSeiteB; property BerechneFl:Double read getBerechneFl; end; implementation { TRechteck } function TRechteck.getBerechneFl: Double; begin result:=FSeiteA*FSeiteB; end; function TRechteck.getSeiteA: Double; begin result:=FSeiteA; end; function TRechteck.getSeiteB: Double; begin result:=FSeiteB; end; procedure TRechteck.SetSeiteA(A: Double); begin FSeiteA:=A; end; procedure TRechteck.SetSeiteB(B: Double); begin FSeiteB:=B; end; { TDreieck } function TDreieck.getBerechneFl: Double; begin result:=FSeiteA*FSeiteB/2; end; function TDreieck.getSeiteA: Double; begin result:=FSeiteA; end; function TDreieck.getSeiteB: Double; begin result:=FSeiteB; end; procedure TDreieck.SetSeiteA(A: Double); begin FSeiteA:=A; end; procedure TDreieck.SetSeiteB(B: Double); begin FSeiteB:=B; end; end.
Delphi-Quellcode:
unit uMain;
interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, uBerechnung, Vcl.StdCtrls; type TForm1 = class(TForm) btnEnde: TButton; edtSeiteA: TEdit; edtSeiteB: TEdit; lblSeiteA: TLabel; lblSeiteB: TLabel; lblErg: TLabel; btnFl: TButton; btnDreieck: TButton; procedure btnEndeClick(Sender: TObject); procedure btnFlClick(Sender: TObject); procedure FormCreate(Sender: TObject); procedure btnDreieckClick(Sender: TObject); private { Private-Deklarationen } public { Public-Deklarationen } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.btnDreieckClick(Sender: TObject); var MyFigure: TDreieck; begin MyFigure := TDreieck.Create; try MyFigure.SeiteA:=strtofloat(edtSeiteA.text); MyFigure.SeiteB:=strtofloat(edtSeiteB.text); lblErg.caption:=floattostr(MyFigure.BerechneFl) finally MyFigure.Free; end; end; procedure TForm1.btnEndeClick(Sender: TObject); begin close; end; procedure TForm1.btnFlClick(Sender: TObject); var MyFigure : TRechteck; begin MyFigure := TRechteck.Create; try MyFigure.SeiteA:=strtofloat(edtSeiteA.text); MyFigure.SeiteB:=strtofloat(edtSeiteB.text); lblErg.caption:=floattostr(MyFigure.BerechneFl); finally MyFigure.Free; end; end; procedure TForm1.FormCreate(Sender: TObject); begin edtSeiteA.text:=''; edtSeiteB.text:=''; end; end. |
AW: Übung Polymorphie
Deklariere Deine lokalen Variablen in den Form-Methoden einmal als TFigur. Wenn der Code dann funktioniert, ist das Polymorphie (etwas vereinfacht, aber im Kern richtig).
|
AW: Übung Polymorphie
nachdem ich in der class TFigur eine Ergänzung vorgenommen habe und dann in der Form die lokale Var in TFigur umbenannt habe geht es auch.
Zuvor hatte das Programm jedoch auch keine Probleme.
Delphi-Quellcode:
Danke.
type TFigur = class(TObject)
private FSeiteA:Double; FSeiteB:Double; procedure SetSeiteA(A:Double);virtual;abstract; procedure SetSeiteB(B:Double);virtual;abstract; function getSeiteA:Double;virtual;abstract; function getSeiteB:Double;virtual;abstract; function getBerechneFl:Double;virtual;abstract; public property SeiteA:Double read FSeiteA write FSeiteA; property SeiteB:Double read FSeiteB write FSeiteB; property BerechneFl:Double read getBerechneFl; Also habe ich das Problem wohl richtig verstanden?! |
AW: Übung Polymorphie
Ich würde die virtuellen Methoden als protected deklarieren, sonst gibt es Kummer, wenn Du die Ableitungen einmal in eine eigene Unit verschiebst. Und Polymorphie bedeutet ja Vielgestaltigkeit, d.h. der konsumierende Code muss die konkrete Klasse, die er benutzt, gar nicht kennen. Es genügt, wenn er die Basisklasse kennt, ihn interessiert es dabei dann nicht mehr, um welche Ableitung es sich handelt.
|
AW: Übung Polymorphie
habe den Hinweis berücksichtigt, jedoch erhalte ich den folgenden Hinweis:
Zitat:
|
AW: Übung Polymorphie
Schreibe den Setter ebenfalls in den Protected Abschnitt und dann ist auch die Warnung weg. Warnungen solltest du schon beachten und auch entsprechend versuchen sie zu beheben. Nicht einfach damit leben. :cyclops:
|
AW: Übung Polymorphie
Ok. Setter ist auch protected. Nun habe ich immer noch eine Warnung:
Zitat:
|
AW: Übung Polymorphie
Hallo,
nein, dann wären die ja in der Basisklasse überflüssig. Ich würde den Tip von DeddyH einfach wieder zurücknehmen oder deine privat-Variablen landen auch in der Basisklasse unter protected. Das wäre dann zwar ein schönes Beispiel für Polymorphie, aber nicht für Klassendesign. Dort sollten ja Variablen, die die abgeleitete Klasse nicht zu interessieren hat, privat sein. |
AW: Übung Polymorphie
Ich habe das Ganze mal umgeschrieben:
Delphi-Quellcode:
Formular:
type
TFigur = class private FSeiteB: Double; FSeiteA: Double; protected function GetFlaeche: double; virtual; abstract; public property SeiteA: Double read FSeiteA write FSeiteA; property SeiteB: Double read FSeiteB write FSeiteB; property Flaeche: double read GetFlaeche; end; TRechteck = class(TFigur) protected function GetFlaeche: double; override; end; TDreieck = class(TRechteck) protected function GetFlaeche: double; override; end; ... { TRechteck } function TRechteck.GetFlaeche: double; begin Result := SeiteA * SeiteB; end; { TDreieck } function TDreieck.GetFlaeche: double; begin Result := SeiteA * SeiteB / 2; end;
Delphi-Quellcode:
type
TForm6 = class(TForm) Button1: TButton; Button2: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); private { Private-Deklarationen } procedure ShowFlaeche(Figur: TFigur); public { Public-Deklarationen } end; ... procedure TForm6.Button1Click(Sender: TObject); var Figur: TFigur; begin Figur := TRechteck.Create; try Figur.SeiteA := 4; Figur.SeiteB := 5; ShowFlaeche(Figur); finally Figur.Free; end; end; procedure TForm6.Button2Click(Sender: TObject); var Figur: TFigur; begin Figur := TDreieck.Create; try Figur.SeiteA := 4; Figur.SeiteB := 5; ShowFlaeche(Figur); finally Figur.Free; end; end; procedure TForm6.ShowFlaeche(Figur: TFigur); begin ShowMessage(Format('Die Fläche beträgt %.2f', [Figur.Flaeche])); end; |
AW: Übung Polymorphie
Von mir:
- in unit uBerechnung kein uses Vcl.Dialogs - statt "privat" besser "strict privat" und "strict" auch bei "protected" |
AW: Übung Polymorphie
Danke Hoika, ja so funktioniert es.
Danke auch an Freimatz, vcl.dialogs habe ich aufgenommen, da ich eigentlich mit showmessage arbeiten wollte. Hat sich aber erledigt. Warum soll ich ausdrücklich "strict" private bzw. strict protected verwenden? reicht private bzw. protected nicht aus? was ist da anders bzw. besser? Danke DaddyH, das ist wesentlich einfacher und übersichtlicher. Warum bin ich nicht darauf gekommen? Ich habe mich von dem Gedöns Getter und Setter verleiten lassen. Aber so einfach funktioniert es auch einwandfrei und vermeidet Stolpersteine. Wieder ein kleines Problemchen gelöst. |
AW: Übung Polymorphie
Zitat:
|
AW: Übung Polymorphie
Genau das ist es. Das ist bei deinem Problem nur eine Kleinigkeit sollte man sich jedoch angewöhnen. Siehe auch:
![]() |
AW: Übung Polymorphie
nun wollte ich das ganze vertiefen und habe dazu eine weitere class TTrapez (TDreieck) hinzugefügt. Für die Flächenberechnung wird nun zusätzlich zu SeiteA und SeiteB noch eine Höhe benötigt. Wenn ich nun in meiner Naivität einfach zur geerbten class TTrapez das Feld FHoehe und das Property Hoehe
hinzufüge im Glauben, dass ich zum Geerbten einfach noch die zusätzlich benötigten Felder und Propertys für eine erfolgreiche Programmausführung hätte, habe ich mich gründlich getäuscht, denn er findet die Poperty Hoehe in der Form nicht. Muss ich also diese Ergänzungen für die Flächenberechnung eines Trapezes in die Basisklasse TFigur aufnehmen damit ich weiter komme? Eigentlich wollte ich sehen wie es mit der Vererbung klappt, doch so würde ich zwar auch mit dem Ergebnis der Vererbung von der Basisklasse arbeiten. Schön wäre es, wenn ich mit dem Erbe von TDreieck und den zusätzlich benötigten Feldern arbeiten könnte. Liege ich da völlig falsch? Wie sollte ich das anstellen. Anbei die Auszüge aus der unit Berechnung und unit Form:
Delphi-Quellcode:
und die Form:
type TFigur = class(TObject)
private FSeiteA:Double; FSeiteB:Double; strict protected function getBerechneFl:Double;virtual;abstract; public property SeiteA:Double read FSeiteA write FSeiteA; property SeiteB:Double read FSeiteB write FSeiteB; property BerechneFl:Double read getBerechneFl; end; type TRechteck = class(TFigur) strict protected function getBerechneFl:Double;override; end; type TDreieck = class(TRechteck) strict protected function getBerechneFl:Double;override; end; type TTrapez = class (TDreieck) private FHoehe : Double; strict protected function getBerechneFl: Double;override; public property Hoehe :Double read FHoehe write FHoehe; end; implementation { TRechteck } function TRechteck.getBerechneFl: Double; begin result:=FSeiteA*FSeiteB; end; { TDreieck } function TDreieck.getBerechneFl: Double; begin result:=FSeiteA*FSeiteB/2; end; { TTrapez } function TTrapez.getBerechneFl: Double; begin result:=(SeiteA+SeiteB)/2*Hoehe; end; end.
Delphi-Quellcode:
procedure TForm1.btnDreieckClick(Sender: TObject);
var MyFigure: TFigur; begin MyFigure := TDreieck.Create; try MyFigure.SeiteA:=strtofloat(edtSeiteA.text); MyFigure.SeiteB:=strtofloat(edtSeiteB.text); lblErg.caption:=floattostr(MyFigure.BerechneFl) finally MyFigure.Free; end; end; procedure TForm1.btnEndeClick(Sender: TObject); begin close; end; procedure TForm1.btnFlClick(Sender: TObject); var MyFigure : TFigur; begin MyFigure := TRechteck.Create; try MyFigure.SeiteA:=strtofloat(edtSeiteA.text); MyFigure.SeiteB:=strtofloat(edtSeiteB.text); lblErg.caption:=floattostr(MyFigure.BerechneFl); finally MyFigure.Free; end; end; procedure TForm1.btnTrapezClick(Sender: TObject); var MyFigure : TFigur; begin MyFigure := TTrapez.create; try MyFigure.SeiteA:=strtofloat(edtSeiteA.text); MyFigure.SeiteB:=strtofloat(edtSeiteB.text); //MyFigure //Problem: findet Hoehe aus TTrapez nicht! lblErg.caption:=floattostr(MyFigure.BerechneFl); finally MyFigure.Free; end; end; procedure TForm1.FormCreate(Sender: TObject); begin edtSeiteA.text:=''; edtSeiteB.text:=''; end; end. |
AW: Übung Polymorphie
MyFigure ist als TFigur deklariert, deshalb kannst Du auch nur dessen Methoden und Eigenschaften direkt nutzen. Für spezifische Sachen musst Du typecasten.
Delphi-Quellcode:
procedure TForm1.btnTrapezClick(Sender: TObject);
var MyFigure : TFigur; begin MyFigure := TTrapez.create; try MyFigure.SeiteA:=strtofloat(edtSeiteA.text); MyFigure.SeiteB:=strtofloat(edtSeiteB.text); TTrapez(MyFigure).Hoehe := 20; // Um die Hoehe ansprechen zu können ist ein Cast notwendig lblErg.caption:=floattostr(MyFigure.BerechneFl); finally MyFigure.Free; end; end; |
AW: Übung Polymorphie
Danke DaddyH, es funktioniert.
Nur zum Selbstverständnis: wollte ich eine geerbte class TTrapez (TDreieck) um irgendwelche Felder und ggf. auch Methoden ergänzen, so müßte ich also die lokale Variable MyFigure als TTrapez deklarieren und könnte somit auf die Felder und Methoden zugreifen? Entspricht dies auch den guten Sitten der OOP? |
AW: Übung Polymorphie
Ja, wieso denn nicht? Der Cast wäre unnötig gewesen, wenn Du die lokale Variable gleich als TTrapez deklariert hättest, aber das wäre ja am Thema vorbei. Du kannst auch beispielsweise in Routinen, die eine TFigur als Parameter entgegennehmen, prüfen, ob es sich um eine spezialisierte Klasse handelt und sie dementsprechend behandeln.
Delphi-Quellcode:
Das kann aber leider auch sehr schnell zu sehr unübersichtlichem Code führen, in dem Fall sollte man vielleicht sein Klassendesign überdenken und/oder die Verwendung von Interfaces in Betracht ziehen.
procedure MachWas(Figur: TFigur);
begin if Figur is TTrapez then TTrapez(Figur).Hoehe := 20; end; |
AW: Übung Polymorphie
Statt diesem
Delphi-Quellcode:
würd' ich lieber das machen:
procedure TForm1.btnTrapezClick(Sender: TObject);
var MyFigure : TFigur; begin MyFigure := TTrapez.create; try MyFigure.SeiteA:=strtofloat(edtSeiteA.text); MyFigure.SeiteB:=strtofloat(edtSeiteB.text); TTrapez(MyFigure).Hoehe := 20; // Um die Hoehe ansprechen zu können ist ein Cast notwendig lblErg.caption:=floattostr(MyFigure.BerechneFl); finally MyFigure.Free; end; end;
Delphi-Quellcode:
Casten würd' ich in soeiner Situation:
procedure TForm1.btnTrapezClick(Sender: TObject);
var MyFigure : TTrapez; begin MyFigure := TTrapez.create; try MyFigure.SeiteA:=strtofloat(edtSeiteA.text); MyFigure.SeiteB:=strtofloat(edtSeiteB.text); MyFigure.Hoehe := 20; lblErg.caption:=floattostr(MyFigure.BerechneFl); finally MyFigure.Free; end; end;
Delphi-Quellcode:
function FlaecheBerechnen(Sender: TFigure) : Double;
begin if Sender is TTrapez then Result := TTrapez(Sender).BerechneFl else if Sender is TRechteck then Result := TRechteck(Sender).BerechneFl else if Sender is TDreieck then Result := TDreieck(Sender).BerechneFl else if Sender is TFigur then Result := TFigur(Sender).BerechneFl else Raise('unbekannte Figur'); end; // Aufruf: procedure TForm1.BtnBerechnenClick(Sender: TObject); begin MyFigure := TTrapez.create; // der gewünschte Typ, den könnte man z. B. über 'n TRadioGroup auswählen. try MyFigure.SeiteA := strtofloat(edtSeiteA.text); MyFigure.SeiteB := strtofloat(edtSeiteB.text); MyFigure.Hoehe := strtofloat(edtHoehe.text); lblErg.caption := floattostr(FlaecheBerechnen(myFigure)); finally MyFigure.Free; end; end; |
AW: Übung Polymorphie
Wozu die ganzen "Is"-Abfragen in FlaecheBerechnen, wenn eine TFigur-Instanz übergeben wird? Die angesprochene Methode ist virtuell, es sollte also immer die passende ausgeführt werden.
|
AW: Übung Polymorphie
Hast recht, wäre nur dann erforderlich, wenn sie nicht von einer Basisklasse abgeleitet wären.
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 06:02 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