[ Teil 1 ] [ Teil 2 ] [ Teil 3 ]

Programmieren mit Visual FoxPro, Teil 2

von Manfred Rätzmann

Der erste Teil der Artikelserie "Programmieren mit Visual FoxPro" befasste sich mit den Aspekten der Datenhaltung in Visual FoxPro. Im zweiten Teil werden wir nun die nächste große Neuerung unter die Lupe nehmen, die Visual FoxPro von seiner Vorgängerversion unterscheidet: die Unterstützung der objektorientierten Programmierung.

In seinem Buch "Objektorientierte Analyse und Design" sagt Grady Booch, daß eine Sprache nur dann als objektorientiert bezeichnet werden kann, wenn sie:

  1. Objekte als Datenabstraktion mit einer Schnittstelle für benannte Operationen und einem verborgenen internen Status unterstützt,
  2. Objekte als Instanzen von Klassen gebildet werden und
  3. Klassen Attribute von Oberklassen erben können

Diesen Forderungen entspricht die in Visual FoxPro verwendete erweiterte xBase Sprache. Auch die Forderung, die Hauptelemente eines Objektmodells, nämlich:

  • Abstraktion
  • Kapselung
  • Modularität und
  • Hierarchie

einfach in Programmcode umsetzen zu können, wird von Visual FoxPro erfüllt. Wie das im einzelnen realisiert ist und welche Möglichkeiten sich dem Entwickler dabei bieten, ist das Thema dieses Artikels. Visual FoxPro 3.0 bietet nicht nur die Möglichkeit der objektorientierten Programmierung an, sondern auch die benötigten Werkzeuge, um Klassen visuell zu erstellen und zu verwalten. Diese Werkzeuge, die FoxPro 3.0 zu "Visual" FoxPro machen, werden in diesem Artikel ebenfalls vorgestellt. Zunächst befassen wir uns jedoch mit der Objektorientierung auf der Programmcodeebene.

Die Basisklassen

Jede in Visual FoxPro (VFP) erstellte Klasse muß von einer der in VFP implementierten Basisklassen abgeleitet werden. Die Basisklassen werden unterschieden in visuelle und nicht-visuelle Klassen. Die visuellen Klassen sind die Elemente, aus denen die Anwendungsoberfläche zusammengesetzt werden kann. Die nicht-visuellen Klassen dienen zum einen zur Zusammenfassung und Gruppierung von Elementen der Anwendungsoberfläche, zum anderen zum Aufbau eigener Klassen der Verarbeitungsschicht einer Anwendung. Eine zweite Unterscheidung teilt die Basisklassen in Container und Nicht-Containerklassen ein. Containerklassen können Objekte anderer Klassen beinhalten. Wenn zur Laufzeit ein Objekt aus einer Containerklasse erstellt wird, wird für jede im Container enthaltene Klasse ebenfalls ein Objekt erstellt. Offensichtlichstes Beispiel für eine Containerklasse ist die Formularklasse FORM. Ein Formular (früher auch: Ein-/Ausgabemaske genannt) enthält diverse Elemente wie Eingabefelder, Befehlsschaltflächen, Listenfelder etc., die selbst wieder eigene Klassen darstellen. Wenn ein Formular durch den Anwender aufgerufen wird und am Bildschirm erscheint, so ist intern nicht nur ein Objekt der Klasse FORM erstellt worden, sondern für jedes im Formular enthaltene Element wurde ebenfalls ein Objekt erstellt.

Basisklassen in Visual FoxPro

Klassenname

Beschreibung

visuell?

Container?

CheckBox

Kontrollkästchen

Ja

Nein

Column

Spalte eines Datenblatts

Ja

Ja

ComboBox

Kombination aus Textfeld und Listenfeld

Ja

Nein

CommandButton

Befehlsschaltfläche

Ja

Nein

CommandGroup

Gruppe von Befehlsschaltflächen

Ja

Ja

Container

Verbund einzelner Steuerelemente, nicht gekapselt

Ja

Ja

Control

Verbund einzelner Steuerelemente, gekapselt

Ja

Ja

Cursor

Tabelle oder Ansicht in einer Datenumgebung

Nein

Nein

Custom

Basisklasse für freie Anwendungsklassen

Nein

Ja

DataEnvironment

Datenumgebung eines Formulars oder Reports

Nein

Ja

EditBox

Editierbox für längere Textfelder

Ja

Nein

Form

Formular, Daten Ein-/Ausgabemaske

Ja

Ja

FormSet

Formulargruppe

Nein

Ja

Grid

Datenblatt in Gitterdarstellung (wie BROWSE-Befehl)

Ja

Ja

Header

Kopf einer Datenblatt Spalte

Ja

Nein

Image

Abbildung

Ja

Nein

Label

Bezeichnungsfeld

Ja

Nein

Line

Linie

Ja

Nein

ListBox

Listenfeld

Ja

Nein

OLEBoundControl

an Tabellenfeld gebundenes OLE Steuerelement

Ja

Nein

OLEControl

OLE Steuerelement

Ja

Nein

OptionButton

Optionsfeld, nur in Optionsfeldgruppe möglich

Ja

Nein

OptionGroup

Optionsfeldgruppe

Ja

Ja

Page

Seite eines Formulars

Ja

Ja

PageFrame

Seitenrahmen zur Aufnahme von Seiten im Formular

Nein

Ja

Relation

Beziehung zweier Cursor in einer Datenumgebung

Nein

Nein

Separator

Zwischenraum für Symbolleisten

Ja

Nein

Shape

Rechteck, Kreis oder Ellipse

Ja

Nein

Spinner

numerisches Ein-/Ausgabefeld mit Intervallschaltern

Ja

Nein

TextBox

Ein-/Ausgabefeld für numerische- und Textdaten

Ja

Nein

Timer

Zeitgeber

Nein

Nein

Toolbar

Symbolleiste

Ja

Ja

 

Eigenschaften, Methoden und Ereignisse

Alle VFP Basisklassen stellen einen Satz von Eigenschaften und Methoden zur Verfügung. Eigenschaften sind Werte, die das Aussehen eines Objekts bestimmen oder seinen aktuellen Status wiedergeben. Eigenschaften können zur Designzeit oder zur Laufzeit eingestellt werden. So gibt es zum Beispiel bei allen visuellen Klassen die Eigenschaft VISIBLE, die auf .T. oder .F. gesetzt werden kann. Wird die VISIBLE Eigenschaft eines Objektes zur Laufzeit vom Programm auf .F. gesetzt, so ist dieses Objekt für den Anwender nicht mehr sichtbar. Das Programm kann aber nach wie vor auf das Objekt zugreifen. Wird eine abgeleitete Klasse nicht visuell mit Hilfe des Klassendesigner erstellt, sondern als Sourcecode formuliert, so erscheint die Festlegung einer Eigenschaft wie eine Variablendefinition. Eigenschaften verhalten sich dementsprechend genau wie Variablen. Insbesondere kann durch eine Zuweisung auch der Typ einer Eigenschaft geändert werden. Lebensdauer und Schutzmöglichkeiten unterscheiden Objekteigenschaften von Variablen.

Methoden sind Code-Stücke, die festlegen, wie ein Objekt auf eintretende Ereignisse reagieren soll bzw. wie es sich bei Aufruf dieser Methode verhalten soll. Wird eine Klasse im Sourcecode definiert, so erscheinen Methoden wie Funktionen oder Prozeduren. Genau wie diese, können Methoden Werte mit RETURN XYZ zurückgeben. Auch Aufrufparameter können übergeben werden. Bei der Frage, ob Aufrufparameter als VALUE oder REFERENCE übergeben werden, gibt es allerdings einen kleinen Unterschied zwischen Methoden auf der einen Seite und Funktionen / Prozeduren auf der anderen Seite. Wenn keine Einstellung von SET UDFPARMS vorgenommen wird, so werden Parameter an Funktionen BY VALUE übergeben und an Prozeduren BY REFERENCE. An Methoden werden Parameter in diesem Fall immer BY VALUE übergeben, gleichgültig, ob die Methode bei der Klassendefinition als FUNCTION oder PROCEDURE aufgebaut wurde. Sobald der Wert von UDFPARMS jedoch explizit gesetzt wird, reagieren auch Methoden so, wie von Funktionen und Prozeduren bekannt. Das Erzwingen einer Parameterübergabe BY VALUE durch Setzen von Klammern, (Beispiel: do DiesUndDas with (Par1) ) bzw. BY REFERENCE durch den vorangestellten Klammeraffen, (Beispiel: = DiesUndDas(@Par1) ) funktioniert bei Methoden ebenfalls.

Ereignisse treten ein auf Grund von Aktionen des Anwenders oder durch bestimmte Systemzustände. So tritt das CLICK-Ereigniss für ein Objekt ein, wenn der Anwender das Objekt anklickt und das TIMER-Ereignis des Zeitgebers tritt ein, wenn eine in der INTERVAL-Eigenschaft festgelegte Anzahl von Millisekunden verstrichen ist. Ereignisse sind mit ihren zuständigen Methoden fest verknüpft. Die Methoden tragen auch den gleichen Namen wie das Ereignis, auf das sie reagieren. Deshalb ist es nicht immer ganz einfach, Ereignisse und Methoden gedanklich auseinanderzuhalten. Methoden können zu abgeleiteten Klassen in beliebiger Zahl hinzugefügt werden, Ereignisse, auf die reagiert werden soll, jedoch nicht.

Es gibt viele Methoden, die nicht mit einem Ereignis verknüpft sind. Diese Methoden werden nur ausgeführt, indem sie explizit aus dem Programm heraus aufgerufen werden. Auch die Methoden, die direkt mit einem Ereignis verbunden sind, können aufgerufen werden. Im Grunde ist der Aufruf also das Ereignis, auf das alle Methoden eines Objekts reagieren. In anderen OO-Sprachen heißt es deshalb auch nicht "Aufruf einer Methode" sondern "Senden einer Nachricht" an ein Objekt.

Abgeleitete Klassen, Vererbung, Polymorphie

Aus den VFP Basisklassen lassen sich eigene Klassen ableiten. Dies geschieht mit dem Befehl: DEFINE CLASS <Name der abgeleiteten Klasse> AS <Name der Oberklasse>. Die abgeleiteten Klassen erben dabei alle Eigenschaften und Methoden der Oberklasse und können diesen neue Eigenschaften und Methoden hinzufügen. VFP ermöglicht die einfache Vererbung, jede Klasse hat also genau eine Oberklasse. Eine mehrfache Vererbung, bei der eine Klasse Methoden und/oder Eigenschaften aus mehreren Oberklassen erben kann, wird nicht unterstützt. Das ist allerdings kein großes Manko. Mehrfachvererbung ist ein in der OO-Gemeinde umstrittenes Konzept. Die Notwendigkeit der Mehrfachvererbung (und häufig auch der einfachen Vererbung) läßt sich durch Objektbeziehungen wie "Enthalten-Sein" (Aggregation, Containment) oder "Verwenden" (Assoziation) vermeiden. Wer zu diesem Thema mehr wissen will, für den ist das Buch "Design Patterns" von Erich Gamma und anderen eine geeignete Lektüre.

Die ererbten Eigenschaften und Methoden können in den Unterklassen geändert werden. Neue Eigenschaften und Methoden können in der abgeleiteten Klasse hinzugefügt werden. Der Wert einer ererbten Eigenschaft wird einfach durch eine neue Zuweisung geändert. Die Methode einer Oberklasse wird durch die Formulierung einer eigenen Methode in der Unterklasse überschrieben. Bei Methoden, die aus einer Basisklasse geerbt wurden, gilt dies allerdings nur für den in der Oberklasse zur Standardmethode hinzugefügten Code. Das Standardverhalten der Basisklasse kann durch Angabe von NODEFAULT unterdrückt werden. Soll die Methode der Oberklasse nicht überschrieben, sondern ergänzt werden, so kann mit dem Bereichsauflösungsoperator (::) an einer beliebigen Stellen der neuen Methode die Methode der Oberklasse aufgerufen werden. So könnte in einer Klasse KUNDE, die von der Oberklasse DATENSATZ abgeleitet wurde, die Methode SPEICHERN wie folgt ergänzt werden:

    DEFINE CLASS Kunde AS Datensatz
        FUNCTION Speichern
     
            * Befehle vor dem Speichern des Kunden
     
            * Aufruf der Methode der Oberklasse
            datensatz::speichern
     
            * Befehle nach dem Speichern
     
        ENDFUNC
    ENDDEFINE

Die Methode Speichern der Klasse Kunde kann also gänzlich andere Dinge ausführen, als die Methode Speichern einer Klasse Artikel. Beide Methoden können sich dabei der Speichern-Methode aus Datensatz bedienen. Diese Möglichkeit, unter dem gleichen Namen klassenspezifisch andere Dinge tun zu können, nennt man "Polymorphie". Von "mehrfacher Polymorphie" spricht man, wenn der Inhalt einer Methode nicht nur von einem Parameter - hier der Frage, ob Kunde oder Artikel - sondern von weiteren Parametern abhängt. Angenommen, das Speichern sei auch abhängig davon, ob in einer lokalen (FoxPro eigenen) oder einer Remote (über ODBC verknüpft) Tabelle gespeichert wird. Dann könnte man den Weg gehen, zunächst eine Klasse Tabelle zu definieren und davon die Klassen LokaleTabelle und RemoteTabelle ableiten. Die Klasse Tabelle beinhaltet eine Methode Speichern, die eventuell leer ist, da sie komplett von den abgeleiteten Klassen LokaleTabelle und RemoteTabelle aufgebaut wird. Die Methode Speichern aus Datensatz (und damit auch aus Kunde) erwartet nun als Aufrufparameter ein Tabellenobjekt und leitet die Speichern-Anforderung an das Tabellenobjekt weiter.

    FUNCTION Speichern(Tabelle)
        * eventuell tabellenunabhängige Verarbeitung
        .
        .
        * weiterleiten an das Tabellenobjekt
        Tabelle.Speichern()
        .
        .
    ENDFUNC
Vor dem Speichern eines Kunden wird im Programmablauf das Tabellenobjekt erstellt. Dieses Tabellenobjekt wird an die Speichern-Methode übergeben:
    * Tabellenobjekt erstellen
    goKundenTabelle = CREATEOBJECT(“LokaleTabelle“)
    .
    .
    * Kundenobjekt aufbauen
    goKunde = CREATEOBJECT(“Kunde“)
    .
    * Kundendaten speichern
    goKunde.Speichern(goKundenTabelle)

Zugriff auf Objekte

Objekte werden in VFP mit der Funktion CREATEOBJECT(Klassenname[,Par1,Par2,...]) erstellt. Der erste Parameter ist der Name der Klasse des Objekts, die weiteren Parameter werden an die INIT-Methode des Objektes übergeben. CREATEOBJECT() liefert einen Zeiger auf das Objekt zurück, der in einer VFP-Variablen gespeichert wird. Diese Variable bildet die Objektreferenz. Durch einfache Wertzuweisung kann die Objektreferenz in einer weiteren Variablen gespeichert werden. Variablen, die eine Objektreferenz enthalten, sind vom Typ "O". Auf eine Eigenschaft oder Methode des so erstellten Objekts kann mit der Punktnotation zugegriffen werden, das heißt, an den Variablennamen der Objektreferenz wird der Name der Eigenschaft oder Methode durch Punkt getrennt angehängt:

<Variable> = <Objektreferenz>.Eigenschaft überträgt den aktuellen Wert der Eigenschaft des referenzierten Objekts in die Zielvariable.

<Variable> = <Objektreferenz>.Methode() überträgt den Rückgabewert der aufgerufenen Methode in die Zielvariable.

Auch <Objektreferenz>.Methode kann als Aufruf einer Methode direkt angegeben werden, ein eventueller Rückgabewert der Methode wird dabei nicht gespeichert.

Ein Beispiel: Angenommen, es existiert eine Klasse KUNDE mit einer Eigenschaft Name und einer Methode OpoSumme sowie der Möglichkeit, bei der Instantiierung eines Objektes gleich die gewünschte Kundennummer zu übergeben. Dann ließe sich folgendes Programmstück formulieren:

    * gewünschte Kundennummer festlegen, z.B. durch Eingabe des Anwenders
    KdNr = 4711
    * Kunden-Objekt aufbauen
    loKunde = CREATEOBJECT(“KUNDE“,KdNr)
    * Zugriff auf den Namen des Kunden
    lcName = loKunde.Name
    * Zugriff auf offene-Posten Summe über Methode
    if  loKunde.OpoSumme() > 10000
        * bösen Brief schreiben
    else
        * abwarten
    endif
    * Kunden-Objekt wieder freigeben
    release loKunde

Ein Objekt lebt solange, wie eine Objektreferenz dazu existiert. Wenn die letzte Variable freigegeben wird (durch ein RELEASE oder durch implizite Freigabe am Ende einer Prozedur), die eine Referenz auf das Objekt enthält, so wird auch das Objekt freigegeben. Dabei wird die DESTROY-Methode des Objekts aufgerufen.

Schützen von Eigenschaften und Methoden

Beim Aufbau der Methoden einer Klasse werden häufig interne Methoden und Eigenschaften benötigt, auf die von außen nicht zugegriffen werden soll. Solche internen Methoden und Eigenschaften lassen sich durch Angabe von PROTECTED abkapseln. Für Eigenschaften erfolgt dies durch eine gesonderte Deklaration, geschützte Methoden werden als PROTECTED FUNCTION oder PROTECTED PROCEDURE angegeben.

    DEFINE CLASS Datensatz AS Custom
        PROTECTED nRecNo, lNeu
        .
        .
        .
        PROTECTED FUNCTION WerteEinlesen
        .
        .
        ENDFUNC
    ENDDEFINE

In diesem Beispiel sind die Eigenschaften nRecNo und lNeu gegen einen Zugriff von außerhalb des Objekts geschützt. Die Funktion WerteEinlesen kann ebenfalls nur innerhalb der Methoden des Objektes aufgerufen werden. Wie weiter oben bereits gesagt, erben abgeleitete Klassen alle Eigenschaften und Methoden der Oberklasse. Das gilt auch für Eigenschaften und Methoden, die in der Oberklasse geschützt wurden.

Zugriff auf Objekte in Containern

Wie oben beschrieben, sind einige der VFP Basisklassen als Containerklassen ausgebildet, das heißt, sie können Objekte anderer Klassen enthalten. Jede Containerklasse kennt die Methode AddObject, mit der zur Laufzeit Objekte dem Container hinzugefügt werden können. Sollen bereits beim Entwurf der Klasse Objekte in diese eingebettet werden, so ist das über eine Zusatzklausel des DEFINE CLASS Statements möglich:

    DEFINE CLASS Kunde AS Datensatz
        ADD OBJECT Anschrift AS Adresse
        .
        .
    ENDDEFINE

In diesem Beispiel wird die Klasse Kunde aus der Oberklasse Datensatz abgeleitet, die auf Custom basiert. Mit der Klausel ADD OBJECT wird ein Objekt der Klasse Adresse hinzugefügt, das über Kunde.Anschrift angesprochen werden kann. Objekte in Containern können durch Angabe von PROTECTED gegen den Zugriff von außen geschützt werden. Ebenso können durch Angabe von WITH ... Eigenschaften des hinzugefügten Objekts eingestellt werden. Das folgende Codestück erstellt eine Klasse txtInPlus aus der Basisklasse Container. In txtInPlus werden ein LABEL-Objekt und ein TEXTBOX-Objekt eingebettet, deren Eigenschaften eingestellt werden. In der INIT-Prozedur des LABEL-Objekts wird dessen Eigenschaft CAPTION mit der Überschrift gefüllt, die im Datenbankcontainer (DBC) für das Feld hinterlegt ist, das als Datenquelle des TEXTBOX-Objektes festgelegt wird. Die Festlegung der Datenquelle erfolgt nicht in der Klassendefinition von txtInPlus sondern beim Ablegen eines txtInPlus-Objektes in einem Formular. Das ganze dient dazu, Ein/Ausgabefelder im Formular automatisch mit der im DBC hinterlegten Überschrift für das jeweilige Feld zu verbinden. Bei Änderung der Überschrift im DBC wird in allen mit txtInPlus erstellten Formularen beim nächsten Aufruf des Formulars die neue Überschrift angezeigt.

    DEFINE CLASS txtInPlus AS Container
          Width = 415
          Height = 20
          BackStyle = 0
          BorderWidth = 0
          Name = "txtinplus"
          ADD OBJECT PROTECTED label1 AS label WITH ;
                FontSize = 8, ;
                Alignment = 1, ;
                BackStyle = 0, ;
                Caption = "Label1", ;
                Height = 14, ;
                Left = 4, ;
                Top = 3, ;
                Width = 169, ;
                Name = "Label1"
          ADD OBJECT text1 AS textbox WITH ;
                FontBold = .F., ;
                FontName = "Courier New", ;
                FontSize = 8, ;
                Height = 20, ;
                Left = 180, ;

In der INIT-Prozedur von Label1 sehen Sie, wie Objekte in ihren Methoden auf sich selbst, den sie umgebenden Container und auf andere Objekte dieses Container referenzieren können. Mit dem Schlüsselwort THIS greift ein Objekt auf eigene Methoden oder Eigenschaften zu. PARENT ist die Referenz auf den umgebenden Container. THIS.PARENT.Text1.ControlSource ist demnach zu lesen als die Eigenschaft ControlSource des Objektes Text1, das sich im Container befindet, der das aktuelle Objekt umgibt. Mit THISFORM und THISFORMSET kann genauso auf das Formular oder den Formularsatz referenziert werden, das bzw. der das aktuelle Objekt enthält. Wenn ein Objekt der Klasse txtInPlus in ein Formular übernommen wird, so kann auf das in txtInPlus enthaltene TEXTBOX-Objekt von außen zugegriffen werden, nicht aber auf das LABEL-Objekt, da dieses durch Angabe von PROTECTED geschützt ist.

Assoziierte Objekte

Container können Objekte enthalten. Objekte, die keine Container sind, können sich ebenfalls der Dienste anderer Objekte versichern, indem sie eine Referenz auf das entsprechende Objekt in einer ihrer Eigenschaften ablegen. Diese Beziehung zwischen zwei Objekten nennt man Assoziation. Auch dazu ein Beispiel im Sourcecode:

    DEFINE CLASS Uhr AS Timer
          Interval = 10000
          Ausgabe = ""
          PROCEDURE Init
                THIS.Ausgabe = CREATEOBJECT("Meldung")
          ENDPROC
          PROCEDURE Timer
                THIS.Ausgabe.Ausgeben
          ENDPROC
    ENDDEFINE
     
    DEFINE CLASS Meldung AS Custom
          PROCEDURE Ausgeben
                WAIT WINDOW "das ist eine Meldung" timeout 1
          ENDPROC
    ENDDEFINE

Die TIMER-Klasse ist keine Containerklasse. Ein Objekt, das auf TIMER basiert, kann dennoch ein anderes Objekt zur Ausgabe seiner Meldungen heranziehen. Dazu definiert die von TIMER abgeleitete Klasse Uhr eine Eigenschaft Ausgabe, die eine Objektreferenz auf die Meldung-Klasse aufnehmen soll. In der INIT-Prozedur von Uhr wird ein Objekt der Klasse Meldung erstellt und dessen Referenz in Ausgabe abgelegt. Ein Objekt der Klasse Uhr, das zum Beispiel mit oRef = CREATEOBJECT("Uhr") erstellt wird, gibt nun alle 10 Sekunden durch Aufruf der Methode THIS.Ausgabe.Ausgeben die Meldung aus. Da die Eigenschaft Ausgabe nicht geschützt ist, kann auch von außerhalb (zum Beispiel aus dem Befehlsfenster) durch oRef.Ausgabe.Ausgeben die Methode Ausgeben des assoziierten Objekts aufgerufen werden. Die Referenz auf assoziierte Objekte unterscheidet sich also nicht von der Referenz auf in Containern enthaltene Objekte.

weitergehende OO-Konzepte

Der Befehlssatz von VFP enthält viele neue Befehle, die der Identifizierung und Manipulation von Objekten zur Laufzeit dienen. Diese Befehle und die Eigenschaften Class, BaseClass und ParentClass, die allen Objekten eigen sind, ermöglichen es, viele der klassischen Design Patterns auf einfache Weise in VFP zu implementieren. So kann eine abstrakte Klasse verhindern, daß Objekte aus ihr direkt erstellt werden, indem sie die Eigenschaft Class in der INIT-Methode mit dem eigenen Klassennamen vergleicht und bei Gleichheit .F. zurückgibt. Eine Singleton-Klasse kann abprüfen, ob bereits ein Objekt aus ihr erstellt ist und die Erstellung eines zweiten ebenfalls mit .F. aus der INIT-Methode ablehnen. Dazu benutzt sie die Funktion AINSTANCE(), die in ein Array alle Objektreferenzen zu Instanzen einer angegeben Klasse einträgt und die Anzahl der Instanzen zurück liefert. Eine eingehende Erörterung der neuen Befehle und der Möglichkeiten, weitergehende OO-Konzepte zu verwirklichen, würde den jedoch Rahmen dieses Artikels sprengen und muß einem eigenen Artikel zu diesem Thema vorbehalten bleiben.

Der Klassendesigner

Das wichtigste Werkzeug zur visuellen Erstellung und Pflege von Klassen in VFP ist der Klassendesigner. Bild 1 zeigt die oben im Sourcecode vorgestellte Klasse txtInPlus im Klassendesigner von Visual FoxPro.

Bild 1: Der VFP Klassendesigner mit der txtInPlus Klasse

Im linken Teil des Bildes sehen Sie oben den Layoutbereich, der die Klasse als Objekt, wie sie in einem Formular erscheint, darstellt. Für nicht-visuelle Klassen, wie Custom, wird im Layoutbereich des Klassendesigners lediglich ein Symbol ausgegeben. Der Container, der Label1 und Text1 enthält, hat die Eigenschaften BackStyle = 0 (Transparent) und BorderWidth = 0, sodaß er im Zielformular nicht sichtbar ist. Unter dem Layoutbereich ist das Codefenster zu sehen, das hier den Initialisierungscode von Label1 anzeigt. Im Codefenster kann mit Hilfe der DropDown-Listboxen "Objekt" und "Prozedur" schnell zwischen allen Methoden der bearbeiteten Klasse gewechselt werden. Beim Verlassen einer Methode wird der hinterlegte Sourcecode sofort kompiliert und Fehler im Sourcecode werden angezeigt. Fehlerhafter, oder besser: nicht kompilierbarer Code kann also gar nicht erst gespeichert werden.

Im rechten Teil von Bild 1 ist das Eigenschaftenfenster angezeigt. Durch einen Mausklick auf ein Objekt im Layoutbereich wird auch die Anzeige des Eigenschaftenfensters auf dieses Objekt gestellt. Das Eigenschaftenfenster ist in fünf Seiten aufgeteilt, die entweder alle Eigenschaften, nur Daten- oder nur Layout-bezogene Eigenschaften oder nur die Methoden eines Objekts anzeigen. Auf der Seite "Andere" werden zum Beispiel der Klassenname, Oberklasse und Basisklasse ausgegeben. Dort finden Sie auch alle Eigenschaften und Methoden wieder, die Sie einer Basisklasse hinzugefügt haben. Die Symbolleisten und alle Fenster des Klassendesigners können selbstverständlich frei plaziert oder weggeschaltet werden, sodaß jeder Entwickler sich seinen Arbeitsbildschirm nach seinen Bedürfnissen einrichten kann.

Den Klassendesigner können Sie aus dem VFP Befehlsfenster mit CREATE CLASS oder MODIFY CLASS aufrufen, wobei der Klassenname, die Oberklasse, deren Klassenbibliothek und die Bibliothek, in der die neue Klasse gespeichert werden soll, mit angegeben werden können. Fehlende Angaben dazu werden von Visual FoxPro vor dem Start des Klassendesigners abgefragt. Beim Aufruf des Klassendesigners wird dem Menü der Punkt "Klasse" hinzugefügt. Über diesen Menüpunkt können Sie Eigenschaften und Methoden hinzufügen oder wieder löschen. Hier können Sie auch für jede Eigenschaft und Methode das PROTECTED-Flag setzen, eine INCLUDE-Datei angeben, sowie Symbole für die Symbolleiste der Klassenbibliothek und zur Darstellung der Klasse in einem Container festlegen.

Der Klassenkatalog

Der Klassenkatalog dient zur Verwaltung der Klassenbibliotheken. Der Klassenkatalog ist selbst eine VFP Applikation, deren Aufruf beim Start von Visual FoxPro dem Menüpunkt "Extras" hinzugefügt wird. Sollte dies nicht der Fall sein, weil zum Beispiel ein anderes Startprogramm verwendet wird, kann der Klassenkatalog auch mit do (_BROWSER) aufgerufen werden. Bild 2 zeigt den VFP Klassenkatalog mit einer Bibliothek aus der Beispielanwendung zu Visual FoxPro.

Bild 2: Der VFP Klassenkatalog

Der Klassenkatalog zeigt die aufgerufene Bibliothek mit Hilfe eines Outline-OCX in einer Baumstruktur an. Dieses und einige andere OCXe werden übrigens bei Visual FoxPro mitgeliefert, sodaß sie auch in eigenen Anwendungen benutzt werden können. Mit Hilfe des Klassenkatalogs können Sie neue Klassen erstellen oder nicht mehr benötigte Klassen entfernen. Sie können Klassen umbenennen oder die Zuordnung einer Klasse zu ihrer Oberklasse verändern. Wenn Sie eine Klasse aus einer Klassenbibliothek in eine andere Klassenbibliothek umspeichern wollen, so öffnen Sie den Klassenkatalog zweifach. Die erste Instanz des Klassenkatalogs öffnen Sie mit der Quellbibliothek, die zweite mit der Zielbibliothek. Markieren Sie nun mit einem einfachen Mausklick die umzusetzende Klasse. Oberhalb der Klassenliste wird daraufhin das Symbol für die ausgewählte Klasse angezeigt. Ziehen Sie dieses Symbol,das nun von einem kleinen Rahmen umgeben sein sollte, auf die gleiche Stelle im Klassenkatalog der Zielbibliothek. Dabei ist es unerheblich, welches Symbol dort gerade angezeigt wird. Lassen Sie das Symbol dort fallen, dadurch wird die Klasse aus der Quellbibliothek in die Zielbibliothek kopiert. Wenn Sie die Klasse in der Quellbibliothek nicht mehr brauchen, können Sie sie dort löschen. Dieser Drag&Drop Mechanismus funktioniert im übrigen auch zwischen Klassenkatalog und Formular.

Aufbau eigener Klassenbibliotheken

Der Aufbau eigener Klassenbibliotheken sollte immer damit beginnen, daß alle VFP Basisklassen abgeleitet und die daraus resultierenden einfachen Kopien der Basisklassen in einer Bibliothek zusammengefasst werden. Alle weiteren Klassen, die Sie nun erstellen, sollten als Oberklasse nicht eine Basisklasse, sondern Ihre Kopie der Basisklasse haben. Der Grund dafür ist, daß Sie an den VFP Basisklassen nichts ändern oder einstellen können, an Ihren Kopien der Basisklassen aber sehr wohl. Sollte sich also im Laufe der Programmentwicklung herausstellen, daß alle Formulare Ihrer Anwendung eine bestimmte Eigenschaft oder Methode bräuchten, so fügen Sie diese Eigenschaft oder Methode Ihrer Kopie der Basisklasse FORM hinzu. Damit steht Sie allen Formularen der Anwendung zur Verfügung.

Ausgehend von Ihrer Bibliothek der Basisklassenkopien können Sie nun weitere Bibliotheken planen, die die zu erstellenden Klassen thematisch aufteilen. Wenn Sie nicht nur eine Anwendung erstellen wollen, sondern mehrere, die sich einen Stamm von Klassenbibliotheken teilen sollen, empfiehlt es sich, von den thematisch

 

aufgeteilten Bibliotheken anwendungsspezifische Bibliotheken abzuleiten. Dabei wird es selten nötig sein, mehr als eine Bibliothek pro Anwendung aufzubauen. Damit ergibt sich ein einfaches Schema für die Organisation eigener Bibliotheken:

Einer Formularklasse kann keine Datenumgebung mitgegeben werden. Die Datenumgebung wird von Visual FoxPro dem Formular erst im Formulardesigner hinzugefügt. Obwohl es möglich ist, Datenumgebungsklassen per Sourcecode zu definieren und zu instantiieren, ist es einfacher, den von VFP vorgesehenen Weg zu nehmen und die im Formulardesigner erstellten Formulare nicht als Klassen sondern in SCX-Dateien zu speichern. Die Formularklassen enthalten im obigen Schema enthalten also nur die Grundmuster für Stammdatenformulare, Verzeichnis- oder Belegformulare.

Wie geht's weiter?

Nach dem Kennenlernen der neuen Datenbankmöglichkeiten von Visual FoxPro im ersten Teil dieser Serie haben wir nun den zweiten großen Schwerpunkt von VFP, die objektorientierte Programmierung, betrachtet. Lassen Sie sich von der Themenfolge dazu verleiten, auch Ihre VFP-Anwendungen in dieser Reihenfolge zu entwickeln. Legen Sie also zunächst das Datenmodell fest, bauen Sie darauf basierend ein Objektmodell Ihrer Anwendung auf und versehen Sie das ganze dann mit einer bedienerfreundlichen Oberfläche.

[ Teil 1 ] [ Teil 2 ] [ Teil 3 ]