Einige Gedanken zur objektorientierten ProgrammierungMärz 99, mk


Es sollen anhand einfacher TP7.0-Programme Grundideen und Möglichkeiten dargestellt werden.
Für Bemerkungen und Hinweise bin ich dankbar. Klaus.Merkert@t-online.de

Was ist ein "Objekt"?

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.
Was sind "Methoden"?

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.
Was ist "Vererbung"?

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.
Was bedeutet das Überschreiben einer Methode?

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.
Wie funktioniert "Vererben mit Erweitern der Methoden"?

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.
Was sind "virtuelle Methoden", was bedeutet "Polymorphie"?

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".


zurück zur HSG-Homepage