Seite 7 von 9

Verfasst: 25.10.2007 00:58
von X0r
>Ich habe eben mit meiner Erklärung geholfen
Nur leider war das NACH deinem ultimativen Flame-Beitrag.

>Merkst du eigentlich dass nun eigentlich nur du am Mosern bist ? ^^

Tia, wer hat denn auch schon Lust sich diese dumme Diskussion anzuhören?

Alles in allem: Ob Precompiler oder Parser. Hier wird warscheinlich nie ein 100% funktionierender PreCompiler kommen. Am besten ists immernoch, wenn Fred die Sache übernimmt.
Vielleicht sollte man mehr Zeit darin investieren, Fred dazuzu bringen, OOP einzubauen. Haben wir sicherlich alle mehr von und außerdem gibts dann auch weniger flame. Fred kannste ja nicht zur Sau machen. ;)

Verfasst: 25.10.2007 01:15
von Hroudtwolf
Och. Ich traue das schon einigen zu. Und Rings ist ja nun auch schon lange kein Neuling mehr.
Ich denke, es ist nur ne Sache der Planung. Und genau die sollten die jeinigen die sich zu diesem Projekt finden sollten, in ihrem Kreise, ungestört von evtl unqualifizierten Meinungen anderer tun.

Ohne diese Besonnenheit oder gar durch voreilige Schnellschüsse haben Projekte meist den Boden verloren bevor sie angefangen haben.

MfG

Wolf

Verfasst: 25.10.2007 01:20
von X0r
Und warum soll ich bitte ein Neuling sein?

Ich progge schon länger als 2 Jahre mit anderen Sprachen. Nur weil mein Anmeldedatum HIER so früh ist....

Verfasst: 25.10.2007 01:28
von Hroudtwolf
Es geht hier gerade gar nicht mehr um dich. (Obwohl das auch ein geiles Thema wär ^^ )

Verfasst: 25.10.2007 02:24
von #NULL
der klügere gibt nach. in dem fall hieße dass, einer macht einen neuen thread für seine sache auf.
nichts hindert euch daran, beide für was zu arbeiten :freak:

Verfasst: 25.10.2007 02:40
von X0r
Damit ich wieder nen dicken flame + neCompiler-Abbildung als feedback bekomme?

Edit: Gut, hat Spaß gemacht mit dem Parser. Das wars aber auch schon. Ich hätte so oder so keine lust mit wolf im team irgenwas zu machen.
Ich überlass das jetzt mal ihm, dem Profi.

Verfasst: 25.10.2007 10:11
von inc.
Ohne irgendeinen in inrgendeiner Weise persönlich anzusprechen, aber ...

Problem ist hier, dass einfach salopp drauf los programmiert wird, OHNE irgendein klares Konzept, OHNE eine genaue Ahnung von OOP, also WIE genau eine Klasse im Speicher angelegt ist. Das ganze geht hier im Thread einher mit einer feinen Priese Austragung von persönlichen Dingen über die Hälfte der hier vorliegenden Postings hinweg.

Bevor man anfängt einen OOP Parser zu programmieren sollte man neben dem Können in PB eine Tokenbasierte Routine anzuwenden auch mal in die OOP Materie eingetaucht sein. Eine sehr gute Information dazu gibt es hier: http://pb-lounge.pb-club.de/viewtopic.php?t=3459 ... wie sowas im Speicher aufgebaut ist.

@Xor
Warum hast du in der Class ... jenes unnütze "Declare" stehen?
Warum wird das nicht PB-like wie in PB's "Interface" von der Syntax her gehandhabt? Zudem wird nicht richtig der *this Zeiger übergeben.
HWolf will dich nicht angreifen, sondern versucht dir klarzumachen, dass solch ein Projekt nicht nur aus Programmieren besteht, sondern auch aus einer vorher durchdachten Struktur, bzw. einem Konzept, wie sowas normalerweise angegangen wird (siehe sein Mapping).

Ohne dich persönlich anzugehen und nicht deine Fähigkeit in PB zu progrmmieren in Frage zu stellen, aber ... wie kann es sein, dass du einen OOP Parser programmieren wilst und du hier und dort die Frage bzgl. deines Codes wiederholst: "... hat das hier jetzt was mit OOP zu tun?".

Verfasst: 25.10.2007 12:57
von X0r
Ich war verunsichert. Im englischen board meinte fsw, es wäre OOP, aber irgedwie nicht skalierbar oder so.


Zum Declare: Tia, genauso unnütz wäre es hier für mich, die Methodendefinitionen den Klassen zuzuordnen.

Verfasst: 25.10.2007 13:40
von inc.
Zum Declare: Tia, genauso unnütz wäre es hier für mich, die Methodendefinitionen den Klassen zuzuordnen.
Ist aber OOP. Hast es nur nicht verstanden.
Da Methoden Bestandteile einer Klasse sind, macht das wohl Sinn, und ist in anderen OOP Sprachen auch so angewendet.

Beispiel: Was machst du wenn du eine Methode Get() sowohl in der Klasse myObject hast als auch in der Klasse yourObject?? Klingelts? (ich rede jetzt hier nicht von Polymorphie, sondern vom schlichten Erstellen der Funktionspointer der entspr. virtuellen Tabellen der einzelnen Klassen.

Lese dich mal ein wenig in die OOP Ansätze ein, dann wirst du das verstehen. Sodann würdest du solch ein Argument, wie in deinem letzten Posting zu lesen, nicht anweden.

Verfasst: 25.10.2007 14:47
von H.Brill
Für Gedanken - anstöße habe ich mal einen Auszug der
XProfan - Hilfe gemacht. Vielleicht ist es nützlich.

Code: Alles auswählen

1: Klassen - Objekte - Eigenschaften - Methoden 

Wie ein Objekt mit Eigenschaften und Methoden beschrieben wird
 
Was ist ein Objekt? Nun, eigentlich alles: Ein Haus, ein Fernseher, ein Mensch, ein Hund, ein Fenster, eine Spielfigur, ein Programm, ein Dialogelement, eine Textzeile, ein Datum, ...
 
In der Programmierung wird ein solches Objekt als eine Sammlung aus Eigenschaften, auch Attribute genannt, und Methoden dargestellt. Ein Hund hat z.B. die Eigenschaften Geburtsdatum, Rasse, Farbe und Größe. Zu den Methoden gehören z.B. laufen, bellen, beißen und schwanzwedeln.
 
Was ist eine Klasse?
 
Es gibt natürlich schrecklich viele Hunde, die alle die genannten Eigenschaften und Methoden haben. Diese Beschreibung trifft auf alle Hunde zu. In der Programmierung nennt man diese Beschreibung eine Klasse. Die Klasse "Hund" beschreibt also welche Eigenschaften und Methoden ein Hund im Allgemeinen hat. Die Klasse kann daher als die Vorlage für die einzelnen Objekte betrachtet werden.
 
Klassen und Objekte in XProfan
 
In XProfan wird eine Klasse mit dem Befehl Class beschrieben:
 
  Class Hund = Geburtsdatum$(10), \
               Rasse$(32),        \
               Groesse%,          \
               bellen@
 
In diesem Beispiel habe ich die Eigenschaften und Methoden auf ein Minimum reduziert, um Tipparbeit zu sparen und es übersichtlicher zu machen. Zur Klassenbeschrebung gehört aber auch noch hinzu, wie die Methode "bellen" aussieht. Eine Methode ist nichts anderes als eine Funktion oder Prozedur, deren Name aus Klassenname und Methodenname mit einem Punkt verbunden besteht:
 
  Proc Hund.bellen
    Parameters text$
    Print text$
  EndProc
 
Hinweis: Bei Methodennamen wird das @ bei der Funktion nicht mitgeschrieben. Es dient bei der Definition der Klasse nur zur Kennzeichnung!
 
Wer schon länger mit Profan arbeitet, erkennt, dass eine Klassenbeschreibung einer Strukturbeschreibung ähnelt, mit dem Unterschied, dass Methoden hinzukommen.
 
Um jetzt von der Beschreibung zum Objekt zu kommen, benötigen wir zunächst eine Variable (Bezeichner), die für das neue Objekt steht. Wie bei Strukturen dient hierzu eine Bereichsvariable:
 
  Declare Waldi#
 
Noch kann diese Variable sehr vieles beschreiben, sei es nun ein Bereich, eine Struktur oder ein Objekt. Wie bei Bereichen und Strukturen kommt hier der DIM-Befehl zum Einsatz:
 
  Dim Waldi#, Hund
 
Und schon ist Waldi ein Hund. Technisch gesprochen: Das Objekt Waldi# ist eine Instanz der Klasse Hund. Waldi# hat die Eigenschaften und Methoden eines Hundes. Wir könnten nun beliebig viele weitere Hunde erzeugen. Nehmen wir also noch Bello# hinzu:
 
  Declare Bello#
  Dim Bello#, Hund
 
Da Bello# und Waldi# beides Hunde sind, haben sie die gleichen Eigenschaften und Methoden. Um Eigenschaften zuzuweisen und Methoden aufzurufen, verbinden wir Objektnamen und Eigenschaft, bzw. Methode mit einem Punkt:
 
  Waldi#.Geburtsdatum$ = "10.01.1997"
  Waldi#.Rasse$ = "Dackel"
  Waldi#.Groesse% = 35
 
Mit WITH können wir das etwas vereinfachen:
 
  With Bello#
    .Geburtsdatum$ = "23.09.1985"
    .Rasse$ = "Bernhardiner"
    .Groesse% = 65
  EndWith
 
Ebenso können wir nun auf diese Eigenschaften zugreifen und die Hunde bellen lassen:
 
  With Waldi#
    Print .Geburtsdatum$
    Print .Rasse$
    Print @Str$(.Groesse%) + " cm"
    .bellen("WauWau!")
  EndWith
 
  With Bello#
    Print .Geburtsdatum$
    Print .Rasse$
    Print @Str$(.Groesse%) + " cm"
    .bellen("Knurr Kläff!")
  EndWith
 
Zum Schluß bleibt uns vor dem Programmende nur das Aufräumen: Alle geDIMten Objekte sollten wieder mit Dispose aus dem Speicher entfernt werden:
 
  Dispose Waldi#
  Dispose Bello#
 
Wenn auch beim Programmende der vom Programm verwandte Speicher von Windows wieder freigegeben wird, so kann es doch bei lokalen Objekten in Prozeduren wichtig sein, den Speicher dieser Objekte vor Verlassen der Prozedur wieder freizugeben, damit das Programm nicht während des Betriebs ständig mehr Speicher verbraucht.
 
2: Kapselung - Klassen organisieren - Vererbung - Polymorphie 

Kapselung - private und geschützte Eigenschaften und Methoden
 
Ein Vorteil objektorientierter Programmierung ist die Kapselung von zusammengehörenden Daten (Eigenschaften) und Funktionen (Methoden) in einer Klasse. Der Nutzer einer Klasse muss nicht wissen, was in einer Klasse vor sich geht oder wie sie aufgebaut ist. Ja, es ist sogar denkbar, dass der Hersteller einer Klasse diese an neuere technische Gegebenheiten oder ein anderes Betriebssysten anpasst, ohne dass ein Nutzer derselben auch nur eine Programmzeile ändern müßte. Wenn eine graphische Klasse z.B. die Methode "zeichne" hat, muss der Nutzer nur wissen, dass mit dieser Methode das von dieser Klasse erzeugte Objekt ausgegeben wird. Wie das nun technisch geschieht, ist für ihn uninteressant. Dafür sorgen Funktionen innerhalb der Klasse. Eine Klasse hat neben den Eigenschaften und Funktionen, die von außerhalb genutzt werden oft auch Eigenschaften und Funktonen, die nur für die Klasse selbst von Bedeutung sind. Diese werden als "private" gekennzeichnet. Von UML (Unified Modelling Language) hat XProfan die Kennzeichnung einer solchen Methode/Eigenschaft mit einem vorangestelltem "-" übernommen. Eine als "private" gekennzeichnete Eigenschaft oder Methode kann aber auch nicht vererbt werden. Besonders bei Eigenschaften möchte man oft, dass sie zwar vererbt werden können, aber trotzdem kein direkter Zugriff von "außerhalb" möglich ist. Hier wäre "private" also zu sehr eingeschränkt. Dafür gibt es die Kennzeichnung als "protected" (geschützt) mit dem UML-Kennzeichen "#". Die so gekennzeichneten Eigenschaften und Methode sind in XProfan nur innerhalb der Klasse und ihrer Nachfahren verwendbar.
Öffentliche Methoden und Eigenschaften werden gemäß UML mit einem "+" bezeichnet. Auch das ist in XProfan erlaubt, aber nicht nötig, da Methoden und Eigenschaften ohne Kennzeichen immer als öffentlich betrachtet werden.
 
Oftmals werden in objektorientierten Programmen Eigenschaften grundsätzlich als "protected" gekennzeichnet und der Zugriff nur über sogenannte Setter- und Getter-Methoden erlaubt. Set = Setzen und Get = Holen einer Eigenschaft. Unsere Klasse Hund sähe dann also so aus:
 
  Class Hund = #Geburtsdatum$(10), \
               #Rasse$(32),        \
               #Groesse%,          \
               setGeburtsdatum@,   \
               getGeburtsdatum@,   \
               setRasse@,          \
               getRasse@,          \
               setGroesse@,        \
               getGroesse@,        \
               bellen@
 
Hinzu kommen natürlich noch die entsprechenden Methoden. Als Beispiel hier die Getter- und Settermethoden für das Geburtsdatum:
 
  Proc Hund.setGeburtsdatum
    Parameters Datum$
    .Geburtsdatum$ = Datum$
  EndProc
 
  Proc Hund.getGeburtsdatum
    Return .Geburtsdatum$
  EndProc
 
Es ist üblich, den Getter- und Settermethoden einen Namen zu geben, der dem Eigenschaftsnamen mit vorangestelltem set bzw. get entspricht, wobei der erste Buchstabe des Eigenschaftsnamens zur besseren Lesbarkeit groß geschrieben wird.
Der Zugriff auf die Eigenschaften und Methoden des Objektes erfolgt durch den Namen mit vorangestelltem Punkt. (In anderen Programmiersprachen wird stattdessen ein "self" vorangestellt.)
Das komplett angepaßte Programm "okurs2.prf".
 
Auf den ersten Blick sieht das ja nach gewaltig viel Zusatzarbeit aus. Wo ist da der Vorteil, außer der reinen Lehre von den Objekten Genüge zu tun? Zum einen ist der Mehraufwand nahezu ausschließlich beim Programmierer des Objektes. Im Programm, das das Objekt nutzt fällt es kaum ins Gewicht, wenn nun statt "bello#.groesse%" das unwesentlich längere "bello#.getGroesse()" steht. Und der Hauptvorteil liegt z.B. darin, dass nun nicht direkt die Eigenschaften beschrieben werden, sondern in den Getter- und Settermethoden noch weiterer Code eingebaut werden könnte, wie etwa Plausibilisierungen. So könnte überprüft werden, ob die Größe im passenden Bereich (hier 2-Byte-Integer) ist oder etwa zur Rasse paßt oder ob das Datum im richtigen Format ist, oder ... Die Klasse kann also völlig unabhängig vom Programm, das diese nutzt, verbessert und erweitert werden.
 
Klassen organisieren
 
Zur besseren Nutzung ist es empfehlenswert, je Klasse, oder Gruppe von zusammengehörigen Klassen, eine Include-Datei zu schreiben, die lediglich diese Klasse(n) enthält. Enthält die Includedatei eine Klasse, so sollte der Dateiname dem Klassennamen entsprechen. So teilen wir nun das Programm des letzten Abschnittes in die Klassendatei "hund.inc" und das Programm "okurs3.prf". Schließlich wollen wir ja von der Wiederverwertbarkeit der Klassen profitieren! Wenn wir jede Klasse in einer eigenen Datei haben, brauchen wir nur die Klassen einzubinden, die wir auch benötigen.
 
Wollten wir unsere genialen Klassen später anderen Programmierern zugänglich machen, ohne den Quellcode preiszugeben, so können wir auch aus den Includedateien recht einfach XProfan-Units machen, aber das ist ein anderes Thema ...
 
Vererbung
 
Ein wichtiger Vorteil der objektorientierten Programmierung ist die Vererbung. Das heißt, ich muss nicht jedes Mal das Rad neu erfinden, sondern kann auf Bewährtes zurückgreifen.Eine neue Klasse kann also alle Eigenschaften und Methoden einer bisherigen Klasse übernehmen und beliebig weitere Eigenschaften und Methoden hinzufügen.
 
Man könnte also z.B. eine Klasse "Lebewesen" schreiben, die diejenigen Eigenschaften und Methoden hat, die jedes Lebewesen auszeichnet. Eine neue Klasse "Tier" könnte nun all dieses erben, da ja jedes Tier ein Lebewesen ist. Ein Tier hat aber noich zusätzliche Eigenschaften und Methoden. Diese werden der Klasse "Tier" hinzugefügt. Jetzt könnte man sich eine weitere Klasse "Säugetier" denken, die alles von der Klasse "Tier" erbt und weitere Eigenschaften hinzufügt ...
 
Der Spieleprogrammierer könnte eine Klasse "Sprite" entwickeln, die alles kann, was Spielfiguren so können, z.B. auf dem Bildschirm angezeigt werden, sich bewegen, etc. Davon könnte er nun einzelne Klassen für die speziellen Objekte seines Spieles ableiten und müßte jeweils nur die neuen Eigenschaften und Methoden hinzufügen.
 
Zurück zu unserem Beispiel: Wir wollen eine Verwaltung unserer Hunde aufbauen und benötigen dazu neben den Eigenschaften und Methoden der Hunde noch weitere Eigenschaften und Methoden, nämlich den Namen des Besitzers und die entsprechenden Getter- und Setter-Methoden. Um eine Klasse zu erben, übernehmen wir den Namen dieser Klasse als ersten Eintrag in die Klassenbeschreibung:
 
  $I hund.inc
 
  Class hundekarte = hund,           \
                     #besitzer$(60), \
                     setBesitzer@,   \
                     getBesitzer@
 
Wichtig: Es kann nur von einer Klasse geerbt werden und diese muss an erster Stelle in der Klassenbeschreibung stehen!
 
Natürlich müssen wir auch noch noch die neuen Methoden schreiben:
 
  Proc hundekarte.setBesitzer
    Parameters name$
    .besitzer$ = name$
  EndProc
 
  Proc hundekarte.getBesitzer
    Return .besitzer$
  EndProc
 
Die Eigenschaften und Methoden der Klasse "hund" sind durch die Vererbung in "hundekarte" bereits vorhanden und müssen nicht neu geschrieben werden. Innerhalb der neuen Methoden können wir auf die nicht privaten Eigenschaften und Methoden von "hund" genauso zugreifen, als stünden sie in "hundekarte".
 
Jetzt können wir die neue Klasse als "hundekarte.inc" abspeichern. Mit der Includezeile "$I hund.inc" wird die Klasse "hund" eingebunden. Anschließend können wir die neue Klasse in unserem Programm verwenden:
 
'-Begin-----------------------------------------------------------------
 
  $I hundekarte.inc
 
  Declare Waldi#, Bello#
  Dim Waldi#,Hundekarte
  Dim Bello#,Hundekarte
 
  With Waldi#
    .setGeburtsdatum("10.01.1997")
    .setRasse("Dackel")
    .setGroesse(35)
    .setBesitzer("Klaus")
  EndWith
 
  With Bello#
    .setGeburtsdatum("23.09.1985")
    .setRasse("Bernhardiner")
    .setGroesse(65)
    .setBesitzer("Hugo")
  EndWith
 
  With Waldi#
    Print .getGeburtsdatum()
    Print .getRasse()
    Print @Str$(.getGroesse()) + " cm"
    Print .getBesitzer()
  EndWith
 
  With Bello#
    Print .getGeburtsdatum()
    Print .getRasse()
    Print @Str$(.getGroesse()) + " cm"
    Print .getBesitzer()
  EndWith
 
  WaitInput
 
  Dispose Waldi#
  Dispose Bello#
 
'-End-------------------------------------------------------------------
End
 
Listing: "okurs4.prf"
 
Polymorphie
 
Unter Polymorphie wird die Fähigkeit von Objekten verstanden, zur Laufzeit unterschiedliche Ausprägungen bzw. Formen annehmen zu können. D.h. dass der genaue Typ des übergebenen Parameters, an ein Objekt, zum Zeitpunkt der Kompilierung nicht bekannt ist.
 
Im folgenden Beispiel wird der Methode Out des Objektes Take ein anderes Objekt zur Verarbeitung übergeben.
 
'-Begin-----------------------------------------------------------------
 
  '-Klasse PrintOut---Gibt einen Text auf dem Drucker wieder------------
 
    Class PrintOut = DirectText@
 
      Proc PrintOut.DirectText
        Parameters Text$
        StartPrint
          DrawText 100, 100, Text$
        EndPrint
      EndProc
 
  '-Klasse PaintOut---Gibt einen Text auf dem Bildschirm wieder---------
 
    Class PaintOut = DirectText@
 
      Proc PaintOut.DirectText
        Parameters Text$
        DrawText 100, 100, Text$
      EndProc
 
  '-Klasse Take---Polymorphe Klasse-------------------------------------
 
    Class Take = Out@
 
      Proc Take.Out
        Parameters Object#, Text$
        Object#.DirectText(Text$)
      EndProc
 
  '-Objekte aus Klassen ableiten und erzeugen (instanzieren)------------
 
    Declare PrintOut#
    Dim PrintOut#, PrintOut
 
    Declare PaintOut#
    Dim PaintOut#, PaintOut
 
    Declare Take#
    Dim Take#, Take
 
  '-Main----------------------------------------------------------------
 
    Cls
 
    Take#.Out(PrintOut#, "Hello world on paper")
    Take#.Out(PaintOut#, "Hello world on screen")
 
    WaitInput
 
    Dispose PrintOut#
    Dispose PaintOut#
    Dispose Take#
 
'-End-------------------------------------------------------------------
End
 
Natürlich können, statt wie in diesem Beispiel gezeigt, auch Zeiger (Pointer) auf andere Variablentypen übergeben werden.
Ein erweitertes Beispiel ist im Listing Polymorphie zu finden.
 
Polymorphie mit virtuellen Methoden
 
XProfan bietet keine explizite Kennzeichnung und Unterstützung von virtuellen Methoden an. Hier soll aber kurz beschrieben werden, wie virtuelle Methoden, im eingeschränkten Rahmen, 'simuliert' werden können.
 
Hinweis: Die Verantwortung für den Umgang mit virtuellen Methoden liegt vollständig beim Programmierer. Besonders beim Umgang mit Units ist darauf zu achten und hinzuweisen.
 
Virtuelle Methoden müssen in jedem folgenden Objekt der Hierarchie überschrieben werden. Es ist eine Möglichkeit, um einer Funktion einen Namen zu geben, der in der gesamten Objekthierarchie verwendet werden kann, wobei jedes Objekt diese in einer für sie angebrachten Weise implementiert.
 
Man kann eine virtuelle Methode erzeugen, indem man die Methode in der Klassenbeschreibung definiert und eine Prozedur beschreibt, die lediglich eine Fehlermeldung ausgibt. Das folgende Beispiel zeigt diese Vorgehensweise:
 
  '-Klasse Out----------------------------------------------------------
 
    Class Out = DirectText@
 
      Proc Out.DirectText
        @Messagebox("Eine virtuelle Methode kann nicht " + \
          "aufgerufen werden!", "Fehler", 0)
      EndProc
 
  '-Klasse PrintOut-----------------------------------------------------
 
    Class PrintOut = Out
 
      Proc PrintOut.DirectText
        Parameters Text$
        StartPrint
          DrawText 100, 150, Text$
        EndPrint
      EndProc
 
  '-Klasse PaintOut-----------------------------------------------------
 
    Class PaintOut = Out
 
      Proc PaintOut.DirectText
        Parameters Text$
        DrawText 100, 150, Text$
      EndProc
 
Einen kleinen Haken hat die Sache: Es würde erst beim Ausführen des Programmes auffallen, wenn hier bei einem Objekt versäumt wurde, eine virtuelle Methode zu überschreiben. Schöner wäre es natürlich, wenn es schon dem Compiler auffiele 

3: Überschreiben - Überladen - Konstruktoren - Destruktoren 


Überschreiben
 
Ok, jetzt haben wir unsere Klasse "hundekarte", aber als Windowsfreak gefällt es uns absolut nicht, dass der Autor der Klasse Hund das Bellen einfach so mittels PRINT ausgibt. Eine Messagebox wäre natürlich angebrachter. Was also tun?
 
Natürlich könnten wir jetzt in unserer Klasse "hundekarte" eine neue Methode hinzufügen, etwa mBellen, aber dann müßten wir alle Programme unserer Verwaltung umschreiben, die schon die Methode "bellen" benutzen. Das kann es wohl nicht sein. Dazu brauche ich keine Objektorientierung.
 
Natürlich könnte man auch die Klasse "hund" umschreiben und die Methode "bellen" dort verändern. Aber wie es der Teufel will, gibt es schon haufenweise andere Programme, die diese Klasse verwenden und darauf angewiesen sind, dass die Ausgabe per PRINT im Textmodus erfolgt. Die sich durch eine derartige Änderung ergebenden Seiteneffekte wären unabsehbar. Verspricht nicht gerade die Objektorientierung derartige Seiteneffekte zu vermeiden?
 
Wir wählen also die dritte Möglichkeit, das sogenannte "Überschreiben": Wenn wir in einer Unterklasse eine Methode der übergeordneten Klasse neuschreiben wollen, geben wir dieser einfach den gleichen Namen, wie in der übergeordneten Klasse. Damit wird die gleichnamige Methode der übergeordneten Klasse überschrieben, so dass alle Instanzen der neuen Klasse die neue Methode verwenden. So sieht unsere Klasse jetzt also aus:
 
$I hund.inc
 
CLASS hundekarte = hund, \
                   #besitzer$(60), \
                   setBesitzer@, \
                   getBesitzer@, \
                   bellen@
 
PROC hundekarte.setBesitzer
  parameters name$
  .besitzer$ = name$
ENDPROC
 
PROC hundekarte.getBesitzer
  return .besitzer$
ENDPROC
 
PROC hundekarte.bellen
  parameters text$
  messagebox(text$,"Der Hund bellt:",48)
ENDPROC
 
Listing: "hund.inc"
 
Ganz nebenbei: Das "Überschreiben" funktioniert auch bei Eigenschaften! (Hier unterscheiden sich Objekte von Strukturen.) Würde ich z.B. etwas ausführlichere Rassenbeschreibungen benötigen, könnte ich z.B. einfach die Eigenschaft rasse$(64) hinzufügen und würde damit die kürzere Variante der übergeordneten Klasse überschreiben.
 
Als nächstes fällt uns noch ein, dass wir bei der Größenangabe mit "setGroesse" überprüfen wollten, dass diese auch im korrekten Bereich zwischen 20 und 120 cm ist. Kleinere und größere Hunde sind nicht zugelassen. Hier tut sich ein weiteres Problem auf. Wir wissen nicht mehr so genau, in welche Eigenschaft die ursprüngliche Klasse diese Eigenschaft speichert. Der Quellcode ist nicht verfügbar und Dokumentation gibt es auch keine. Was tun? Auch kein Problem: Wir rufen einfach in unserer Methode die gleichnamige Methode der übergeordneten Klasse auf, denn die weiß ja, was zu tun ist!
 
Die Klassenbeschreibung wird also um "setGroesse@" erweitert, die damit die gleichnamige Methode der übergeordneten Klasse überschreibt:
 
CLASS hundekarte = hund, \
                   #besitzer$(60), \
                   setBesitzer@, \
                   getBesitzer@, \
                   bellen@, \
                   setGroesse@
 
Und die Methodenprozedur wird geschrieben:
 
PROC hundekarte.setGroesse
  parameters wert%
  if wert% < 20
    wert% = 20
  elseif wert% > 120
    wert% = 120
  endif
  .super.setGroesse(wert%)
ENDPROC
 
Das ".super." vor dem Methodennamen führt dazu, dass die Methode der übergeordneten Klasse aufgerufen wird.
 
Hinweis: Das "super" ist an die Syntax von Java angelehnt und macht deutlich, dass hier die Superklasse, de übergeordnete Klasse aufgerufen wird. In Delphi wird hierzu der Befehl "inherited" verwandt.
 
Überladen
 
Mit "überladen" meint man, dass es mehrere Methoden desselben Namens in einer Klasse geben kann, die sich nur durch Typ oder Anzahl der Parameter unterscheiden. Das ist nicht in allen Sprachen möglich und in XProfan nur bedingt. Aber es geht:
 
Nehmen wir also an, wir wollen, um möglichst flexibel zu bleiben, das Bellen in unserer Klasse "hundekarte" so einrichten, dass bei einem Parameter die gewohnte Methode mit PRINT aufgerufen wird und bei zwei Parametern die Messagebox, wober der zweite Parameter dann die Überschrift darstellt. Zwei Methoden eines Namens können nicht definiert werden (bzw. wenn es denn doch geschieht wird immer nur die erste verwandt). Aber in dieser Methode kann ich abhängig von der Parameterzahl unterschiedliche Routinen aufrufen, was ja auf das Gleiche herauskommt. Der Schlüssel dazu ist die Systemvariable %PCount (nicht zu verwechseln mit %ParCount), die die Anzahl der Parameter des Methoden- bzw. Prozeduraufrufes angibt. So könnte es also realisiert werden:
 
PROC hundekarte.bellen
  if %pcount = 1
    parameters text$
    .super.bellen(text$)
  elseif %pcount = 2
    parameters text$,ueber$
    messagebox(text$,ueber$,48)
  endif
ENDPROC
 
Diese erweiterte Klasse "hundekarte.inc" und das erweiterte Programm "okurs5.prf".
 
Konstruktoren und Destruktoren
 
Ein Konstruktor ist eine Methode, die immer aufgerufen wird, wenn von einer Klasse eine Instanz (ein Objekt) erzeugt wird. Hier werden z.B. Eigenschafts-Variablen initialisiert und andere Grundeinstellungen vorgenommen. Sofern es nur um die Initialisierung der Eigenschaften geht, macht dies XProfan beim DIM automatisch. Wenn zur Initialisierung eines Objektes weiteres notwendig ist, muss man in XProfan eine entsprechende Methode schreiben. Damit XProfan eine Methode als Konstruktor erkennt, muß sie den gleichen Namen wie die Klasse haben. Wenn nun das Objekt nicht mit DIM, sondern mit @New erzeugt wird, wird diese Methode automatisch bei der Erstellung des Objektes ausgeführt. Natürlich kann diese Methode auch Parameter haben. Diese folgen dann dem Klassennamen im Aufruf der Funktion @New.
 
In unserem Beispiel könnte man sich einen Konstruktor vorstellen, der einem neu erzeugten Hund gleich die drei notwendigen Eigenschaften Geburtsdatum, Rasse und Größe zuweist:
 
  Class Hund = Geburtsdatum$(10), \
               Rasse$(32),        \
               Groesse%,          \
               hund@,             \
               bellen@
 
  Proc Hund.hund
    Parameters GebDat$, Rasse$, Gr%
    .Geburtsdatum$ = GebDat$
    .Rasse$ = Rasse$
    .Groesse% = Gr%
  EndProc
 
  <...>
 
Jetzt kann man das Erzeugen eines neuen Hundes deutlich vereinfachen. Mit einer Programmzeile wird das Objekt erzeugt und die drei Eigenschaften zugewiesen:
 
  Declare Waldi#, Bello#
  Waldi# = New(Hund,"10.01.1997","Dackel",35)
  Bello# = New(Hund,"23.09.1985","Bernhardiner",65)
 
 
Der Destruktor ist die Methode, die aufgerufen wird, wenn das Objekt beendet und sein Speicher freigegeben wird. Ebenso wie Java kennt XProfan keine Destruktoren. Die Speicherfreigabe erfolgt in XProfan mit dem Dispose eines Objektes (oder beim Ende des Programmes). Sind weitere "Aufräumarbeiten" notwendig, könnte man eine Methode "exit" hinzufügen, die man vor dem DISPOSE aufruft:
 
  meinObjekt#.exit()
  DISPOSE meinObjekt#
 
Hinweis: Auch in Java und C++ hat die Konstruktor-Methode immer den gleichen Namen wie die Klasse selbst und wird automatisch beim Anlegen des Objektes mit "new" ausgeführt. Diese Automatik gibt es auch Delphi, wobei hier jedoch bei der Methodendeclaration anstelle des Wortes "procedure" das Wort "constructor" steht. In Delphi gibt es auch Destruktoren, die durch das Wort "destructor" bezeichnet werden. Java kennt keine Destruktoren, da das Aufräumen des Speichers im Zuge einer "Garbage-Collection" automatisch geschieht. 


4: Statische Eigenschaften und Methoden - Klasseneigenschaften - Klassenmethoden 

 
Wie wir in den vorherigen Kursteilen gelernt haben, unterscheiden wir zwischen Klassen und Objekten:
Eine Klasse beschreibt alle Eigenschaften und Methoden eines Objektes. Wenn wir ein Objekt aus einer Klasse erstellen, so hat dieses Objekt alle Eigenschaften und Methoden dieser Klasse. Erstellen wir ein weiteres Objekt dieser Klasse, so hat es auch die gleichen Eigenschaften und Methoden. Es ist aber ein anderes, ein eigenständiges Objekt. Um in unserem Beispiel zu bleiben: Jeder Hund hat ein eigenes Alter, ein eigenes Bellen, eine eigene Rasse, etc.
 
Statische Eigenschaften = Klasseneigenschaften
 
Was aber, wenn uns jetzt die Anzahl der Hunde interessiert oder im zweiten Beispiel die Anzahl der Hunde-Karteikarten, die wir erzeugt haben? Auch dafür gibt es eine Lösung: eine statische Eigenschaft einer Klasse, oft auch Klassenvariable genannt. Um eine Klassenvariable zu erstellen, deklarieren wir sie genau wie eine globale Variable (was sie technisch gesehen auch ist) und setzen vor ihren Namen den Klassennamen, getrennt durch einen Punkt:
 
  Declare hund.Anzahl&
 
In JAVA würde eine Klassenvariable durch das Schlüsselwort "static" gekennzeichnet.
 
Auf diese Variable können wir nun zugreifen wie auf jede andere globale Variable auch:
 
  hund.Anzahl& = hund.Anzahl& + 1
  Print hund.Anzahl&
 
Die Klassenvariable kann natürlich nur mit dem Klassennamen verwandt werden. In den Objekten einer Klasse sind die Klassenvariablen nicht als Eigenschaften bekannt. Ein Zugriff auf waldi#.Anzahl& würde zu einer Fehlermeldung führen.
 
Hinweis: Klassenvariablen werden nicht vererbt! Eine Klassenvariable hundekarte.Anzahl& gibt es in unserem Beispiel also nicht.
 
Statische Methoden - Klassenmethoden
 
Es wäre sicher eleganter, das Hochzählen der Anzahl mit einer Methode zu lösen. Hier gibt es prinzipiell zwei Möglichkeiten. Da die Klassenvariablen technisch gesehen ja globale Variablen sind, kann natürlich in den Methoden der Objekte darauf zugegriffen werden. Es wäre also denkbar in die Klasse hund eine Methode weitererHund aufzunehmen, um diese Variable hochzuzählen:
 
  CLASS Hund = #Geburtsdatum$(10), \
               #Rasse$(32),        \
               #Groesse%,          \
               setGeburtsdatum@,   \
               getGeburtsdatum@,   \
               setRasse@,          \
               getRasse@,          \
               setGroesse@,        \
               getGroesse@,        \
               bellen@,            \
               weitererHund@
 
Die Methode sähe dann so aus:
 
  Proc hund.weitererHund
    hund.Anzahl& = hund.Anzahl& + 1
  EndProc
 
Da in dieser Methode aber auf keine Objekteigenschaften zugreift, können wir auf diese Methode auch "statisch" zugreifen, d.h. direkt über den Klassennamen. Folgende beiden Zeilen würden also identisch wirken:
 
  waldi#.weitererHund()
  hund.weitererHund()
 
Was passiert mit dieser Methode aber nun in Objekten der Klasse hundekarte, die ja direkt von hund abgeleitet ist?
 
  kartewaldi#.weitererHund()
  hundekarte.weitererHund()
 
Die erste Zeile funktioniert offensichtlich, die zweite aber nicht, da Klassenmethoden ebensowenig wie Klassenvariablen vererbt werden. Aber halt: Wenn Klassenvariablen nicht vererbt werden, was wird dann in der ersten Zeile hochgezählt?
 
Wer nicht aus dem Auge verloren hat, dass Klassenvariablen technisch gesehen globale Variablen sind, wird es wissen: hund.Anzahl& wird hochgezählt, was aber ziemlich sicher nicht gewünscht ist.
 
Um derartige Probleme zu vermeiden gibt es zwei Möglichkeiten: Zum einen sollte man Methoden, die auf Klassenvariablen schreibend zugreifen grundsätzlich als "private" kennzeichnen. Zum anderen kann man Methoden, die überhaupt nicht auf Objekteigenschaften oder andere Objektmethoden zurückgreifen, ausschließlich als Klassenmethoden definieren, indem man sie in der Klassenbeschreibung einfach wegläßt. Schließlich ist die Klassenmethode ebenso wie eine Klassenvariable schon durch ihren Namen der Klasse zugeordnet. Damit sähe unsere erweiterte Hundeklasse (hund.inc) nun so aus:
 
'-Begin-----------------------------------------------------------------
 
  Class Hund = #Geburtsdatum$(10), \
               #Rasse$(32),        \
               #Groesse%,          \
               setGeburtsdatum@,   \
               getGeburtsdatum@,   \
               setRasse@,          \
               getRasse@,          \
               setGroesse@,        \
               getGroesse@,        \
               bellen@
 
  Declare Hund.Anzahl&
 
  Proc Hund.setGeburtsdatum
    Parameters Datum$
    .Geburtsdatum$ = Datum$
  EndProc
 
  Proc Hund.getGeburtsdatum
    Return .Geburtsdatum$
  EndProc
 
  Proc Hund.setRasse
    Parameters Rasse$
    .Rasse$ = Rasse$
  EndProc
 
  Proc Hund.getRasse
    Return .Rasse$
  EndProc
 
  Proc Hund.setGroesse
    Parameters cm%
    .Groesse% = cm%
  EndProc
 
  Proc Hund.getGroesse
    Return .Groesse%
  EndProc
 
  Proc Hund.bellen
    Parameters text$
    Print text$
  EndProc
 
  Proc Hund.weitererHund
    Hund.Anzahl& = Hund.Anzahl& + 1
  EndProc
 
'-End-------------------------------------------------------------------
 
Hinweis: Wenn man einen Konstruktor verwendet (siehe vorherigen Kursteil) könnte man die Zeile, die Hund.Anzahl& um 1 hochzählt natürlich auch in diesem unterbringen, so dass die Anzahl beim Erzeugen eines Hundes automatisch hochgezählt wird.
 
Und dieses wäre das erweiterte Programm (okurs3a.prf):
 
'-Begin-----------------------------------------------------------------
 
  '-IncludeFiles--------------------------------------------------------
 
    $I hund.inc
 
  '---------------------------------------------------------------------
 
  Declare Waldi#
  Dim Waldi#,Hund
  Hund.weitererHund()
 
  Declare Bello#
  Dim Bello#,Hund
  Hund.weitererHund()
 
  Waldi#.setGeburtsdatum("10.01.1997")
  Waldi#.setRasse("Dackel")
  Waldi#.setGroesse(35)
 
  With Bello#
    .setGeburtsdatum("23.09.1985")
    .setRasse("Bernhardiner")
    .setGroesse(65)
  EndWith
 
  With Waldi#
    Print .getGeburtsdatum()
    Print .getRasse()
    Print @Str$(.getGroesse()) + " cm"
    .bellen("WauWau!")
  EndWith
 
  With Bello#
    Print .getGeburtsdatum()
    Print .getRasse()
    Print @Str$(.getGroesse()) + " cm"
    .bellen("Knurr Kläff!")
  EndWith
 
  Print "Anzahl der Hunde: " + @Str$(Hund.Anzahl&)
  WaitInput
 
'-End-------------------------------------------------------------------  
End
 
Noch einmal zur Erinnerung
 
Klasseneigenschaften und Klassenmethoden sind Eigenschaften (Variablen) und Methoden, die für die gesamte Klasse unabhängig von gebildeten Objekten gelten. Sie werden wie globale Variablen und Prozeduren bzw. Funktionen behandelt und aufgerufen. Sie werden nicht vererbt und tauchen daher auch nicht in der Klassenbeschreibung durch den Befehl Class auf.
 
Objekteigenschaften und Objektmethoden sind jene Eigenschaften und Methoden, die in der Klassenbeschreibung (Class = ...) der Klasse stehen, aus der das Objekt gebildet wurde. Diese können nur im Zusammenhang mit einem Objekt benutzt werden. Jedes Objekt, das von dieser Klasse gebildet wird, hat diese Eigenschaften und Methoden.
 
Objektmethoden, die nicht auf andere Objekteigenschaften oder -methoden zugreifen, können auch statisch, d.h. wie eine Klassenmethode aufgerufen werden.