[ 1 ] [ 2 ] 

Vererbung von Verhalten - eine Einbahn-Straße als Sackgasse?

Als erster Aspekt bei der Vererbung wurde die effektive, an zentraler Stelle vorzunehmende Veränderung des "Aussehens von Objekten" behandelt. Nach der Diskussion über die unsichtbaren Objekte rückt allerdings die Einflußnahme auf das "Verhalten von Objekten" wesentlich mehr in den Vordergrund. Und nun wird die Geschichte erst richtig interessant.

Bei der Beeinflussung des Verhaltens von Objekten sind zwei Arten zu unterscheiden:

Die Effekte bei der Pflege und Wartung von objektorientierten Programmen sind weiter oben schon besprochen worden.

Im folgenden sollen Möglichkeiten der Verhaltensbeeinflussung von Objekten für bestimmte Einsatzfälle bzw. in bestimmten Programm-Situationen näher untersucht werden.

Das Verhalten von Objekten ist in seinen Methoden und Events hinterlegt und wird ggf. durch Properties beeinflußt.

Es gibt verschiedene Varianten, wie das Verhalten eines Objekts verändert werden muß:

TIP: Ein solcher Kommentar sollte genaue Auskunft über seinen Zweck geben, damit er nicht irgend einer Säuberungsaktion zum Opfer fällt und dadurch das Objektverhalten gestört wird.

·         Für die Ergänzung des betrachteten Verhaltens durch vor- bzw. nachgelagerte Aktivitäten steht der "Scope Resolution Operator" und in Visual FoxPro 5.0 zusätzlich die DODEFAULT()-Funktion zur Verfügung. Das folgende Code-Beispiel illustriert die Einsatzmöglichkeiten von DODEFAULT():

PROCEDURE Click
:
irgendwelcher zusätzlicher Kram
:
DODEFAULT()     && ruft den Click der übergeodneten Klasse auf
:
irgendwelcher weiterer zusätzlicher Kram
:
ENDPROCEDURE
TIP: DODEFAULT() darf man nicht verwechseln mit dem Schlüsselwort NODEFAULT, welches Eventauswertungs-Aktivitäten unterdrücken kann (siehe VFP-Dokumentation bzw. Online-Hilfe).

Der Einsatz des "Scope Resolution Operators" ist nur ganz besonders kniffligen Fällen vorbehalten, denn bei seiner Verwendung können durch Unachtsamkeit schnell Rekursionen oder andere unerwartete Ergebnisse entstehen.

Was passiert nun aber, wenn für mehrere Methoden unterschiedliche Verhaltensweisen vorgesehen werden müssen?

Objekt-Komposition vs. Klassenhierarchien

In diesem Fall kann Vererbung nicht sonderlich helfen. Aus dieser Sackgasse gibt es zwei Auswege:

Der "prozedurale" Ausweg bedarf keiner weiteren Erläuterung, dieses Verfahren ist wohl allen xBase-Programmierern geläufig.

Der wesentlich interessantere Ansatz ist der "objektorientierte" Ausweg, für dessen Diskussion allerdings einige Grundlagen gelegt werden müssen.

Lassen Sie sich die Referenz erweisen!

Der Zugriff auf Objekte kann über zwei verschiedene Wege erfolgen:

"Object Reference" ist ein neuer Datentyp seit VFP 3.0, der einige Besonderheiten aufweist:

xoRef0 = CREATEOBJECT( "myClass" )
                      && hiermit wird die Referenz der erstellten
                      && Objekts der Variablen xoRef0 zugewiesen
 
xoRef1 = This         && hiermit weist ein Objekt seine eigene
                      && Objektreferenz der Variablen xoRef1 zu
 
xoRef2 = xoRef1       && hiermit wird der Variablen xoRef2 die
                      && aktuelle Objektreferenz zugewiesen, die
                      && gerade in xoRef1 gespeichert ist
Beim Zuweisen einer Objektreferenz wird KEINE(!) Kopie des Objektes angelegt, sondern lediglich eine weitere Referenz auf das entsprechende existierende Objekt!

Neben den Object References haben die meisten Container-Objekte Array-Properties, über die man die im Container enthaltenen Objekte referenzieren kann (siehe VFP-Hilfe zu den Properties "Buttons", "Columns", "Controls", "Forms", "Pages"). Außerdem kann man mit der AMEMBERS()-Funktion ein beliebiges Array mit den Objektreferenzen der Unterobjekte eines beliebigen Containers füllen. Für das Parsen von Containern sei außerdem auf das neue FOR EACH - Konstrukt verwiesen.

Komponieren Sie Objekte!

Zurück zum "objektorientierten" Ausweg aus der beschriebenen Sackgasse.

Das zu lösende Problem besteht also in der Ausstattung eines Objekts mit variierbaren Verhaltensweisen (je nach Einsatzfall des Objekts bzw. nach Programmsituation). Zu diesem Zweck macht man sich die hohe Flexibilität von Object References zunutze.

Gegeben sei ein Objekt Y mit den Methoden M1, M2 und M3. Jede dieser Methoden wird nach Programmsituation in den vier verschiedenen Spielarten V1 bis V4 benötigt (in nicht vorhersehbarer Kombination).

Rein mathematisch existieren also 4**3 = 64 Varianten, die auf keinen Falls als Vererbungsvarianten in einer Klassenhierarchie vordefiniert sein können.

Anstelle des Aufbaus der klassisch-prozeduralen CASE-Statement-Variante kann man wie folgt vorgehen:

        werden jetzt Aufrufe wie

This.qoM1.wuDoIt( myParm )

        verwendet.

Auf diese Weise kann das Verhalten des Objekts Y durch einen bloßen Austausch der den Properties qoM1 bis qoM4 zugeordneten Objekte verändern.

Die hier beschriebene Verfahrensweise soll lediglich das Grundprinzip andeuten, nach dem eine solche Objekt-Komposition funktioniert. Es gibt diverse Spielarten von Objekt-Kompositionen - die sogenannten Design Patterns. Diese Patterns werden in den Sessions E‑PATT systematisch erläutert werden. Außerdem beschäftigt sich die Session D‑KOMP ausführlich mit Fragen der Kommunikation von Objekten untereinander.

Wo ist der siebzehner Schlüssel?

Die Programmiersprache von Visual FoxPro beinhaltet eine ganze Menge an Werkzeugen und Mechanismen, die für die objektorientierte Programmierung wichtig sind.

Da diese Dinge innerhalb von Visual FoxPro sehr verstreut liegen und nicht zusammenhängend dokumentiert sind, soll die folgende Tabelle eine Hilfestellung geben:

Stichpunkt Sprachelement1) Erläuterung

Objekt erzeugen

CreateObject()

·    dient dem Erzeugen von assoziierten Objekten (Objekte, die nicht in anderen Containern enthalten sind)

·    liefert als Rückgabewert die Objektreferenz auf das erzeugte Objekt (bzw. :NULL:, wenn das Objekt nicht instanziert werden konnte)

·    kann an das zu instanzierende Objekt Parameter übergeben

·    ACHTUNG! Ein per CreateObject() erzeugtes Objekt ist zunächst immer unsichtbar und muß per object.Visible=.T. explizit sichtbar gemacht werden (damit kann das Objekt programmatisch noch verändert werden, ohne daß diese Änderungen optisch mitverfolgt werden können)

·    CreateObject() kann auch ActiveX-Objekte erzeugen (siehe auch GetObject())

ActiveX-Objekt erzeugen

GetObject()

·    ebenso wie mit CreateObject() können mit GetObject() ActiveX-Objekte erzeugt werden

·    wann welcher der beiden Befehle Verwendung findet, hängt von Programmsituation und Art des ActiveX-Objekt ab

Objekt hinzufügen

.AddObject()

·    dient zum Erzeugen von aggregierten Objekten (Objekte, die nicht in anderen Containern enthalten sind)

·    ansonsten analog CreateObject(), allerdings liefert AddObject() keine Objektreferenz als Rückgabewert

Objekt entfernen

.RemoveObject()

·    dient zu Entfernen von aggregierten Objekten (Objekte, die nicht in anderen Containern enthalten sind)

Container parsen

.SetAll()

·    mit dieser Methode kann man ein Property für alle in direkt einem Container enthaltenen Objekte auf einen neuen Wert setzen (es sind allerdings nur absolute Zuweisungen möglich, keine relativen Veränderungen)

Collections (Objekte in Containern)

.Controls

.Buttons

.Pages

.Forms

·    diese Collections sind Pseudo-Array-Properties von Containern, über die man auf die Objektreferenzen (fast) aller im jeweiligen Containern enthaltenen Objekte zugreifen kann

Container parsen

FOR EACH ...

·    dieses Sprachelement ist ein vereinfachtes Schleifenkonstrukt für die Arbeit mit Collections

Objekt-Referenzen

 

·    dieser neue Datentyp beinhaltet den Zugriffspfad auf Objekte und ermöglicht damit auch die Weitergabe als Parameter, Speicherung in Arrays usw. (siehe Punkt "Lassen Sie sich die Referenz erweisen" des vorliegenden Dokuments)

Referenzierungs-Präfixe

This.

·    Referenzierung eines Objektes auf sich selbst

 

ThisForm.

·    Referenzierung auf die Form (Maske), in der sich das betreffende Objekt befindet

 

ThisFormSet.

·    Referenzierung auf das FormSet (Maske), in der sich das betreffende Objekt befindet

 

.ActiveControl.

·    Referenzierung auf das aktive Control einer Form, Page oder ToolBar

 

.ActiveForm.

·    Referenzierung auf die aktive Form innerhalb eines FormSets bzw. in _screen (siehe auch VFP- Hilfe)

 

.Parent.

·    Referenzierung auf den übergeordneten Container

 

_screen.

·    Referenzierung auf das VFP-Hauptfenster (siehe auch VFP- Hilfe)

 

.Application.

·    Referenzierung beim Einsatz von VFP als ActiveX-Server

 

_vfp.

·    Referenzierung beim Einsatz von VFP als ActiveX-Server

Gültigkeit von Objektreferenzen

TYPE(...) = "O" AND NOT ISNULL(...)

bzw.

TYPE("xyz.Name")

      ="C"

·    mit diesen beiden Konstrukten kann man prüfen, ob eine gültige Objektreferenz vorliegt

·    das erste Konstrukt ist etwas umständlicher

·    die zweite Variante versagt allerdings bei per SCATTER...NAME erstellten Objekten, da diese als einzige Objekte kein Name-Property haben

Datensatz-Objekte

SCATTER ... NAME

GATHER ... NAME

·    diese Variante des SCATTER-Befehls erstellt ein Objekt, welches die Felder des aktuellen Datensatzes mit ihrem Inhalt als Properties bekommt

·    der analoge GATHER-Befehl kann ein solches Objekt in einen Datensatz zurücktransferieren

Vergleichen von Objektreferenzen

CompObj()

·    vergleicht zwei Objektreferenzen und liefert .F. zurück, wenn die Referenzen auf unterschiedliche Objekte zeigen

·    der Umkehrschluß .T. = gleiches Objekt ist nicht zulässig, da nur die vorhandenen Properties und deren Inhalte verglichen werden (was keine Garantie dafür ist, daß beide Referenzen wirklich auf das gleiche Objekt zeigen!)

Objekt-Informationen

AMEMBERS()

·    diese Funktion füllt ein Array mit bestimmten Informationen über ein Objekt

selektierte Elemente zur Design-Zeit

ASELOBJ()

·    liefert ein Array mit den Objektreferenzen auf die im Moment im Designer selektierten Objekte (wird benötigt beim Programmieren von Buildern)

"freifliegende" Instanzen

AINSTANCE()

·    füllt ein Array mit Objektreferenzen aller per CREATEOBJECT() erzeugten Instanzen einer bestimmten Klasse

Klassen-Informationen

ACLASS()

·    diese Funktion füllt ein Array mit allen Elternklassen eines Objekts

Abrufen von Properties eines Objekts

GETPEM()

·    mit GETPEM() kann man ein Property eines Objektes abfragen, dessen Referenz in einer Variablen gespeichert ist (vermeidet ggf. Makro-Substitution)

Abrufen der Eigenschaften von Properties und Methoden eines Objekts

PEMSTAT()

bzw.

SYS(1269)

·    diese Funktion liefert diverse Informationen über die Eigenschaften und Methoden eines Objekts (siehe VFP-Hilfe)

Objekt-Referenz erlangen

SYS(1270)

·    SYS(1270) liefert eine Objektreferenz auf das Objekt an einer bestimmten Bildschirm-Position bzw. an der Mausposition (wichtige Unterstützung beim Debugging!)

SCXX-Datei ermitteln

SYS( 1271 )

·    liefert den Namen der SCX-Datei, aus der ein Objekt erstellt wurde

Containment ermitteln

SYS( 1272 )

·    gibt einen String mit den Containment-Informationen zurück (z.B. "MyForm.Pgf1.Page1.MyButton")

Objekt-Position umrechnen

ObjToClient()

·    rechnet die Objekt-Koordinaten relativ zur linken oberen Ecke des Fensters um, in dem sich das betreffende Objekt befindet

1) Ein Punkt vor dem Sprach-Element bedeutet, daß es sich um eine Methode oder ein Property von Objekten handelt.

Wie fällt der Kronleuchter von der Decke?

Wenn man mit Objekt-Komposition arbeitet, werden viele Objekt-Referenzen in Variablen, Arrays, Properties usw. gespeichert. Dabei muß man um eine Besonderheit wissen, die im folgenden beschrieben wird. Dabei wird ein "Bild" verwendet, welches schon seit geraumer Zeit die Runde durch die FoxPro-Gemeinde macht, um den etwas verzwickten Tatbestand zu beschreiben

Soweit, so gut! Die Probleme beginnen dann, wenn irgendwelche Object References auf das Objekt belegt werden.

Eine ärgerliche Geschichte. Noch schlimmer wird die Sache allerdings, wenn man das Bild auf die Objekte in der Form erweitert und diese als einzelne Arme des Kronleuchters betrachtet.

Dieses sehr problematische Verhalten passiert leider auch dann, wenn alle Object References sich als Properties innerhalb der freizugebenden Form befinden und sowieso gelöscht werden würden.

Aus diesem Dilemma gibt es zwei Auswege:

Letztendlich bleibt aber unklar, weshalb ein solches Verhalten in der Version 5.0 nicht abgestellt wurde.

Schlußbetrachtung

Die bis hier diskutierten Schritte beinhalten die wesentlichen Aspekte der formalen Erschließung der OOP-Möglichkeiten von Visual FoxPro. Um die objektorientierte Programmierung allerdings effektiv einsetzen zu können, sind noch einige weitere wichtige Aspekte zu beachten. Informieren Sie sich darüber in den Sessions D‑KPUT, D‑MODL, E‑PATT, E‑OOAD sowie D‑KOMP und D‑KAPP!

Bei weiterführendem Interesse kann zum Autor gern Kontakt aufgenommen werden (E-Mail-Adresse: SFlucke@asci-consulting.com).

[ 1 ] [ 2 ]