Beruflich Dokumente
Kultur Dokumente
Teil I führt Sie auf praktische Weise zügig in die Programmierung von Notes-/
Domino mit LotusScript ein.
Sie lernen, wo und wie LotusScript-Programme erstellt und zum Laufen gebracht
werden. Es werden sämtliche grundlegenden Elemente besprochen wie Lotus-
Script-Befehle, -Variablen, -Objekte, -Module, -Prozeduren, usw.
Natürlich erfahren Sie auch in allen Einzelheiten, wie der Debugger bedient und
praktisch eingesetzt wird. Dieses Wissen ist eine wichtige Unterstützung bei der
Durchführung effektiver Tests.
Außerdem erhalten Sie eine ausführliche, anschauliche Darstellung zur objektori-
entierten Programmierung unter LotusScript. Ein gutes Verständnis dieses Punktes
wird Ihnen bei der Bildung qualitativ hochwertiger Architekturen sehr behilflich
sein.
Die Darlegung des Stoffes beginnt zunächst sehr detailliert, um Sie beim Einstieg
sicher zu begleiten und zieht dann im Tempo recht schnell an, um Sie schnellst-
möglich zu den wesentlichen Erkenntnissen zu führen, die Sie für eine erfolgreiche
Erstellung und Wartung auch komplexer Programme benötigen.
Im sich anschließenden Dialog geben Sie einen Titel für die Datenbank und den
Dateinamen und inklusive Pfad an. Wenn Sie den Titel eintragen, wird dieser auto-
matisch auch als Dateiname vorgeschlagen. Sie können den Dateinamen jetzt
natürlich nach Belieben verändern. Außerdem wird dem Namen die Endung .nsf
angehängt. Das ist die Standardendung für Notes-Datenbanknamen.
Ich gebe der Datenbank in meinem Beispiel den Namen LotusScript-Programmie-
rung, und zwar für TITEL und DATEINAME. Sie können es mir nachmachen oder einen
anderen Namen wählen. Alle übrigen Einstellungen lassen wir unverändert und
klicken auf OK (Abbildung 3.2).
Sofort wird die Datenbank erstellt und auch gleich geöffnet (Abbildung 3.3).
Sie sehen in der linken Spalte oben den Titel und darunter ein Symbol für die auto-
matisch erstellte, noch unbenannte Vorgabe-Ansicht. Diese ist im rechten Teil zu
sehen. Ihre einzige Spalte trägt im Kopf das Nummernsymbol #. Natürlich sind
noch keine Dokumente enthalten.
Diese brauchen wir jetzt aber erst einmal gar nicht. Wichtig ist, dass wir unsere
Datenbank haben, die als Container für die zu erstellenden Programme dient.
Hinweis
Wie ich bereits gesagt habe, sind LotusScript-Programme immer Teil einer
Notes-Datenbank; sie können nicht frei davon existieren – und umgekehrt:
Wenn man in Notes eine Anwendung mit LotusScript schreibt, dann besteht
diese immer aus mindestens einer Datenbank.
Wohl auch deshalb werden Notes-Datenbanken ab Version 8 auch als Anwen-
dungen bezeichnet. Daher werde ich die beiden Begriffe in diesem Buch auch
schon mal synonym gebrauchen.
Trotzdem werden Sie schnell merken, dass Anwendungen aus Entwicklersicht
aus einer oder mehreren Datenbanken bestehen, also begrifflich eigentlich
davon zu trennen sind. Während die Anwendung mehr das Gesamtprodukt
aus Benutzersicht darstellt, haben wir es als Programmierer eher mit den
Datenbanken als Trägern der Applikationen zu tun.
Der hauptsächliche Grund, warum Datenbanken von IBM jetzt als »Anwen-
dungen« bezeichnet werden, liegt wahrscheinlich in der Einführung des Kon-
zepts der Verbundanwendungen (Composite Applications).
Das Ziel dabei ist es, mithilfe einer Webservice-Architektur für klare Schnitt-
stellen zwischen Einzelapplikationen zu sorgen, die dann zu Verbundanwen-
dungen zusammengestellt werden können. Mehr dazu finden Sie im Kapitel
16, Verbundanwendungen, ab Seite 563.
Es öffnet sich dann automatisch der Domino Designer, das hauptsächliche Pro-
grammierhandwerkzeug des Notes/Domino-Programmierers. Unsere Datenbank
wird in Programmierdarstellung angezeigt (Abbildung 3.5).
Abbildung 3.5 Die neue Datenbank im Domino Designer, fertig zum Erstellen des ersten Programms
Im linken Bereich sehen Sie eine baumartige Darstellung der einzelnen Datenbank-
Elemente, die im Designer bearbeitet werden können: Rahmengruppen, Seiten,
Masken usw. Vieles davon kann mit LotusScript programmiert werden.
Hinweis
Sie sehen, dass ich die Datenbanken respektive Anwendungen über den
Arbeitsbereich verwalte. Eigentlich ist dies eine veraltete Benutzeroberfläche,
die das Erscheinungsbild von Notes in der Vergangenheit sehr geprägt hat.
Vor einiger Zeit wurden unter anderem die Lesezeichen eingeführt, um Notes
moderner erscheinen zu lassen. Für Benutzer mag dies sehr praktisch und
auch ansprechender sein. Zumal die Lesezeichen unter Notes 8 in der Stan-
dardkonfiguration in ein sehr schönes ÖFFNEN-Menü überführt worden sind
(Abbildung 3.6).
Das erste Programm 35
Für Entwickler und Administratoren zeigt sich in der Praxis, dass der gute alte
Arbeitsbereich mit seinen Arbeitsbereichsseiten und Kacheln trotzdem kaum
zu ersetzen ist. Die Art der Anordnung ist im ersten Moment etwas gewöh-
nungsbedürftig, erweist sich dann aber als sehr gut geeignet, um die Über-
sicht über große Mengen von Datenbanken zu behalten.
Sie erreichen den Arbeitsbereich in der Standardkonfiguration (wie in Abbil-
dung 3.61.6 gezeigt) oder in der Basiskonfiguration über das Lesezeichen
ANWENDUNGEN in der linken Navigationsleiste (siehe Abbildung 3.7).
Abbildung 3.7 Öffnen Sie den Arbeitsbereich mit den Kacheln über die Lesezeichenleiste.
Sie sehen rechts eine leere Liste, in der Sie später den neuen Agenten wiederfinden
werden, den Sie jetzt bitte mit einem Klick auf die Aktion NEUER AGENT rechts oben
erstellen.
Es öffnet sich ein Dialog, in dem wir unserem Agenten einen fantasievollen Namen
mit hohem Wiedererkennungswert geben (diejenigen unter Ihnen, die bereits eine
Das erste Programm 37
Es ist wichtig, dass wir unter LAUFZEIT das richtige ZIEL eingeben, nämlich Keines.
Das ist übrigens eine Einstellung, die für viele LotusScript-Programme genau die
richtige ist, da Sie damit in der Regel die meisten Möglichkeiten bei der Kodierung
haben. Allerdings können Sie in diesem Fall die Dokumentauswahl durch den
Agenten nicht nutzen. Diese muss dann im Script programmiert werden.
Schließen Sie nun den Dialog mit dem kleinen Kreuz rechts oben. Sie werden dann
den neuen Agenten wie in Abbildung 3.10 geöffnet vor sich sehen. Wir müssen nur
noch die richtige Programmierart auswählen (viele Notes-Datenbankelemente
können mit unterschiedlichen Sprachen programmiert werden). Schalten Sie
rechts oben von EINFACHE AKTION(EN) auf LOTUSSCRIPT um.
Abbildung 3.10 Programmierart bzw. -sprache auswählen (Schalten Sie von EINFACHE AKTION(EN) auf
LOTUSSCRIPT um.)
Jetzt sind wir schon an der Stelle, an der wir richtig in LotusScript kodieren kön-
nen. (»Endlich!«, werden Sie vielleicht sagen. Aber diese wenigen Schritte werden
Ihnen schnell in Fleisch und Blut übergehen.)
Hinweis
Doch halt, wozu brauchen wir eigentlich einen Agenten?
Nun, LotusScript lässt sich nicht als eigenständiges Programm schreiben.
LotusScript kann nicht von der Kommandozeile gestartet werden. Es muss
immer in irgendwelche Datenbankbestandteile eingebettet werden.
Der klassischen Vorstellung von einem »richtigen Programm« kommt dabei
das Datenbankelement Agent noch am nächsten, und deswegen beginnen wir
mit der Programmierung eines solchen.
Einen Agenten können Sie sich als eine Hülle, einen Container oder auch als
ein Rahmenwerk für Ihr LotusScript-Programm vorstellen. Wir betten das
Programm dort ein und lassen es dann vom Agenten ausführen.
Später werden Sie Programme auch an anderen Stellen einbauen, zum Bei-
spiel in Masken oder Schaltflächen.
Klicken Sie also bitte links in der Liste auf INITIALIZE (Abbildung 3.11), und geben
Sie dann rechts Folgendes ein:
Messagebox "Hello World. I’m the champion!"
Das war’s schon. Das ist Ihr erstes LotusScript-Programm (Abbildung 3.12).
Nach getaner Arbeit möchten wir natürlich sehen, wie dieses aufwendige und
wichtige Programm in der Realität arbeitet. Dazu schließen Sie bitte den Agenten
mit dem kleinen Kreuz oben in seinem Fenster-Reiter. Sie werden gefragt werden,
ob Sie Ihre Änderungen speichern möchten, und dazu sagen Sie selbstverständlich
ja. Anschließend können Sie Ihr fertiges Produkt in der Liste der Agenten bewun-
dern (Abbildung 3.13).
Egal, wie Sie den Agenten starten, als Resultat erhalten Sie folgende Ausgabe (Abbil-
dung 3.16):
Schritt 1
Wir haben einen Agenten erstellt, der als Container für unser Programm dient.
In anderen Fällen werden Sie auch Masken, Seiten, Schaltflächen und andere Ele-
mente verwenden.
Schritt 2
Wir haben uns einen geeigneten »Startpunkt« für unseren Code gesucht und sind
mit Sub Initialize fündig geworden.
In Masken sind beliebte Startpunkte Sub PostOpen, Sub QueryClose usw. Ähnlich ver-
hält es sich bei Seiten, Ansichten und Datenbank-Scripts. Man nennt sie auch
Eventhandler (Routinen zur sogenannten Ereignisbehandlung). Weitere Details
dazu finden Sie im Abschnitt 3.9, Weitere Startpunkte für LotusScript-Programme, auf
Seite 66.
Schritt 3
Im Eventhandler Sub Initialize haben wir unseren Code eingegeben. Und das wird
bei anderen Startpunkten natürlich genauso gehandhabt.
Schritt 4
Dann haben wir den Agenten geschlossen. Dabei wurde er gespeichert.
ten. Sie können ja den Agenten spaßeshalber noch einmal öffnen und Messagebox in
Massagebox ändern (ein beliebter Rechtschreibfehler). Versuchen Sie dann, den
Agenten noch einmal zu speichern. Das können Sie diesmal tun, ohne ihn zu
schließen. Wählen Sie einfach im Menü DATEI/SPEICHERN aus, oder klicken Sie auf
das Diskettensymbol in der Toolbar-Leiste. Auch die Verwendung von [Strg] + [S]
ist möglich. Auf jeden Fall erhalten Sie die Meldung aus Abbildung 3.17.
Den Fehler hätten Sie in diesem Fall auch schon vorher sehen können, denn der
LotusScript-Editor prüft die verwendeten Begriffe und Operatoren bereits während
der Eingabe. Wenn er bekannte Schlüsselwörter erkennt, dann färbt er sie blau ein.
Messagebox ist ein solches Schlüsselwort und wird erkannt. Nachdem wir aber das
erste »e« gegen ein »a« ausgetauscht hatten, handelte es sich um keinen bekannten
(eingebauten) Begriff mehr, und Massagebox wurde schwarz.
Man kann also schon während der Eingabe erkennen, ob man die richtigen Schlüs-
selwörter verwendet.
Schritt 5
Zum Schluss haben wir den Agenten gestartet. Was dann passierte, ist wichtig für
das Grundverständnis aller LotusScript-Programme.
Der Notes-Client hat beim Start die Kontrolle übernommen. Er weiß, dass in einem
Agenten eine Prozedur namens Sub Initialize vorkommen muss. Eine Prozedur ist
eine Funktion ohne Rückgabewert. Mehr dazu folgt später im Abschnitt 3.5, Eine
eigene Prozedur schreiben. Der Client weiß das also und startet sie für uns. Damit kam
dann auch unsere Codezeile Messagebox “Hello World. I’m the champion!“ zur Ausfüh-
rung.
Prozeduren beginnen immer mit dem Kopf Sub <Prozedurname> und enden unten mit
dem Anweisungsteil End Sub. Dazwischen steht der Code, der von der Prozedur aus-
geführt werden soll. In unserem Fall lautet die gesamte Prozedur:
Sub Initialize
Messagebox "Hello World. I’m the champion!"
End Sub
Wir haben allerdings nur die mittlere Zeile hingeschrieben, weil der Client wie
gesagt weiß, dass ein Agent eine Prozedur Initialize haben muss, und daher den
»Rahmen«, nämlich Kopf und Fuß, schon selbst hingeschrieben hat.
Der Hinweis, dass das Programm beim Speichern kompiliert wurde, braucht Sie im
Prinzip nicht weiter zu interessieren.
Hinweis
Falls Sie es doch genauer wissen wollen, nur so viel zur Erklärung:
Der Code wird beim Kompilieren in eine andere Form gebracht; eine Form,
die vom LotusScript-Interpreter verstanden werden kann.
Der Interpreter ist ein internes Programm, das beim Aufruf von LotusScript-
Code die Anweisungen ausführt, die der Entwickler notiert hat (wie Message-
box). Diese Form des auszuführenden Codes nennt man Objektcode.
Der Objektcode wird für den Benutzer unsichtbar neben dem vom Entwickler
geschriebenen Programm abgespeichert.
Variablen
Variablen sind benannte Speicherplätze, auf die während eines Programm-
ablaufs zugegriffen werden kann. In LotusScript sind Variablen immer verän-
derbar, d. h., ihnen können während des Programmablaufs jederzeit neue
Werte zugewiesen werden.
tung im Hintergrund, sich auf das einzustellen, was da kommt, und zum anderen
hilft es, mögliche logische Fehler bei der Datenzuordnung von vornherein besser
zu erkennen.
Achtung
Variablen müssen immer vor ihrer ersten Verwendung deklariert werden. Tun
Sie dies nicht, dann kann es sein, dass LotusScript dies für Sie unsichtbar im
Hintergrund tut. Das sollten Sie aber unterbinden, indem Sie immer im
Abschnitt (Options) Folgendes eingeben: Option Declare (oder Option Explicit,
was funktional dasselbe ist). Mehr dazu finden Sie im Abschnitt 4.2 Optionen
und Voreinstellungen, ab Seite 130.
Die Angabe As String sagt aus, dass in dem angeforderten Speicherplatz eine Zei-
chenkette abgelegt werden soll (in unserem Hello World-Programm war das eine
Aneinanderreihung von Buchstaben). Außerdem wird auf diese Weise klarge-
macht, dass über die Größe noch nicht viel gesagt werden kann. String-Variablen
können nämlich während des Programmablaufs fast beliebig wachsen und auch
schrumpfen.
Hinweis
Es gibt auch die Möglichkeit, String-Variablen mit fester Länge zu deklarieren.
Außerdem gibt es natürlich auch andere Arten von Variablen, zum Beispiel
zur Verwaltung von Zahlen. Man spricht dabei von verschiedenen Daten-
typen.
Der Begriff direkt nach Dim ist der Name, der dieser Variablen gegeben wird (str-
VariablenName). Unter diesem Namen kann auf die Variable und damit auf den mit
ihr verbundenen Speicherplatz zugegriffen werden.
Bei der Wahl des Namens ist man sehr flexibel. So kann man zum Beispiel jede Buch-
stabenkombination unseres Alphabets wählen, solange sich kein Begriff ergibt, der
schon von der LotusScript-Sprache selbst belegt ist.
In diesem Beispiel habe ich mit str ein Präfix verwandt, das dem Leser des Pro-
gramms deutlich macht, dass es sich hier um eine String-Variable handelt. Auf die
Funktion der Variablen hat das keinerlei Einfluss. Solche Präfixe sind aber sehr
praktisch, um auch bei langen Programmen den Überblick zu behalten, was für
Daten in welchen Variablen abgelegt werden.
Ich kann Sie nur dazu ermuntern, sich einen entsprechenden Styleguide anzuge-
wöhnen. Verwenden Sie für Variablennamen immer Präfixe, und zwar möglichst
gängige bzw. sprechende Präfixe. Auch die Namen der Variablen sollten möglichst
klar für den Inhalt oder die Verwendung sprechen. Das sollten Sie auch dann tun,
wenn Sie erfahrene Programmierer kennenlernen, die sich über solche Regeln hin-
wegsetzen.
Datentyp-Suffixe
In Basic-Sprachen wie LotusScript kann man viele Datentypen statt mit der Formu-
lierung As Datentyp auch mit einem Suffix angeben.
Für String-Variablen lautet das Suffix $, und so könnten wir die obige Deklaration
auch wie folgt notieren:
Dim strVariablenName$
Diese Art der Deklaration hat den Vorteil, dass Sie weniger schreiben müssen und
mit ein wenig Übung die manchmal lange Liste der Datendeklarationen schneller
überblicken können.
Achtung
Wenn man eine Variable im Programm anspricht, deren Datentyp mit einem
Suffix angegeben wurde, dann kann man das Suffix als Namensbestandteil
gebrauchen, muss es aber nicht tun. Im nächsten Beispiel werden Sie beide
Möglichkeiten nebeneinander sehen.
Allerdings sollten Sie sich für eine davon entscheiden. Nichts ist unübersicht-
licher, als wenn man solche Verfahrensweisen mitten im Code ändert! Die
Mischung zeige ich hier nur einmal, um die Gleichwertigkeit zu veranschau-
lichen.
Wenn eine Variable ohne Suffix deklariert wurde, dann muss sie in der Folge
auch ohne Suffix verwendet werden.
Ausdruck
Ein Ausdruck ist eine wie auch immer geartete Berechnung von Werten. Die
Berechnung kann auch unter Einbeziehung von Funktionen und Objekten
erfolgen.
Wenn Sie noch kein so routinierter Programmierer sind, dann wird Ihnen die Defi-
nition etwas schwierig bzw. schwammig vorkommen. Die Frage ist: Wo beginnt ein
Ausdruck, und wo hört er auf? Und zu den Begriffen Funktion und Objekt kommen
wir erst später. Am besten ist es, Sie vergessen das Thema erst einmal wieder. Die
genaue Abgrenzung dieses Begriffs ist in der Praxis des LotusScript-Entwicklers
nicht so wichtig. Außerdem wird er immer mal wieder in recht harmlosen Zusam-
menhängen auftauchen, und dann wird die genaue Bedeutung ganz nebenbei
immer klarer.
Was Sie allerdings wissen sollten: Fast überall, wo ein Wert oder der Inhalt einer
Variablen benötigt wird, können Sie stattdessen auch einen Ausdruck notieren.
Dieser wird dann automatisch berechnet, und der sich ergebende Wert wird an sei-
ner Stelle übergeben. Das haben Sie gerade gesehen, als wir der Variablen strAnzei-
geText zunächst eine Zeichenkette, dann aber mehrfach einen Ausdruck nach der
Art strAnzeigeText & “ Gestern!” zugewiesen haben.
Hier haben wir eine sehr einfache Berechnung mit Zeichenketten vorgenommen.
Andere Ausdrücke können Berechnungen von Zahlen- und Datumswerten (nume-
rische Ausdrücke) und von Wahrheitswerten (boolesche Ausdrücke) einschließen.
iErgebnis = 10 * 2.5 – 3
Ausdrücke erstellen und verwenden 47
Der Ausdruck ist hier: 10 * 2.5 – 3. Dieser (bzw. sein Wert) wird der Variablen iErgeb-
nis zugeordnet. Durch die Zuordnung wird der Ausdruck automatisch berechnet.
Hinweis
Unser deutsches Dezimalkomma ist im Englischen ein Punkt und wird so
auch von LotusScript gefordert.
Vorrangregeln
Bei der Berechnung von Ausdrücken gelten gewisse Vorrangregeln, die im Bereich
der arithmetischen Berechnungen an den mathematischen Usus angelehnt ist.
Zum Beispiel gilt »Punktrechnung von Strichrechnung«. Der Ausdruck 10 * 2.5 – 3
wird also zunächst zu 25 – 3 und ergibt das Resultat 22.
Wollten wir es anders haben, so müssten wir entsprechende Klammern setzen:
10 * (2.5 – 3) wird ausgewertet zu 10 * (-0.5) und gibt -5.
Hinweis
Vor längerer Zeit gab es in LotusScript keinen eigenen Datentyp für Wahr-
heitswerte. Statt Boolean nahm man häufig Integer-Variablen.
In diesem Fall wurde die 0 als False gewertet und jeder andere Wert als True.
Intern wird nach wie vor False mit dem Wert 0 gleichgesetzt und True mit -1.
Sie werden wahrscheinlich noch auf das eine oder andere Script stoßen, in
dem Integer-Werte für Wahrheitswerte verwendet werden. Lassen Sie sich
dadurch nicht verwirren. Mit diesem Hintergrundwissen gewappnet, können
Sie mit diesen Werten praktisch genauso rechnen, als würden »echte« Wahr-
heitswerte vorliegen.
Vorrangregeln
Auch bei booleschen Ausdrücken gelten gewisse Vorrangregeln. So wird zunächst
die Negation Not berechnet, dann And und schließlich Or. Auch hier lassen sich die
Regeln durch Klammerungen umgehen.
Not False Or True ‘ergibt True Or True und das ist True
Not (False Or True) ‘ergibt Not True und das ist False
Prozedur
Prozeduren sind benannte Programmteile, die über ihren Namen zum Zwe-
cke der Ausführung einmal oder mehrmals aufgerufen werden können.
Praktisch gesehen bedeutet diese Definition, dass Sie einen (fast) x-beliebigen Code
nehmen und ihn in eine Prozedur packen können. Dadurch haben Sie die Möglich-
keit, diesen Code aus einem Programm auszulagern und ihn darin mit einem einzi-
gen Aufruf ausführen zu lassen. Dabei ist es egal, wie lang der Code in der Prozedur
ist. Auch Teile aus einer Prozedur können in weitere Prozeduren ausgelagert werden.
Sie werden dadurch sehr frei in der Gestaltung der Codestruktur. Sie sind gut bera-
ten, wenn Sie dies zu Ihrem eigenen Vorteil so nutzen, dass eine möglichst klare
Struktur entsteht, die die Logik der zugrunde liegenden Aufgabe gut abbildet.
Wir nehmen an, wir würden es für sinnvoll halten, einen Programmteil zu haben,
der bei seinem Aufruf die aktuelle Zeit ausgibt. Der aktuelle Agent ist der Agent, in
dem das gerade ablaufende Script enthalten ist, mithin unser Hello World-Agent.
Die zugehörige Prozedur könnte so aussehen:
Eine eigene Prozedur schreiben 49
Sub ShowTime
Messagebox "Wir schreiben den " & Date & ". Die Uhrzeit ist: " & Time
End Sub
Listing 3.2 Unsere selbst erstellte Prozedur
Dieses kleine Programm arbeitet mit sogenannten Objekten. Wenn Sie damit noch
keine Erfahrung haben, dann denken Sie im Moment noch nicht allzu viel darüber
nach. Ich komme gleich im Abschnitt 3.8, Mit (Notes)-Objekten arbeiten, darauf
zurück. Nehmen Sie den Code im Moment einfach so, wie er ist, und konzentrieren
Sie sich eher darauf, wie die Prozedur selbst (sozusagen die Klammer um diesen
Code) erstellt wird.
Anschließend geben Sie mit [¢] noch einen Zeilenumbruch ein. Dabei wird die
zugehörige Kennzeichnung des Endes (End Sub) vom LotusScript-Editor automa-
tisch angelegt und die so erstellte leere Prozedur in ein eigenes Fenster verfrachtet,
das Ihnen sofort angezeigt wird.
Gleichzeitig wird die Prozedur als neuer Programmierabschnitt im Baum links vom
Programmierfenster eingetragen. Wenn Sie die neue Prozedur in Zukunft öffnen
und bearbeiten wollen, wählen Sie einfach ihren Namen im Abschnittsbaum aus
(Abbildung 3.19).
Jetzt füllen wir die Prozedur ShowTime noch mit Inhalt, indem wir die noch fehlen-
den Zeilen LotusScript hineinschreiben. Dieser Teil wird auch allgemein als Rumpf
der Prozedur bezeichnet (Listing 3.3: Prozedurrumpf).
Messagebox "Wir schreiben den " & Date & ". Die Uhrzeit ist: " & Time
Listing 3.3 Prozedurrumpf
Wie Sie sich schon denken können, ist Call die LotusScript-Anweisung, mit der Pro-
zeduren aufgerufen werden können.
Damit sind wir fertig. Der Agent kann gespeichert und geschlossen werden. Lassen
Sie ihn einmal ablaufen, damit Sie die Früchte unserer gemeinsamen Arbeit genie-
ßen können, bevor wir den Prozedurrumpf aus Listing 3.3 besprechen.
3.5.3 Funktionen
Prozeduren können Werte zurückgeben, die wie ein Ausdruck an Variablen zuge-
wiesen oder im Rahmen von Berechnungen weiter ausgewertet werden können.
Solche Prozeduren nennt man Funktionen. Sie werden mit dem Schlüsselwort
Function statt mit Sub angelegt.
Wir können die Prozedur ShowTime so umformulieren, dass sie den Text zurückgibt,
anstatt ihn anzuzeigen. Wir nennen die entstehende Funktion ihrer Arbeitsweise
gemäß GetTime:
Function GetTime As String
Dim strTimeText$
GetTime = strTimeText
End Function
Listing 3.5 Die zur Funktion mutierte Prozedur aus Listing 3.4
Eine eigene Prozedur schreiben 51
Hier fallen zwei Dinge auf. Erstens erhält der Funktionsname mit As String einen
Zusatz wie bei einer Variablendeklaration. Damit wird der Datentyp der Rückgabe
angegeben. Je nach Rückgabewert sind natürlich auch andere Deklarationen denk-
bar, wie Integer, Variant usw.
Zweitens sehen Sie in der letzten Zeile der Funktion, dass der berechnete Wert aus
strTimeText dem Namen der Funktion zugewiesen wird. Dadurch erfährt sie, wel-
chen Wert sie ausgeben soll.
Hinweis
Es gehört zum guten Programmierstil, diese Zuweisung erst zum Schluss zu
tätigen.
Natürlich könnte man bei dieser kurzen Berechnung statt der Variablen str-
TimeText gleich den Ausdruck zuweisen. Das wäre in diesem Fall in Ordnung,
da die Zuweisung auch dann ja am Ende erfolgen würde. Die vorgenommene
strikte Trennung ist aber am übersichtlichsten und soll Ihnen den Grundsatz
vor Augen führen.
Theoretisch kann man den Funktionsnamen sogar wie eine lokale Variable
behandeln und ihren Inhalt zwischendurch abfragen, verarbeiten und erneut
zuweisen. Davon ist aber auf jeden Fall abzuraten.
Möchten wir den Wert der Funktion von unserem Agenten ausgeben lassen, so
müssen wir den Aufruf ein wenig umschreiben. Denn wir müssen ja den Rückgabe-
wert »abholen« und dann zur Ausgabe bringen:
Sub Initialize
Messagebox GetTime
End Sub
Listing 3.6 Der Aufruf von GetTime
Wir können den Rückgabewert auch erst einer Variablen zuweisen und dann wei-
tere Berechnungen vornehmen:
Sub Initialize
Dim strTime$
Dim strPrefix$
}
strTime = GetTime
Wenn Sie die Erstellung des ersten Textteils (strPrefix) betrachten, wird Ihnen auf-
fallen, dass die Zeichenkette diesmal nicht von Anführungszeichen, sondern von
geschweiften Klammern umrandet ist und sich in die übernächste Zeile erstreckt.
Das hat den Grund, dass wir einen doppelten Zeilenumbruch für die Ausgabe
benötigen. Anstatt ihn mit den betriebssystemspezifischen Zeichen zu kodieren,
schreiben wir ihn einfach hin. Das geht aber nur mit geschweiften Klammern (oder
mit der noch verbleibenden Alternative, den senkrechten Strichen ||). Die Ausgabe
sehen Sie in Abbildung 3.21.
Parameter
Parameter sind Variablen, die im Prozedurkopf angegeben werden (also in der
Zeile, in der der Prozedurname steht). Parameter stehen innerhalb der Proze-
dur wie lokale Variablen zur Verfügung. Sie können abgefragt und bearbeitet
werden.
Wenn eine solche Prozedur aufgerufen wird, dann muss ihr für jeden Parameter ein
Wert mitgegeben werden.
Die Funktion GetTime gibt bislang nur deutsche Texte aus. Wenn wir in einem inter-
nationalen Umfeld arbeiten, dann benötigen wir wahrscheinlich auch eine eng-
lische Ausgabe. Um nicht für jede Sprachausgabe eine eigene Funktion schreiben
zu müssen, parametrisieren wir sie und übergeben ein Kürzel, das sie anweist, eine
deutsche oder eine englische Ausgabe zu generieren.
Function GetTime(in_strLanguage As String) As String
Dim strTimeText$
GetTime = strTimeText
End Function
Listing 3.8 Die parametrisierte Funktion zur Ausgabe von verschiedenen Sprachen
Sie sehen, dass Parameter fast wie Variablen deklariert werden. Sie werden hinter
dem Prozedurnamen als Liste aufgeführt und von runden Klammern umschlossen.
Die Liste wird durch Kommata getrennt. Da wir nur einen Parameter benötigen, ist
natürlich kein Komma zu sehen. Statt der Deklaration mit As String hätten wir
auch hier das Suffix $ verwenden können.
Hinweis
Dem Namen habe ich ein in_ vorangestellt. Das ist nicht zwingend, genauso
wie das Präfix str nicht zwingend ist, aber einen guten Programmierstil aus-
macht. Das Präfix in_ ist sehr praktisch, weil Sie dadurch besonders innerhalb
von längeren Prozeduren den Überblick darüber behalten, welche Werte als
Parameter »von außen geliefert wurden« und welche in lokalen Prozedurvari-
ablen vorliegen.
}
strTime = GetTime(strLanguage)
InputBox ist eine eingebaute Funktion der Sprache LotusScript. Auch sie wird durch
Parameter gesteuert. Die erste Zeichenkette teilt dem Benutzer mit, was von ihm
erwartet wird. Die zweite wird als Fenstertitel verwendet. Und die dritte (“de“) wird
als Vorgabe oder Vorschlag schon in die Eingabezeile geschrieben.
Wir haben die Bedingung in der Funktion GetTime so gewählt, dass bei der Übergabe
von “de“ die deutsche Version ausgegeben wird und bei allen anderen die eng-
lische. Das macht unsere Funktion fehlerfest, »robust«, wie man in der Program-
mierung sagt. Es wird also auch dann ein sinnvolles Ergebnis geliefert, wenn der
Benutzer irgendwelchen Unsinn übergibt oder eine Sprache, die die Funktion nicht
kennt.
Zur Programmierung von Verzweigungen gibt es noch andere Möglichkeiten, wie
es auch verschiedene Schleifen zum wiederholten Durchlaufen eines Codes gibt.
Mehr dazu finden Sie im Abschnitt 4.7, Ablaufsteuerung – Verzweigungen und Schlei-
fen, ab Seite 167.
Debugger aktivieren
Um den Debugger zu nutzen, muss er vor dem Aufruf eines Agenten oder einer
Routine aktiviert werden, genauer gesagt: vor dem Laden des entsprechenden
Moduls. Bei einem Agenten geschieht das wie gesagt vor dessen Start; bei einer
Maske, einer Ansicht usw., passiert es, bevor dieses Element im Client geöffnet
wird.
Die Aktivierung erfolgt über WERKZEUGE/DEBUG LOTUSSCRIPT im Menü des Desig-
ners. Wenn links neben dem Menüpunkt ein Häkchen zu sehen ist, dann ist er akti-
viert. Alternativ kann er über ein SmartIcon in der Universal-Leiste aktiviert wer-
den.
Debugger starten
Sobald danach ein Modul geladen und eine seiner Routinen gestart wird, erscheint
das Debugger-Fenster. In Abbildung 3.22 sehen Sie das Fenster für den zuletzt
erstellten Agenten.
Den Debugger verwenden 55
Das Programm wurde an der ersten ausgeführten Zeile angehalten (das geschieht
immer). Diese wird durch einen kleinen gelben Pfeil markiert. Er zeigt an, welche
Zeile als Nächstes ausgeführt werden wird.
Haltepunkte
Etwas weiter unten sehen Sie einen großen roten Punkt (im Buch ist er natürlich
schwarz). Dieser markiert einen Haltepunkt (engl. Breakpoint).
Sie haben jetzt die Wahl, das Programm einfach weiterlaufen zu lassen oder nur
gezielt den nächsten Schritt auszuführen. Der nächste Schritt würde mit der oben
befindlichen Schaltfläche SCHRITT AUSFÜHREN abgearbeitet. Das ist der Code, der
sich in der aktuellen Zeile befindet, an deren Beginn der Pfeil postiert ist. Danach
würde der Pfeil automatisch zur nächsten Zeile weiterrutschen. Manchmal ist es
notwendig, jeden Schritt zu verfolgen; dann wählt man diesen Weg.
Häufig gibt es aber nur eine gewisse Stelle, die näher untersucht werden soll. Dann
setzt man dort einen Haltepunkt und klickt FORTFAHREN an. Auf diese Weise wird
das Programm alle weiteren Schritte abarbeiten, bis es auf diesen oder einen ande-
ren Haltepunkt stößt. Mit FORTFAHREN würden wir in unserem Beispiel also bei dem
roten Punkt vor der Messagebox-Anweisung landen.
Um einen Haltepunkt zu setzen, bewegt man den blauen Balken (Cursor) auf die
entsprechende Zeile und klickt sie doppelt an oder drückt einmal [F9]. Möchte
man einen Haltepunkt entfernen, so wiederholt man das Ganze zweimal. Wieder-
holt man es nur einmal, dann wird der Haltepunkt nur deaktiviert. In Abbildung
3.26 sehen Sie ein Beispiel dafür. Dieser ist zwar vermerkt, aber nicht aktiv, sodass
das Programm dort nicht halten wird. Dennoch können deaktivierte Haltepunkte
über das Fenster BREAKPOINTS angesteuert werden.
In Abbildung 3.22 sehen Sie im unteren Bereich vier Reiter für vier verschiedene
Anzeigen. Der obere Reiter ist mit BREAKPOINTS beschriftet. Somit sehen Sie hier die
gesetzten Haltepunkte. In unserem Beispiel ist es nur einer. Die Angaben sagen aus,
dass er in der Routine Initialize in der 13. Zeile gesetzt ist. Wenn Sie mehrere Hal-
tepunkte gesetzt haben und zu einem bestimmten »hinspringen« möchten, dann
wählen Sie einfach die BREAKPOINTS-Seite und klicken auf den entsprechenden Hal-
tepunkt. Dies bringt Sie an den gewünschten Ort.
Mit der Anweisung Stop kann man Haltepunkte auch kodieren. Sie haben den Vor-
teil, dass sie mit dem Code verbunden sind und mit Bedingungen verknüpft wer-
den können, beispielsweise so:
...
If strPrefix = “Blödsinn“ Then
Stop
End If
...
Stop-Anweisungen werden allerdings nicht als Haltepunkte auf der Seite BREAK-
POINTS aufgeführt.
Die Haltepunkte werden nur beachtet, wenn das Programm im Debug-Modus läuft.
Außerdem sind die im Designer gesetzten Haltepunkte an die bookmark.nsf des Ent-
wicklers gebunden (sie werden dort in einem Profildokument hinterlegt). Daher
brauchen sie für ein Produktivprogramm nicht entfernt zu werden. Allerdings wür-
de es keinen guten Eindruck machen, eine Reihe von Stop-Anweisungen zu hinter-
lassen, über die dann nachfolgende Entwickler »stolpern«. Sie sollten die Halte-
punkte vor der Auslieferung Ihres Programms beseitigen.
Als Beispiel führen wir eine Änderung an der Variablen strPrefix aus. Dazu wird die
Variable mit der Maus angeklickt, woraufhin ihr Wert im unteren Feld NEUER WERT
erscheint. Dort schreibt man den neuen Wert einfach hinein.
Möchte man die Änderungen übernehmen, klickt man rechts das grüne Häkchen
an. Sollen sie verworfen werden, klickt man auf das rote Kreuzchen oder geht ein-
fach ohne Bestätigung des Häkchens weiter (Abbildung 3.24).
Hier wird der Text “Der Agent sagt: “ umgeändert in “Das Programm sagt: “.
Genauso geht man bei Zahlen, Datumswerten, Array-Elementen, Listen-Elementen
und entsprechenden Eigenschaften von Objekten vor.
Manche Eigenschaften von Objekten lassen sich nicht ändern. Dies sind dann
Eigenschaften, die auch in Programmen nur gelesen werden können.
Ausgabe überwachen
Mit Print getätigte Ausgaben werden im Fenster AUSGABE angezeigt. Im “Hello World“-
Agent wurde dazu noch eine Zeile eingefügt, die einen Text ausgibt (Abbildung 3.25).
Folgt auf ausgelassene Parameter kein Parameter mehr, den Sie angeben möchten,
so können Sie diese komplett inklusive Kommata weglassen. Folgt aber noch eine
Wertangabe, so müssen die ausgelassenen Stellen dazwischen fast immer durch
Kommata markiert werden. Nur so kann LotusScript erkennen, welchen Parameter
Sie spezifizieren wollten und welche auszulassen sind.
Beispielsweise kann MessageBox bis zu drei Parameter verarbeiten: Anzeigetext,
Optionen und Titel. Wenn Sie nur einen Text anzeigen möchten, dann genügt fol-
gender Aufruf:
MessageBox “Dieser Text soll angezeigt werden“
Möchten Sie auch einen Titel angeben, aber keine Optionen, dann sieht der Aufruf
so aus:
MessageBox “Dieser Text soll angezeigt werden“, , “Dies ist der Titel“
Bevor wir etwas mehr über Klassen und Objekte auch im allgemeinen Sinne spre-
chen, möchte ich Ihnen zunächst kurz die praktische Arbeit damit vorstellen. So
bekommen Sie am schnellsten eine Vorstellung davon, was Klassen und Objekte
für Sie als LotusScript-Programmierer bedeuten.
Die erste Anweisung lautet Dim session As New NotesSession und legt ein NotesSes-
sion-Objekt an. Mit Set agentThis = session.CurrentAgent bekommen wir Zugriff auf
den Agenten, in dem das Script läuft, also auf »unseren« Agenten. Sein Name wird
mit agentThis.Name ermittelt und anschließend über die Messagebox ausgegeben.
Sie werden gesehen haben, dass drei neue Dinge aufgetaucht sind: die Anweisung
New innerhalb der Variablendeklaration der ersten Zeile, dann das Schlüsselwort Set
bei der ersten Zuweisung sowie der Punktoperator zwischen session und Current-
Agent bzw. agentThis und Name. Zudem fallen noch die neuen »Datentypen« NotesSes-
sion und NotesAgent in den Deklarationszeilen auf.
Bevor wir die Angelegenheit Stück für Stück durchgehen, benötigen wir noch etwas
Theorie.
Objekt
Objekte sind benannte Programmteile, die sich ein wenig wie die Gegenstän-
de in der realen Welt verhalten. Sie verfügen über eigene Daten (die Eigen-
schaften), die sie unabhängig von anderen Objekten verwalten. Sie haben
Prozeduren (die Methoden), die ihre Aktionsmöglichkeiten ausmachen.
Wenn Sie diese Definition mit der Definition der Prozedur auf Seite 48 vergleichen,
dann werden Sie feststellen, dass sich die Definitionen ein wenig ähneln. Auch
Objekte bieten also die Möglichkeit, eine bestimmte Menge Code auszulagern und
diese bei Bedarf einfach über den Objektnamen aufzurufen. Dieser Code wird
innerhalb der Klasse wie eine Prozedur formuliert und wird fast genauso mit Call
aufgerufen. Solche Objekt-»Prozeduren« nennt man Methoden.
Darüber hinaus haben Objekte im Allgemeinen sogenannte Eigenschaften. Das sind
vereinfacht gesagt Variablen, die diverse für das Objekt relevante Daten speichern
und ausgeben können. Eigenschaften werden häufig von Methoden bearbeitet,
geändert und ausgegeben. Bei einem streng objektorientierten Design legt man
sogar Wert darauf, dass auf Eigenschaften nur in Ausnahmefällen direkt zugegrif-
fen weden kann.
Man könnte auch sagen, dass Objekte eine Zusammenfassung von verschiedenen
Prozeduren mit gemeinsamen Daten darstellen.
Klasse
Eine Klasse ist eine Art Schablone, mit der Objekte beschrieben und erstellt
werden können.
Beim Programmieren wird zunächst eine Klasse deklariert. Wenn man mit ihr
arbeiten möchte, erzeugt man ein Objekt (mit New). Die in LotusScript eingebauten
Notes-Klassen braucht man natürlich nicht mehr zu deklarieren.
Objekte sind genaue Abbilder ihrer Klasse. Allerdings ist die Klasse nur die »Bauvor-
schrift«. Eine Klasse kann in einem Programm nur einmal existieren. Davon abge-
leitete Objekte kann man so viele erstellen, wie man möchte bzw. wie der Arbeits-
speicher hergibt. Die Objekte sind unabhängig voneinander. Insbesondere ihre
Eigenschaften sind eine »Privatangelegenheit«, in die sich kein anderes Objekt ein-
mischen sollte. Diesen letzten Punkt kann man zwar durch schlechte Konzeption
und Programmierung umgehen. Man sollte es aber nicht tun, da die Unabhängig-
keit der Objekte voneinander ein wichtiges Merkmal des objektorientierten
Designs (OOD) darstellt.
Diese eher anschauliche Definition wird nicht jeder wissenschaftlichen Betrach-
tungsweise standhalten. Aber wir betreiben hier ja auch keine wissenschaftlichen
Forschungen, sondern möchten mit den Objekten arbeiten.
Verlassen wir also die Theorie wieder, und wir wenden uns unserem Beispiel zu.
erzeugen kann. Sobald diese Anweisung abgearbeitet wurde, existiert das Objekt
und kann im Programmcode verwendet werden.
Der Klassenname ist die Bezeichnung für die Art des Objekts. In unserem Beispiel
haben wir ein Objekt von der Art NotesSession erzeugt.
Damit haben wir ein Objekt, das uns nützliche Auskünfte zur momentanen Notes-
Sitzung geben kann. Beispielsweise »weiß« das Objekt, wer der gerade geöffnete
Agent ist. Was es nicht weiß, ist, wie dieser Agent heißt. Das weiß er aber natürlich
selbst.
Deswegen haben wir das session-Objekt erst einmal nach dem geöffneten Agenten
»befragt«. Dazu wird die Eigenschaft CurrentAgent mit dem »Punktoperator« ange-
sprochen (englisch Dot), wobei das Objekt wie eine Variable mit dem Variablen-
namen notiert wird:
session.CurrentAgent
Hinweis
Dazu muss man sagen, dass der Objektname in der Tat der Name einer Vari-
ablen ist. Denn beim Erzeugen mit Dim...As New... wurde zunächst eine Vari-
able mit dem Objektnamen angelegt (hier session), dann wurde das Objekt
erzeugt und im Programmspeicher abgelegt, und anschließend wurde die
Adresse des Objekts in der Variablen abgelegt.
Da wir für die Variable den Datentyp einer Klasse bestimmt haben (NotesSes-
sion), weiß LotusScript, dass es hier nicht einfach den Inhalt, nämlich die
Objektadresse, ausgeben soll. Stattdessen sucht es automatisch das Objekt an
der angegebenen Adresse und wertet dieses aus. Dass hier im Hintergrund
Speicheradressen eine Rolle spielen, brauchen Sie als LotusScript-Entwickler
im Prinzip nicht zu wissen. Es wird Ihnen aber verständlicher machen,
warum eine Objektzuweisung ein vorangestelltes Set benötigt, während
andere Variablenwerte ohne Set zugewiesen werden.
In der Eigenschaft CurrentAgent befindet sich ein NotesAgent-Objekt des gerade lau-
fenden Agenten, also »unseres« Agenten. Das Objekt wurde bereits intern von
NotesSession erzeugt. Deshalb können wir es wie gezeigt einfach mit dem Punktope-
rator abfragen und benötigen keine zweite New-Anweisung.
Wir weisen das Objekt einer Objektvariablen vom Typ NotesAgent zu. Wie schon
gesagt, muss eine Objektzuweisung immer durch ein vorangestelltes Set kenntlich
gemacht werden:
Set agentThis = session.CurrentAgent
Allerdings interessiert uns dieser Agent selbst nicht so sehr, sondern sein Name.
Den erfährt man mit:
strAgentName = agent.Name
Diese Eigenschaft von NotesAgent gibt eine Zeichenkette aus, also einen String-Wert.
Daher können wir sie einfach einer String-Variablen zuweisen (natürlich ohne Set,
das ja nur zur Zuweisung von Objektreferenzen verwendet wird).
Und den Rest kennen Sie ja bereits.
In Verbindung mit NotesSession hatten wir also gesehen, wie Objekt-Eigenschaften
gelesen werden. Im folgenden Abschnitt werden wir eine Objekt-Methode verwen-
den.
Hinweis
Da wir gerade über Dialogboxen sprachen: Sie werden bemerkt haben, dass es
offensichtlich »an zwei Stellen« Dialogboxen gibt, denn wir hatten ja schon
die LotusScript-Funktionen MessageBox und InputBox als solche kennengelernt.
Hier zeigt sich wieder die Zweiteilung: einerseits die Sprache LotusScript –
andererseits die von Notes/Domino zusätzlich zur Verfügung gestellten Klas-
sen, deren Methoden sich hier und da ein wenig mit LotusScript-Funktionen
überschneiden.
Das ist insbesondere bei den Dialogboxen, der Arbeit mit Dateien und bei
OLE-Objekten der Fall. Bei der Arbeit mit den Klassen werden Sie feststellen,
dass deren Angebote meistens über die Möglichkeiten von LotusScript hin-
ausreichen bzw. anderen Anforderungen genügen. Es hat also beides seine
Berechtigung.
Sub Initialize
Dim dbMail As New NotesDatabase("", "")
Dim ws As New NotesUIWorkspace
Sie sehen, das Programm ist sehr kurz, und es wird praktisch nur mit Notes-Objek-
ten gearbeitet.
Als Erstes benötigen wir wieder ein NotesDatabase-Objekt. Dies steht für Datenban-
ken im Hintergrund, also für den nicht sichtbaren Teil. Wir möchten gern erfah-
ren, wo sich die Mail-Datei des Benutzers befindet (also Ihre Mail-Datei, wenn Sie
das Script laufen lassen). Dazu bietet uns die Klasse NotesDatabase die Methode Open-
Mail an. Der Aufruf öffnet die Datenbank nur im Hintergrund, es wird (noch) kein
Fenster sichtbar. Wir nehmen als sicher an, dass Sie eine Mail-Datei besitzen, dass
alles richtig konfiguriert ist und dass Sie Zugriff auf die Datenbank haben. Dann
werden in dem dbMail-Objekt nach dem Aufruf von OpenMail der Server und der
Dateipfad (FilePath) vermerkt sein. Diese Angaben übergeben wir als die ersten bei-
den notwendigen Parameter an ComposeDocument. Diese Methode soll auch noch wis-
sen, welche Maske sie für das neue Dokument verwenden soll, und wir geben
»Memo« an. Nach ihrem Aufruf wird ein Fenster mit der Mail-Maske (»Memo«)
sichtbar.
Jetzt können Sie die neue Mail am Bildschirm wie jede andere füllen und absenden.
In diesem Script wurde zweimal eine Objekt-Methode aufgerufen: mit NotesData-
base.OpenMail eine parameterlose und mit NotesUIWorkspace.ComposeDocument eine
andere mit Parametern.
Der Aufruf ist sehr ähnlich wie bei den Prozeduren, die Sie früher kennengelernt
haben, und erfolgte mit Call.
Hinweis
Call kann bei allen Arten von Prozeduraufrufen auch weggelassen werden.
Genauso können die runden Klammern weggelassen werden, wenn keine
Parameter übergeben werden. Aus Call dbMail.OpenMail() würde dann:
dbMail.OpenMail
Das sieht aber fast wie die Abfrage einer Eigenschaft aus, und deshalb ver-
wende ich bei solchen Aufrufen immer Call und setze dann in der Regel auch
die Klammern. Andere entscheiden sich für die kürzere Variante. Das ist eine
reine Geschmackssache.
Der Unterschied ist nur, dass wie bei den Eigenschaften der Name der Objekt-
variablen davor gesetzt und mit dem Punktoperator verbunden werden muss.
Wenn eine Methode einen Rückgabewert hat, kann die Zuweisung zu einer Variab-
len analog zu der Vorgehensweise bei Funktionen erfolgen. Häufig geben die
Methoden Objekte zurück, die dann mit Set zugewiesen werden müssen. Unsere
ComposeDocument-Methode gibt ein NotesUIDocument-Objekt zurück, das man für wei-
tere Bearbeitungen nutzen kann. Die Zuweisung könnte so erfolgen:
...
Dim uidoc As NotesUIDocument
...
3.9.1 Aktionsschaltflächen
Wenn Sie auf einer Maske, einer Seite oder in einer Aktionsleiste eine Schaltfläche
einbauen, dann kann diese anschließend ähnlich wie ein Agent programmiert wer-
den. Wenn sich der Fokus auf der Schaltfläche befindet, zeigt sich das Program-
mierfenster unten ähnlich wie bei dem Agenten. Auch müssen wir zunächst die
Sprache LotusScript auswählen (vorher wird eine Formel angezeigt) und sehen
dann die einzelnen Eventhandler. Bei Schaltflächen gibt es außer den schon
bekannten Sub Initialize und Sub Terminate noch Sub Click und Sub ObjectExecute.
Soll der Button das Programm auf einen Klick hin ausführen, dann ist Sub Click die
richtige Wahl. (Sub Initialize würde beim Laden der Maske und dem damit ver-
bunden Initialisieren der Schaltfläche aufgerufen, was selten das ist, was wir benö-
tigen).
Print docThis.UniversalID
End Sub
Listing 3.13 Zugriff auf das Hintergrunddokument
Wird ein Agent bei geöffneter Maske aufgerufen, so startet man mit einem Notes-
UIWorkspace-Objekt, mit dem man als CurrentDocument auf das Vordergrunddokument
Zugriff erlangt und anschließend auf das Hintergrunddokument:
Sub Initialize
Dim ws As NotesUIWorkspace
Dim docThis As NotesDocument
Print docThis.UniversalID
End Sub
Listing 3.14 Zugriff auf das Hintergrunddokument der geöffneten Maske über das NotesUIWork-
space-Objekt
3.9.4 Datenbanken
Auf das Öffnen und Schließen einer Datenbank kann mit Eventhandlern im soge-
nannten DATENBANK-SCRIPT in den DATENBANKRESSOURCEN aus dem Abschnitt ANDERE
reagiert werden. Was man vielleicht weniger vermuten würde, ist, dass hier auch
Eventhandler für die Behandlung von Drag&Drop-Operationen verborgen sind.
3.9.5 Gliederungen
Gliederungen können mit den Klassen NotesOutline und NotesOutlineEntry bearbei-
tet werden. Sie können Einträge hinzufügen, entfernen und ihre Ziele verändern.
Die Startpunkte für diese Aktionen können in irgendeinem passenden Eventhand-
ler liegen (in einer Schaltfläche: Sub Click, in einem Agenten: Sub Initialize usw.).
LIOTHEK angelegt werden. Geben Sie ihnen sprechende Namen, die die darin abge-
legten Programmteile charakterisieren. Dann ist es später leichter, die darin
befindlichen Codestücke zu finden.
Wenn eine Bibliothek neu angelegt wird, präsentiert sie sich zunächst wie ein noch
leerer Agent (Abbildung 3.27).
Zunächst trägt sie den Namen “(unbenannt)“, was man in den Eigenschaften ändert.
Spätestens beim Abspeichern wird man dazu aufgefordert.
Nun wird der auszulagernde Code hier genauso abgelegt wie in anderen Modulen
auch. Nur werden Sie in der Regel die beiden Routinen Sub Initialize und Sub Ter-
minate unberührt lassen. Die erste kommt nur in Betracht, wenn Sie möchten, dass
beim Laden der Bibliothek gewisse Werte voreingestellt werden, beispielsweise glo-
bale Variablen (die Sie als guter Programmierer hoffentlich nur in Ausnahmefällen
verwenden werden). Sub Terminate kann zum Aufräumen beim Entladen der Biblio-
thek verwendet werden können.
Bibliotheken werden mit der Anweisung
Use strLibraryName$
eingebunden. Diese muss im (Options)-Abschnitt eines anderen Moduls, also eines
Agenten, einer Maske oder einer Ansicht usw. aufgeführt werden. Wenn dieses
andere Modul geladen wird, dann wird die Bibliothek automatisch mitgeladen.
Wird das andere Modul entladen, dann wird auch die Bibliothek entladen.
Es können mehrere Use-Anweisungen untereinander geschrieben werden. Außer-
dem können Ketten gebildet werden, indem eine Bibliothek eine weitere angibt,
die von ihr selbst geladen wird, usw.
Hinweis
Die Auflösung der verketteten Inhalte ist in der Vergangenheit nicht immer
zuverlässig gewesen. Dies hat sich insbesondere in mysteriösen Fehlern von
Hintergrundagenten bemerkbar gemacht. Deshalb verzichte ich auch heute
noch auf eine Verkettung bzw. führe die verketteten Bibliotheken im ausfüh-
renden Modul (zum Beispiel einem Agenten) lückenlos auf. So kann man
sicher sein, dass der LotusScript-Interpreter alle findet.
Anschließend kann der (öffentliche) Code aus der Bibliothek wie jeder andere ver-
wendet werden. Das heißt, Prozeduren, Funktionen, Klassendefinitionen und
benutzerdefinierte Datentypen können verwendet werden, als wären sie in demsel-
ben Modul geschrieben worden. Es muss nur darauf geachtet werden, dass ein zu
verwendendes Element nicht als Private deklariert wurde.
tPerson.Geburtsjahr = in_iYearOfBirth%
tPerson.Nachname = in_strLastName
tPerson.Vorname = in_strFirstName
tPerson.Ort = in_strCity
Print "Datensatz erzeugt für " & tPerson.Vorname & " " tPerson.Nachname
End Sub
Listing 3.15 Variable vom Datentyp Person erstellen und mit Inhalten füllen
Eigene Klassen und Objekte erstellen 71
Führt man den folgenden Aufruf durch, dann kann man das Füllen der Typ-Ele-
mente beobachten und erhält zum Schluss in der Statuszeile die Information
“Datensatz erzeugt für Code Coder“:
Call FillPersonRecord("Code", "Coder", "Kodistan", "1972")
Hier sieht man auch, dass es nicht darauf ankommt, in welcher Reihenfolge die
einzelnen Elemente des selbst erstellten Datentyps angesprochen werden.
Solche Datenstrukturen lassen sich gut verwenden, um zusammenhängende
Daten, auf denen umfangreiche Aktionen ausgeführt werden, sinnvoll zusammen-
zufassen. Das kann zum Beispiel in der Schnittstellenprogrammierung sehr zur
Erstellung eines gut strukturierten Programms beitragen.
Andere Einsatzgebiete sind die Bearbeitung von Dateien im wahlfreien Zugriffs-
modus oder der Austausch von Daten mit der Notes C-API, bei dem oft zusammen-
gesetzte Datentypen gefragt sind.
Jetzt könnten wir für jeden dieser Befehle eine Prozedur schreiben, die die damit
verbundenen Aktionen durchführt:
Sub NachRechtsAusschlagen
End Sub
Sub NachLinksAusschlagen
End Sub
Sub GeradeZiehen
End Sub
Wenn der Autofahrer nach rechts abbiegen möchte, ruft er Call NachRechtsAusschla-
gen auf, will er nach links, so führt er Call NachLinksAusschlagen aus.
Wir stellen uns jetzt ein noch moderneres Auto vor, ein Auto, in dem die einzelnen
Vorderräder nicht mehr mechanisch miteinander verbunden sind. Jedes Rad wird
getrennt durch Software angesteuert. Auf diese Weise können die Räder schwieri-
gen Bedingungen viel flexibler angepasst werden, als das mit einer starren Lenk-
achse der Fall ist. Jetzt hat jedes Rad einen eigenen Stellmotor. Jedes Rad empfängt
die Befehle Call NachRechtsAusschlagen, Call NachLinksAusschlagen und Call GeradeZie-
hen.
Natürlich möchte der Fahrer nicht jedem Rad diese Befehle einzeln mitteilen. Es
gibt also ein zentrales Programm, das die Verwaltung übernimmt. Außerdem gibt
es die Aufrufe Call NachRechtsAusschlagen, Call NachLinksAusschlagen und Call Gerade-
Ziehen jetzt sowohl für die zentrale Lenksoftware als auch für die Rädersoftware.
Und nun beginnt sich abzuzeichnen, dass unsere Lenkung softwaretechnisch
schwierig werden wird. Wir müssen die Prozeduren von Lenksoftware und Räder-
software auseinander halten. Also schreiben wir:
Sub LenkungNachRechtsAusschlagen
End Sub
Sub LenkungNachLinksAusschlagen
End Sub
Sub LenkungGeradeZiehen
End Sub
Sub RechtesRadNachRechtsAusschlagen
End Sub
Sub RechtesRadNachLinksAusschlagen
Eigene Klassen und Objekte erstellen 73
End Sub
Sub RechtesRadGeradeZiehen
End Sub
Sub LinkesRadNachRechtsAusschlagen
End Sub
Sub LinkesRadNachLinksAusschlagen
End Sub
Sub LinkesRadGeradeZiehen
End Sub
Alle diese Prozeduren müssen wir ins Hauptprogramm einbinden und dort anspre-
chen. (Man könnte natürlich eine Prozedur schreiben, die die Lenkung repräsen-
tiert und damit die Steuerung der einzelnen Räder vom Hauptprogramm auslagert.
Allerdings würde dadurch das Problem nicht gelöst, dass viele Prozeduren vorhan-
den sind, die auch von anderen Programmteilen missbraucht werden könnten.)
So wäre es, wenn wir den sogenannten funktionalen Ansatz wählen würden. Das
machen wir aber nicht; wir entscheiden uns natürlich für die Erstellung von Objek-
ten, dafür ist das Kapitel ja gedacht. Wenn wir die erst einmal haben (wir nennen
sie hier schon einmal Lenkung und Rad), dann gestalten sich die Aufrufe sehr einfach:
Call Lenkung.NachRechtsAusschlagen
Call Lenkung.NachLinksAusschlagen
Call RechtesRad.NachRechtsAusschlagen
Call LinkesRad.NachRechtsAusschlagen
Jetzt werden alle beteiligten Objekte ihre eigene Prozedur NachRechtsAusschlagen
haben. Es gibt keine Namenskonflikte mehr, weil die Prozeduren den Objekten als
Methoden fest zugeordnet sind. Die feste Zuordnung bringt es mit sich, dass die
Prozeduren nicht mehr so leicht im falschen Zusammenhang angewendet werden
können. Es müssen keine Namensungetüme zur Unterscheidung der vielen »her-
umliegenden« Prozeduren mehr geschaffen werden.
Zunächst schreiben wir eine Klasse, die dann die Vorlage für die Räder bildet.
Klassen werden in LotusScript mit der Anweisung Class erstellt (im Modul-
Abschnitt (Declarations)):
Class Rad
Sub NachRechtsAusschlagen
End Sub
Sub NachLinksAusschlagen
End Sub
Sub GeradeZiehen
End Sub
End Class
Mit der Dim-Anweisung können wir dann für jedes Rad ein Objekt dieser Klasse
deklarieren:
Dim radVorneRechts As Rad
Dim radVorneLinks As Rad
Soll das rechte Vorderrad nach links lenken, dann lautet der Aufruf:
radVorneRechts.NachLinksAusschlagen
Wenn Ihnen der Vorteil noch nicht ganz einleuchtet, dann haben Sie noch ein
wenig Geduld, es kommt gleich besser.
Es wurde ja bereits festgestellt, dass Objekte eigene Variablen besitzen können, die
sogenannten Eigenschaften. In unserem Beispiel wird sich das gleich als sehr nütz-
lich erweisen.
Wenn die Software wissen soll, wie ein angeforderter Lenkausschlag umgesetzt wer-
den soll, dann muss sie eine Information darüber haben, wie der momentane Aus-
schlag ist. In einem konventionellen funktionalen Softwaredesign würden wir
diese Werte in einzelnen Variablen festhalten müssen (für jedes Rad eine), die dann
an zentraler Stelle gepflegt werden (von der Möglichkeit, statische Variablen zu ver-
wenden, sehen wir hier einmal ab).
Im objektorientierten Design brauchen wir die Variable nur einmal für die Klasse
Rad zu deklarieren, und schon sind zwei Stück verfügbar, für jedes der zwei Rad-
Objekte getrennt:
Class Rad
MomentanerLenkausschlag As Integer
...
End Class
Diese Eigenschaft muss jetzt natürlich noch »gefüttert« werden. Wir erstellen dazu
eine Methode SetzeLenkausschlag, die das für uns übernimmt (die Eigenschaft ist
nicht mit Public angelegt worden, denn wir möchten ja nicht, dass irgendwelche
anderen Programme Zugriff auf den Lenkausschlag bekommen und zum Sicher-
heitsrisiko werden).
Dabei stellen wir fest, dass die Methoden für das Rechts-, Links- und Geradeauslen-
ken sehr verwandt sind. Daher können wir die eigentliche Lenkarbeit der neuen
Methode überlassen, während die übrigen Methoden sie nur mit dem jeweils pas-
senden Wert aufrufen.
Um uns die Sache einfacher zu machen, gehen wir davon aus, dass es nur drei mög-
liche Werte gibt: 1 für rechts, -1 für links und 0 für geradeaus. Wir wollen dem Fah-
Eigene Klassen und Objekte erstellen 75
rer nicht zumuten, sich die Zahlen zu merken, und hinterlegen sie daher in der Rad-
Klasse selbst als entsprechende Eigenschaften. Wir müssen nur dafür sorgen, dass
sie richtig initialisiert werden. Das übernimmt die Methode New (New wird automa-
tisch aufgerufen, wenn ein Objekt mit New neu angelegt wird):
Class Rad
iMomentanerLenkausschlag As Integer
iRechts As Integer
iLinks As Integer
iGeradeaus As Integer
Sub New()
iRechts = 1
iLinks = -1
iGeradeaus = 0
End Sub
Sub SetzeLenkausschlag(in_iLenkrichtung%)
If Not ((in_iLenkrichtung = iRechts) Or (in_iLenkrichtung = iLinks) Or _
(in_iLenkrichtung = iGeradeaus)) Then Error 1999, “Ungültiger Wert“
iMomentanerLenkausschlag = in_iLenkrichtung
End Sub
Sub NachRechtsAusschlagen
Call SetzeLenkausschlag(iRechts)
End Sub
Sub NachLinksAusschlagen
Call SetzeLenkausschlag(iLinks)
End Sub
Sub GeradeZiehen
Call SetzeLenkausschlag(iGeradeaus)
End Sub
Hinweis
Eine Property (Eigenschaft) verhält sich einerseits wie eine Methode und
andererseits wie eine Zuweisung mit Einbahnstraßencharakter.
In einer Property können alle möglichen Anweisungen wie in einer Methode
ausgeführt werden. Sie kann sogar parametrisiert werden.
Auf der anderen Seite wird sie aber »von außen« wie eine Eigenschaft aufge-
rufen. Einer Property Get kann ein Wert zugewiesen werden, und eine Pro-
perty Set kann abgefragt werden. Gibt es nur eine davon, dann kann auch
nur die entsprechende Richtung verwendet werden.
Auf diese Weise kann man Eigenschaften programmieren, die nur gelesen,
nur beschrieben oder sowohl gelesen als auch beschrieben werden können.
Jetzt benötigen wir noch das Auto, an dem die Räder befestigt werden. Da es auch
Autos gibt, bei denen alle vier Räder in den Lenkvorgang einbezogen werden, mon-
tieren wir gleich vier. Normalerweise müssten wir natürlich zwei Sorten von
Rädern erstellen:
Class Auto
Räder List As Rad
Sub New()
Set Räder(“VorneRechts”) = New Rad()
Set Räder(“VorneLinks”) = New Rad()
Set Räder(“HintenRechts”) = New Rad()
Set Räder(“HintenLinks”) = New Rad()
End Sub
End Class
Die Montage geschah in Form einer Liste, weil sich das beim Erstellen der zentralen
Lenkvorrichtung als praktisch erweisen wird.
Jetzt erstellen wir die Lenkung. Sie muss alle vier Räder einstellen. Die hinteren
werden gegenläufig eingestellt. Dies erreichen wir durch das Voranstellen eines
einfachen Minuszeichens. Der Wert für Geradeaus ist 0, die Negation davon auch;
also können wir immer so vorgehen.
Allerdings ist es praktischer, der Lenkung nur eine einzige Methode zum Lenken
mitzugeben (es gibt ja auch nur ein Lenkrad). Dann muss der Fahrer beim Aufrufen
mitteilen, in welche Richtung er möchte. Damit er das in sprechender Weise tun
kann, bieten wir im die drei Eigenschaften RECHTS, LINKS und GERADEAUS an.
Dazu werden entsprechende Properties erstellt. Wir können sie auch intern ver-
wenden, wodurch wir uns die Variablen sparen, die wir für die Räder angelegt und
in New initialisiert haben.
Die Lenkung muss natürlich mit den Rädern in Verbindung stehen, also sehen wir
vor, die Räderliste bei Erstellung des Objekts zu übergeben und intern eine Referenz
festzuhalten. Diese wird dann beim Lenkvorgang abgearbeitet.
Eigene Klassen und Objekte erstellen 77
Class Lenkung
iMomentanerLenkausschlag As Integer
lstobRäder As Variant
Sub Lenken(in_iLenkrichtung%)
If Not ((in_iLenkrichtung = RECHTS) Or (in_iLenkrichtung = LINKS) Or _
(in_iLenkrichtung = GERADEAUS)) Then Error 1999, “Ungültiger Wert“
lstobRäder(“VorneRechts“).SetzeLenkausschlag(in_iLenkrichtung)
lstobRäder(“VorneLinks“).SetzeLenkausschlag(in_iLenkrichtung)
lstobRäder(“HintenRechts“).SetzeLenkausschlag(- in_iLenkrichtung)
lstobRäder(“HintenLinks“).SetzeLenkausschlag(- in_iLenkrichtung)
iMomentanerLenkausschlag = in_iLenkrichtung
End Sub
Sub New()
Set lstobRäder(“VorneRechts”) = New Rad()
Call obAuto.Lenkrad.Lenken(obAuto.Lenkrad.RECHTS)
Call obAuto.Lenkrad.Lenken(obAuto.Lenkrad.LINKS)
Call obAuto.Lenkrad.Lenken(obAuto.Lenkrad.GERADEAUS)
Ein Aufruf, und es geht in die Richtung, die wir vorgeben. Die ganzen Interna (wie
viele und welche Räder in welche Richtung gelenkt werden) interessieren uns nicht
– so, wie es sich bei einem richtigen Auto gehört.
Und genauso einfach kann sich Software bedienen lassen. Einige Vorteile dieser
Programmierungsart sind:
Die hohe Anschaulichkeit: Das macht das Programmieren einfacher.
Es entwickelt sich auf natürliche Weise eine sinnvolle Architektur.
Es werden wie von allein abgeschlossene Module (die Klassen) erstellt, die sich
einzeln fortentwickeln und warten lassen.
Es bilden sich klare Schnittstellen heraus, was den Überblick und die Austausch-
barkeit fördert.
Es wird eine hohe Wiederverwendbarkeit des Codes und von Codeteilen er-
reicht.
Aber das war noch nicht alles. Sie werden gleich noch mehr Möglichkeiten zur
Codeoptimierung kennenlernen. Außerdem wird deutlich werden, wie sich ein
objektorientiertes Design mit der Zeit fortentwickeln kann, ohne dass das eigent-
liche Program davon betroffen ist. Der Benutzer merkt davon nichts.
Vererbung
Wenn wir also etwas genauer hinsehen, dann entdecken wir gewisse Gemeinsam-
keiten zwischen der Rad-Klasse und der Klasse Lenkung. Beide beschäftigen sich mit
der Lenkung, die eine für ein einzelnes Rad, die andere für alle Räder. Das bringt
eine gewisse Redundanz mit sich, die wir jetzt mithilfe der Vererbung beseitigen.
Eigene Klassen und Objekte erstellen 79
Zunächst erstellen wir eine Klasse, die die gemeinsamen Merkmale aufnimmt. Da
sich der Einsatz der Properties für die Lenkrichtungen ganz gut macht, überneh-
men wir diese Idee auch für die gemeinsame Klasse. Wir nennen sie Lenkbares-
Objekt:
Class LenkbaresObjekt
iMomentanerLenkausschlag As Integer
Sub Lenken(in_iLenkrichtung%)
If Not ((in_iLenkrichtung = RECHTS) Or (in_iLenkrichtung = LINKS) Or _
(in_iLenkrichtung = GERADEAUS)) Then Error 1999, "Ungültiger Wert"
iMomentanerLenkausschlag = in_iLenkrichtung
End Sub
Hinweis
Theoretisch könnte man die Räder ganz durch Objekte vom Typ LenkbaresOb-
jekt ersetzen. Aber um die Architektur klar zu gestalten, ist es besser, sie so zu
belassen. Denn die Verwendung von Rad-Objekten ist näher an der Anschau-
ung als die Verwendung von LenkbaresObjekt. Und umgekehrt wäre es nicht
sehr geschickt, die Klasse LenkbaresObjekt in Rad umzubenennen, denn dann
müssten wir die Klasse Lenkung von der Klasse Rad erben lassen, und auch das
ist nicht sehr anschaulich. Es ist einfach so, dass die Räder und die Lenkung
sehr viel gemeinsam haben, aber dennoch sehr verschieden sind. Wir können
also nur die Gemeinsamkeiten in eine gemeinsame Basisklasse verlagern, aber
nicht sagen, dass die eine Klasse eine direkte Ableitung von der anderen sei.
Dann lassen wir also auch noch die Klasse Lenkung alle Eigenschaften und Metho-
den von LenkbaresObjekt erben. Alles, was vererbt wird, kann aus der Klasse Lenkung
entfernt werden. Das macht auch sie wesentlich kürzer. Die SetzeLenkausschlag-Auf-
rufe in die Rad-Objekte benennen wir dabei in Lenken um.
Class Lenkung As LenkbaresObjekt
lstobRäder As Variant
Sub Lenken(in_iLenkrichtung%)
If Not ((in_iLenkrichtung = RECHTS) Or (in_iLenkrichtung = LINKS) Or _
(in_iLenkrichtung = GERADEAUS)) Then Error 1999, "Ungültiger Wert"
lstobRäder("VorneRechts").Lenken(in_iLenkrichtung)
lstobRäder("VorneLinks").Lenken(in_iLenkrichtung)
lstobRäder("HintenRechts").Lenken(- in_iLenkrichtung)
lstobRäder("HintenLinks").Lenken(- in_iLenkrichtung)
iMomentanerLenkausschlag = in_iLenkrichtung
End Sub
End Class
Die Methode Lenken haben wir in dieser Klasse neu formulieren müsssen, da sie bei
dieser Klasse eine andere Bedeutung hat als bei den Rädern. Man nennt diese Art
der Neuformulierung Methoden überschreiben. Bei LotusScript muss man darauf ach-
ten, dass die Signatur der neu formulierten Methode genau mit der Signatur der
vererbten übereinstimmt (damit sind gemeint: die Art der Prozedur, der Name, der
oder die Parameter und der Rückgabewert). Für diejenigen, die sich mit der objekt-
orientierten Programmierung auskennen: Methoden von LotusScript-Klassen kön-
nen überschrieben, aber nicht überladen werden.
Eigene Klassen und Objekte erstellen 81
Für die Erstellung des Auto-Objekts und die Lenkaufrufe ändert sich nichts:
Dim obAuto As New Auto
Call obAuto.Lenkrad.Lenken(obAuto.Lenkrad.RECHTS)
Call obAuto.Lenkrad.Lenken(obAuto.Lenkrad.LINKS)
Call obAuto.Lenkrad.Lenken(obAuto.Lenkrad.GERADEAUS)
Es gibt noch eine Verbesserungsmöglichkeit. Auch diese möchte ich Ihnen nicht
vorenthalten:
Die überschriebene Methode Lenken der Klasse Lenkung könnte einen Teil ihrer Arbeit
der Basisklasse überlassen. Denn die Abprüfung, ob der Parameter zur Lenkrich-
tung zulässig ist, ist in beiden gleich, und auch das Festhalten der internen Eigen-
schaft iMomentanerLenkausschlag wiederholt sich.
Wir können diese Schritte also entfernen, müssen nur dafür sorgen, dass zu Beginn
die Methode Lenken aus der Basisklasse aufgerufen wird. Dies geschieht mit dem
doppelten Punktoperator in Verbindung mit dem Namen der Basisklasse, also mit
der Anweisung Call LenkbaresObjekt..Lenken(in_iLenkrichtung):
Class Lenkung As LenkbaresObjekt
lstobRäder As Variant
Sub Lenken(in_iLenkrichtung%)
Call LenkbaresObjekt..Lenken(in_iLenkrichtung)
lstobRäder("VorneRechts").Lenken(in_iLenkrichtung)
lstobRäder("VorneLinks").Lenken(in_iLenkrichtung)
lstobRäder("HintenRechts").Lenken(- in_iLenkrichtung)
lstobRäder("HintenLinks").Lenken(- in_iLenkrichtung)
End Sub
End Class
Es ist zu empfehlen, sich den Ablauf des Programms mal im Debugger anzusehen.
Dort wird uns als Objekt der obersten Ebene unser Auto-Objekt angezeigt. Gleich-
zeitig kann man den Aufbau der Objekte für Auto, Lenkung und Räder und die
jeweiligen Zustände ansehen (repräsentiert durch die gemeinsame interne Eigen-
schaft iMomentanerLenkausschlag). Dazu habe ich einen Screenshot nach jedem der
vier Vorgänge erstellt, sodass man zumindest den groben Ablauf sehen kann:
Abbildung 3.28 Objektzustände der Räder und der Lenkung nach Erstellung der Variable obAuto.
Alle Lenkangaben in iMomentanerLenkausschlag stehen auf GERADEAUS (0).
Abbildung 3.29 Die Objektzustände nach dem Rechtslenken. Die Vorderräder stehen auf RECHTS (1).
Dasselbe trifft auf den in obLenkrad gespeicherten Gesamtzustand zu. Die
Hinterräder stehen auf LINKS (-1). Das ist aufgrund des geforderten gegenläufigen
Verhaltens korrekt.
Abbildung 3.30 Die Objektzustände nach dem Linkslenken. Alle Lenkzustände haben sich ins
Gegenteil verkehrt. Auch das ist korrekt.
Gültigkeitsbereiche und Module 83
Modifizierer
Um die Sache nicht zu einfach werden zu lassen, gibt es auch noch die Modifizierer
Public, Private und Static.
In der Designer-Hilfe werden sie zwar nicht so genannt, sondern einfach als abge-
wandelte Schlüsselwörter für die Variablendeklaration behandelt (als Ergänzung zu
Dim). Aber sie haben im Prinzip keine andere Funktion, als den Sichtbarkeitsbereich
zu verändern.
Modulebene
In einem Modul kann eine Variable mit einer öffentlichen oder einer privaten
Sichtbarkeit angelegt werden. Im ersten Fall sind die Variablen auch außerhalb des
Moduls sichtbar, im anderen Fall nur innerhalb. Wenn Sie beispielsweise eine
LotusScript-Bibliothek zu einem Maskenmodul hinzuladen (mit Use), dann sind
deren Variablen für die Maskenprozeduren nur sichtbar, wenn sie in der Bibliothek
als öffentlich deklariert wurden.
Hiermit können wir die beiden Begriffe Gültigkeit und Sichtbarkeit noch etwas
genauer fassen: Beide Modulvariablen sind gültig, solange das Modul geladen ist.
Doch ist die private Modulvariable nur innerhalb des Moduls sichtbar, also zugäng-
lich. Die öffentliche Variable ist auch außerhalb sichtbar.
Die gleichen Bedingungen gelten für die Deklaration von Prozeduren, Klassen und
Typen. Wird eines dieser Elemente mit Private deklariert, so ist es nur innerhalb des
Moduls sichtbar.
Auf diese Weise kann man Teile eines Moduls, die nur intern verwendet werden,
vor dem Zugriff von außen verstecken. Nach außen zeigt man dann nur die Ele-
mente, die in einem anderen Modul zur Anwendung kommen sollen. Diese kann
man dann als die API des Moduls bezeichnen.
Prozedurebene
Innerhalb von Prozeduren sind Variablen immer nicht-öffentlich. Da man hier
nicht zwischen privat und öffentlich entscheiden kann, gibt es in dieser Hinsicht
nur eine Deklarationsmöglichkeit, und die heißt Dim. Dim legt in diesem Fall eine
private Variable an, auch wenn das Schlüsselwort Private nicht zum Einsatz kom-
men kann.
Sie können aber eine andere Modifikation durchführen, die speziell für Prozeduren
gilt: Static. Mit Static wird eine statische Variable angelegt. Damit wird die Aufbe-
wahrungsdauer des zuletzt zugewiesenen Variableninhalts verändert. Normaler-
weise ist der Inhalt mit dem Verlassen einer Funktion vergangen. Bei statischen
Variablen wird er hingegen bis zum nächsten Aufruf der Funktion aufbewahrt.
Solange das Modul mit der Funktion geladen ist, wird man also bei jedem Aufruf
den zuletzt zugewiesenen Wert vorfinden.