PROGRAM OOP1; { Objekt als RECORD }
USES
Crt;
TYPE
tBruch = OBJECT
z,n : longint; { ; muss stehen }
END;
VAR
a : tBruch;
BEGIN
ClrScr; a.z := 2; a.n := 3;
writeLn('a = ',a.z,'/',a.n);
readLn
END.
|
Das kleine Programm zeigt, dass das Schlüsselwort 'RECORD' durch das Schlüsselwort 'OBJECT' ausgetauscht werden kann.
Das heißt, ein Objekt ist zunächst einmal ein Record. |
PROGRAM OOP2; { Methoden }
USES
Crt;
TYPE
tBruch = OBJECT
{ Datenfelder }
z,n : longint;
{ Methoden }
PROCEDURE gibaus; { hier nur Prozedurkopf }
END;
PROCEDURE tBruch.gibaus; { Syntax beachten ! }
BEGIN { 'gibaus' gehört zu 'tBruch'}
write(z,'/',n)
END;
VAR
a,b : tBruch;
BEGIN
a.z := -2; a.n := 307; b.z := 1; b.n := 7;
writeLn; a.gibaus; write(' + '); b.gibaus; writeLn;
readLn
END.
|
Datenfelder in einem Record sind nichts neues, aber Prozeduren, die mit dem Objekt verbunden sind, das ist neu in nebenstehendem Programm. Zur Sprechweise: Im Programm gibt es einen Datentyp 'tBruch' und eine Variable a vom Typ tBruch, was ist jetzt das Objekt ? Das Delphi-Handbuch und auch so manche Publikation sieht die Datentypen als Objekte an und nennt die Variablen dann 'Instanzen'. Da halte ich es lieber mit denen, die die Typen 'Klassen' nennen und die Variablen Objekte. Beide Sprechweisen kommen vor, also aufpassen! Rüdeger Baumann geht am Schluss seines Artikels "JAVA - Stimulans für den Informatikunterricht" auf diese Problematik ein. |
PROGRAM OOP3; { Vererbung }
USES
Crt;
TYPE
tBruch = OBJECT
{ Datenfelder }
z,n : longint;
{ Methoden }
PROCEDURE gibaus; { hier nur Prozedurkopf }
END;
tsBruch = OBJECT(tBruch) { tsBruch erbt alles aus tBruch }
{ Datenfelder }
s : STRING;
{ Methoden }
PROCEDURE gibaus_s;
END;
PROCEDURE tBruch.gibaus;
BEGIN
write(z,'/',n)
END;
PROCEDURE tsBruch.gibaus_s;
BEGIN
write(s)
END;
VAR
a : tBruch;
b : tsBruch;
BEGIN
a.z := -2; a.n := 307; b.z := 1; b.n := 7; b.s := 'Superbruch';
writeLn; a.gibaus; write(' + '); b.gibaus; writeLn; b.gibaus_s;
readLn
END.
|
Der Datentyp 'tBruch' wird hier zu einem neuen Datentyp 'tsBruch' erweitert. Dabei ist es nicht nötig, auf bekannte, ererbte Datenfelder und Methoden einzugehen; sie sind weiter verfügbar. Ohne Probleme lassen sich aber neue Datenfelder und/oder Methoden hinzufügen. Das Vererbungsprinzip ist insbesondere dann interessant, wenn es 'viel zu erben gibt' wie bei vorgefertigten mächtigen Objekten. |
PROGRAM OOP4; { Vererben mit Überschreiben }
USES
Crt;
TYPE
tBruch = OBJECT
{ Datenfelder }
z,n : longint;
{ Methoden }
PROCEDURE gibaus; { hier nur Prozedurkopf }
END;
tsBruch = OBJECT(tBruch) { tsBruch erbt alles aus tBruch }
{ Datenfelder }
s : STRING;
{ Methoden }
PROCEDURE gibaus; { ererbte Methode wird überschrieben }
END;
PROCEDURE tBruch.gibaus;
BEGIN
write(z,'/',n) { Beachte die Verwendung der Objekt-Qualifizierer }
END;
PROCEDURE tsBruch.gibaus;
BEGIN
write(s)
END;
VAR
a : tBruch;
b : tsBruch;
BEGIN
a.z := -2; a.n := 307; b.z := 1; b.n := 7; b.s := 'Superbruch';
writeLn; a.gibaus; write(' + '); b.gibaus; writeLn; b.gibaus;
readLn
END.
|
Es kann vorkommen, dass man eine ererbte Methode lieber durch eine andere z.B. selbst modifizierte Methode ersetzen möchte. Auch das ist wie in nebenstehendem Beispiel demonstriert möglich. |
PROGRAM OOP5; { Vererben mit Erweitern der Methoden }
USES
Crt;
TYPE
tBruch = OBJECT
{ Datenfelder }
z,n : longint;
{ Methoden }
PROCEDURE gibaus; { hier nur Prozedurkopf }
END;
tsBruch = OBJECT(tBruch) { tsBruch erbt alles aus tBruch }
{ Datenfelder }
s : STRING;
{ Methoden }
PROCEDURE gibaus; { ererbte Methode wird überschrieben }
END;
PROCEDURE tBruch.gibaus;
BEGIN
write(z,'/',n) { Beachte die Verwendung der Objekt-Qualifizierer }
END;
PROCEDURE tsBruch.gibaus;
BEGIN
tBruch.gibaus; write(' '); write(s) { alte Methode wird erweitert }
END;
VAR
a : tBruch;
b : tsBruch;
BEGIN
a.z := -2; a.n := 307; b.z := 1; b.n := 7; b.s := 'Superbruch';
writeLn; a.gibaus; write(' + '); b.gibaus; writeLn; b.gibaus;
readLn
END.
|
Oft möchte man eine ererbte Methode in ihrer Funktionalität erweitern, ohne die bestehenden Teile neu programmieren zu wollen. Auch das ist wie in nebenstehendem Beispiel demonstriert möglich. |
PROGRAM OOP6; { Virtuelle Methoden , mit Init-Constructoren }
USES
Crt;
{$R+} { zur Bereichsüberprüfung, bei der Programmentwicklung einschalten! }
TYPE
tBruch = OBJECT
{ Datenfelder }
z,n : longint;
{ Methoden }
CONSTRUCTOR init(init_z,init_n : longint);
PROCEDURE gibaus; VIRTUAL;
PROCEDURE gib2xaus;
END;
tsBruch = OBJECT(tBruch) { tsBruch erbt alles aus tBruch }
{ Datenfelder }
s : STRING;
{ Methoden }
CONSTRUCTOR init(init_z,init_n : longint; init_s : STRING);
PROCEDURE gibaus; VIRTUAL;
END;
CONSTRUCTOR tBruch.init(init_z,init_n : longint);
BEGIN
z := init_z; n := init_n
END;
PROCEDURE tBruch.gibaus;
BEGIN
write(z,'/',n)
END;
PROCEDURE tsBruch.gibaus;
BEGIN
tBruch.gibaus; write(' '); write(s) { alte Methode wird erweitert }
END;
PROCEDURE tBruch.gib2xaus;
BEGIN
gibaus; write(' '); gibaus; { das "richtige" gibaus wird herausgesucht }
END;
CONSTRUCTOR tsBruch.init(init_z,init_n : longint; init_s : STRING);
BEGIN
z := init_z; n := init_n; s := init_s
END;
VAR
a : tBruch;
b : tsBruch;
BEGIN
ClrScr; a.init(-2,3); b.init(2,5,'bbb');
write('a = '); a.gibaus; write('; b = '); b.gibaus; writeLn;
writeLn; writeLn; a.gib2xaus; writeLn; b.gib2xaus; writeLn;
readLn
END.
|
Die in nebenstehendem Programm verwendete Methode 'gib2xaus' soll jeweils auf die Methode 'gibaus' zurückgreifen. Das Problem ist
nur, wie wird je nach Datentyp 'tBruch' bzw. 'tsBruch' das jeweils richtige 'gibaus' bestimmt. Dazu wird die Methode 'gibaus' als
'virtuell' erklärt und zur Laufzeit über sogenannte 'Konstruktoren' das jeweilige Objekt dem Compiler bekanntgemacht. Als Konstruktor
kann eine beliebige - auch leere - Methode dienen, in der Regel wird die Initialisierung dafür verwendet. Die Tatsache, dass eine Methode gleichen Namens für unterschiedliche Objekte in einer Vererbungshierarchie jeweils trotz unterschiedlicher Funktionalität richtig zugeordnet wird, nennt man "Polymorphie". |