home *** CD-ROM | disk | FTP | other *** search
- Dokumentation zur "OOP-Spezial"
- -------------------------------
-
- Eine Übersicht über alle Objekte dieser "Spezial" sowie den
- vollständigen Namen der Unit, in der sie implementiert sind,
- bietet "HIERARCH.DOC". Hier soll vor allem auf die Frage
- eingegangen werden, warum und wie die Hierarchie gemacht ist. Wie
- die Objekte implementiert sind, wie sie einzelne Detailprobleme,
- möge man bitte dem jeweiligen Quellcode entnehmen. Die Anwendung
- eines Objekts demonstriert entweder ein Demoprogramm, das sich im
- gleichen Verzeichnis oder in einem Unterverzeichnis mit dem Namen
- "DEMOS" findet, oder ein anderes Objekt, das von ihm Gebrauch
- macht.
-
- Diese Spezial stellt Objekte zur Verfügung, die sich der
- Programmierung von Oberflächenelementen auseinandersetzen. Zentral
- dafür ist wohl das Eventhandling (in der Unit "MouKey"). Hier kann
- sich ein genaues Studium (auch des Quellcodes) lohnen. Besitzer
- von Turbo Pascal 6.0 werden hier nicht viel neues, nur ganz anders
- aufgebautes finden. Diese Spezial ist vor dem Erscheinen von Turbo
- Pascal 6.0 noch unter dessen Vorgänger entwickelt worden.
- Allerdings liegen hier die Quellcodes vollständig bei, und
- vielleicht findet man bei ihrem Studium oder auch bei der
- Auseinandersetzung mit der Hierarchie, die in dieser Dokumentation
- diskutiert werden soll, Anstöße und Anregungen.
-
- Die Dokumentation ist nach Units geordnet, in welchem Verzeichnis
- sie sich befinden, verrät "HIERARCH.DOC".
-
-
- 1. UBase
- --------
-
- "Base" soll als Grundlage für alle anderen Objekte der Hierarchie
- dienen, alle Objekte auf einen gemeinsamen Nenner zurückführen und
- somit "kompatibel" machen. Da die Unit für Listen- oder auch
- Arrayverwaltung gebraucht werden soll, muß sie einen Destruktor
- enthalten, der den Speicher freigibt, der von und für das Objekt
- benötigt wird. Daher muß der Destruktor virtuell sein, damit auch
- immer der zum jeweiligen Objekttyp gehörige aufgerufen wird.
-
- Alle Objekte werden von "Base" abgeleitet, nicht nur scheinbare
- "Datenobjekte" wie z.B. "SAAItem". Das hat den Vorteil, daß man
- zweidimensionale, einfach oder doppelt verkettete Listen
- sehr leicht herstellen kann: Eine Liste, deren Einträge wiederum
- Listen sind. Das ist aber nur möglich, weil das Listenobjekt
- selbst auch von Base abgeleitet wurde. Sonst hätte es nicht in die
- Liste eingetragen werden können.
- Sehr einfach können so Sachen programmiert werden, die gar nicht
- vorgesehen waren. Müssen sie auch nicht, da der Polymorphismus und
- die Vererbung in Fällen wie dem obigen Beispiel das ihrige tun.
-
- Manchmal will man, wie z.B. in "AbstractList" eine
- Reimplementation von Methoden erzwingen. Damit dabei auch keine
- vergessen wird, kann die Methode "Abstract" aus der Unit "UBase".
- Als Parameter soll ihr der Name des Objekts übergeben werden,
- dessen Methode "Abstract" aufgerufen hat. Sie gibt diesen
- Objektnamen aus und erzeugt einen "Runtime-Error", um den
- Programmierer (hoffentlich noch in der Testphase!) darauf
- aufmerksam zu machen.
-
-
- 2. UMouse
- ---------
-
- "MouseObj" dient in der vorliegenden Version nur als Grundlage für
- spätere Mausobjekte. Die meisten Methoden werden von
- "EventHandlerObj" benötigt und sind deshalb implementiert. Hier
- gibt es kein großes "Warum" zu beantworten: Die Maus ist
- physikalisch gegeben, mitsamt all ihren Fähigkeiten. Die Methoden
- des Mausobjekts dienen nur zur Abfrage der Mausposition, zum
- Setzen der Position oder zum Ändern des Mauscursors. "MouseObj"
- stellt also nur ein (hier noch vereinfachtes) Modell der realen
- "Maus" dar; die Methoden spiegeln die Fähigkeiten der Maus wieder,
- die Daten von MouseObj speichern ihren Zustand (sichtbar,
- Position, Knöpfe gedrückt oder nicht...).
-
- Es macht keinen Sinn, mehr als eine Instanz des Mausobjekts in
- einem Programm zu verwenden: Schließlich ist physikalisch auch nur
- eine Maus vorhanden. Deshalb sind die Konstanten "DoubleClickTime"
- und "MouseDelay" auch modul-global definiert; sie sollten
- innerhalb eines Programmes immer die gleichen Werte aufweisen,
- sind aber als typisierte Konstanten gehalten, damit Änderungen
- möglich sind.
-
- Damit einem Programm die automatisch angelegte Instanz "Mouse"
- des Typs "MouseObj" zur Verfügung steht, muß es die Unit "MouKey"
- laden; ansonsten muß man sich selbst um eine Instanz des Typs
- "MouseObj" kümmern. Demonstriert wird der Gebrauch von "Mouse" in
- "MKDemo".
-
- "Mouse" müßte eigentlich nicht von "Base" abgeleitet werden, da
- es keinen Sinn macht, Mausobjekte in einer Liste zu verwalten.
- Aber man kann nie wissen, wie "MouseObj" einmal erweitert werden
- soll; also dient es der Offenheit. Außerdem wird die
- Hierarchie dadurch einheitlicher. Ein anderer Vorteil, den man
- manchmal ausnutzen möchte, ist, daß durch die Einführung von
- "Base" alle von ihm abgeleiteten Objekte etwas gemeinsam haben:
- Der Offset-Zeiger auf die VMT (virtuelle Methodentabelle) ist vor
- allen Variablen eines Objekts gespeichert.
-
-
- 3. MouKey
- ---------
-
- Hier dreht sich alles um die Verarbeitung von hereinkommenden
- Ereignissen. Das eigentliche Objekt dieser Unit ist
- "EventHandlerObj", das die Ereignisse in zwei "EventQueue"-
- Objekten verwaltet. Eine dieser "Warteschlangen" ist für die
- Tastaturevents, die andere für Mausevents reserviert. Damit wird
- verhindert, dass Maus- und Tastaturevents sich gegenseitig
- durcheinander bringen können. Das wäre der Fall, wenn gerade ein
- Mausevent bearbeitet würde und durch ein Tastaturevent
- unterbrochen wird. Dann würde zuerst das in die Queue eingefügt
- und danach wieder mit der Abarbeitung des Mausevents fortgefahren.
- Inzwischen wäre aber die Queue verändert und das Chaos perfekt.
-
- Eine "EventQueue" enthält neben zwei Indexzeigern ein Array
- reiner Datenobjekte vom Typ "EventObj", der ein Event beschreibt.
- Dazu sind der Zeitpunkt und der Typ des Events nötig. Je nach
- "EventType" sind spezifische Daten für die Beschreibung des Events
- von Bedeutung (siehe Listing). So ist bei einem Mausevent die
- Position des Mauscursors und die Anzahl der gedrückten Knöpfe von
- Interesse, bei einem Tastaturevent muß zwischen verschiedenen
- Arten unterschieden werden.
-
- Daß diese Objekte reine Datenobjekte sind, widerspricht streng
- genommen den Prinzipien von OOP: Diese Objekte enthalten nur Daten
- und keine Methoden; die Daten werden mit Direktzugriffen gelesen
- und verändert. Doch der Grund liegt in der
- Ausführungsgeschwindigkeit: Die Methoden von "EventHandlerObj",
- die die Verwaltung vornehmen, werden indirekt von dem Ersatz für
- den Interrupt $09 (Keyboardinterrupt) und einem installierten
- Maushandler des Assemblermoduls "MouKeyAs.Asm" aufgerufen. Und bei
- der Interruptbearbeitung geht es bekanntlich um die maximale
- Ausführgeschwindigkeit. Deshalb ausnahmsweise der Bruch mit der
- reinen objektorientierten Programmierung. Daß es trotzdem noch
- Objekte sind und keine Records, liegt an der Erweiterbarkeit der
- Objekte. Ein Kindobjekt von "EventObj" könnte auch noch fähig
- sein, die Informationen eines Lichtgriffel-Ereignisses zu
- speichern. Wäre "EventObj" ein Record, wäre diese Erweiterung
- nicht so einfach möglich.
-
- Nur wenige Methoden von "EventHandlerObj" werden von einem
- Programm benötigt werden; welche das sind und wie man Events vom
- Typ "EventObj" auswertet, das zeigt das Demoprogramm "MKDemo". Mit
- ihm kann man gleichzeitig eine Liste der Events und ihren
- Auswertungen, z.B. Tastaturcodes, erstellen. Wie für verschiedene
- Ereignisse ein Handler installiert wird, demonstriert auch das
- Demoprogramm.
-
- Die Unit MouKey versucht immer, ein "MouseObj" und ein
- "EventHandlerObj" anzulegen. Auch hier gilt: Nur ein Eventhandler-
- Objekt pro Programm macht Sinn, denn mehrere würden alle die
- gleichen Ereignisse bearbeiten. Damit es aber möglich ist, ein
- erweitertes Eventhandler-Objekt, das natürlich von
- "EventHandlerObj" erbt, zu verwenden, gibt es "DeInstall" und
- "ReInstall". Analog für die Maus existieren "DeInstallMouse" und
- "ReInstallMouse", falls man ein eigenes, erweitertes Mausobjekt
- benutzen will. Auch das wird in "MKDemo" demonstriert.
-
-
- 4. UBuffer und DiskBuf
- ----------------------
-
- Das Objekt "Buffer" aus der Unit "UBuffer" implementiert einen
- Puffer im normalen Heap. Es organisiert den Puffer zeilenweise und
- speichert für jedes Zeichen ein Attribut. Es stellt einige
- Fähigkeiten zur Verfügung, die sich an seiner Methodenliste
- ablesen lassen. Einige Methoden sind intern, sollten also nur von
- Nachkommen gebraucht und überschrieben werden. Die meisten der
- anderen Methoden sind "Set-..." oder "Get-..." Methoden, die
- einzelne Variablenwerte setzen oder lesen. Am wichtigsten sind
- wohl die Methoden "WriteStr" und "WriteStrXY". Die Arbeit mit
- "Buffer" und auch mit "DiskBuf" demonstriert "BufDemo".
-
- Wem der Hauptspeicher zuwenig ist, der kann "DiskBuf" einsetzen:
- Dieses Objekt lagert, sobald im Heap kein Platz mehr ist, auf die
- Festplatte oder Diskette (allerdings wegen der langsamen Lese- und
- Schreibzugriffe nicht sehr ratsam) aus und arbeitet gemixt mit
- Heap und Festplattenspeicher. Um all das braucht sich der Anwender
- von "DiskBuf" jedoch nicht zu kümmern, hauptsächlich wird er die
- Methoden benötigen, die der Vorfahre, "Buffer", schon enthalten
- hat. Das Auslagern läuft intern ab, sollte nicht von außen
- beeinflußt werden, nur über die dafür vorgesehenen Methoden. Ist
- die Festplatte oder Diskette voll, so arbeitet "DiskBuf" weiter,
- nimmt aber keine neuen Daten mehr an. Nur die schon vorhandenen
- Daten können noch manipuliert werden. Dieser Fehlerfall muß
- explizit mit "GetErrorL1" abgefragt werden. Liegt ein anderer
- Fehler vor, z.B. ungültiges Laufwerk, so kann der Dos-Fehlercode
- mit "GetDosError" abgefragt werden. "GetErrorL1" liefert nur
- "BufFileErr" zurück.
-
-
- 5. Lists
- --------
-
- Um eine einfach verkettete Liste zu verarbeiten, braucht es ein
- Knotenobjekt, das einen Zeiger auf das nächste Knotenobjekt
- enthält ("SListNode"). "DListNode" erweitert "SListNode" um einen
- "Rückwärtszeiger" auf den Vorgängerknoten. Die Aufgabe dieser
- Objekte besteht nur darin, diese Knoten zu speichern und auf
- Anfrage bekanntzugeben.
-
- Es wäre möglich, daß eine Liste nun Einträge verlangt, die von
- diesen Objekten abgeleitet sind und somit immer den Zeiger auf das
- nächste oder vorhergehende Objekt im Glied der Liste automatisch
- auch enthalten. Doch man kann die Abschottung weiter treiben, so
- daß die zu verwaltenden Objekte überhaupt nicht zu wissen
- brauchen, wie sie verwaltet werden. Das heißt, sie kennen die
- Knoten nicht, sondern können ganz normal von "Base" erben. Sonst
- müßte man alle Objekte von "SListNode" oder "DListNode" statt von
- "Base" ableiten. Das wäre sicherlich nicht wünschenswert.
-
- "SListNode" enthält zusätzlich einen Zeiger auf ein Objekt vom Typ
- "Base". Auf was für einen Objekttyp dieser Zeiger nun zur Laufzeit
- wirklich zeigt, spielt für "SListNode" keine Rolle, das Objekt
- stammt von "Base" ab, also kann sein Destruktor aufgerufen werden,
- sobald derjenige von "SListNode" aufgerufen wird. Mehr braucht
- "SListNode" von dem Objekt, für das es den Knoten bildet, gar
- nicht zu wissen. Natürlich entsteht dadurch ein gewisser
- "Overhead", doch den muß man in Kauf nehmen.
-
- Hier wird deutlich sichtbar, was für einen enormen Vorteil die
- Verwendung eines abstrakten Objekts "Base" als Vater oder
- Grossvater oder auch Urur...grossvater mit sich bringt: Alle von
- "Base" erbenden Objekte können mit demselben Listenobjekt
- verwaltet werden. Polymorphie macht's möglich !
-
- Wenn man eine objektorientierte Toolbox eines anderen
- Programmierers benutzt und dieser sich an das gleiche Prinzip
- gehalten hat (also auch einen Grundtypen in seiner Hierarchie
- führt), kann man eigene Objekt schnell an die Toolbox anpassen:
- Das eigene "Base" von dem Baseobjekt der Toolbox erben lassen,
- alle eigenen Units neu compilieren, und schon steht's - na ja,
- ganz so einfach dürfte's nicht sein, aber trotzdem...
-
- "AbstractList" ist ein Grundobjekt für Listen verarbeitende
- Objekte. Es enthält nur das Protokoll, das allen von ihm
- erbenden, Listen verwaltenden Objekten gleich sein muß.
- "DListCollection" trägt die Objekte in der Reihenfolge in die
- doppelt verkettete Liste ein, in der sie mit "Put" oder "Insert"
- hinzugefügt werden. Um aber bequem mit einer doppelt verketteten,
- nicht sortierten Liste arbeiten zu können, sind einige Methoden
- zusätzlich notwendig, z.B. um sich durch die Liste hangeln zu
- können ("GetNext/Prev-Node/Data").
-
- Die Anwendung von "DListCollection" demonstrieren "RadioButton"
- und "DialogBox".
-
-
- 6. Units aus \VSMS
- ------------------
-
- Dieses Kapitel ist nach Objekten geordnet. Alle Objekte sind in
- einer eigenen Unit implementiert. Der Namen der Unit ist in der
- Hierarchie festgehalten.
-
- ∙ "VirtualScreen" ist ein im Heap lebender "Bildschirm". Zur
- Beschreibung dieses Bildschirms sind wenig Daten nötig. Wichtig
- ist die Ausdehnung des (virtuellen) Bildschirms. Zu seiner
- "Bearbeitung" sind jedoch um so mehr Methoden (zum Schreiben von
- Zeichen und Strings, zum Füllen von Bereichen ...) notwendig.
- Der "Init"-Konstruktor versucht, Speicher für den Bildschirm zu
- allokieren. Schlägt dieser Versuch fehl, so wird "Fail"
- aufgerufen. Der belegte Speicher wird von "Done" wieder
- freigegeben.
-
- ∙ "ScrObj" braucht keinen Heap, sondern benutzt als Speicher den
- Bildschirmspeicher. In "Init" wird automatische die
- Anfangsadresse des Bildschirmspeichers ermittelt. "ScrObj" ist
- also ein "Modell" des wirklichen Bildschirms. Was in "ScrObj"
- ausgegeben werden soll, wird auf dem Bildschirm sichtbar (es sei
- denn, man hat auf eine Bildschirmseite ungleich der "nullten"
- umgeschaltet). "Init" und "Done" wurden ganz überschrieben, die
- Vorgängermethoden werden nicht mehr aufgerufen, da "ScrObj"
- keinen Speicher benötigt, weil es den richtigen Bildschirm
- "darstellt", für den der Speicher hardwaremäßig reserviert ist.
- Also braucht Speicher weder belegt noch freigegeben zu werden.
-
- ∙ Interessanter sind die Verwaltungen der "VirtualScreen"s.
- "VSManagerObj" implementiert nur die nötigsten Methoden dieser
- Verwaltung, wie "AddVS", "DeleteVS" und "SelectVS". Der
- Einfachkeit halber werden die virtuellen (und auch der richtige)
- Bildschirm in einem Array verwaltet, die maximale Anzahl
- virtueller Bildschirme ist also schon zur Compilationszeit
- festzulegen. Möglich wäre auch die Verwaltung in einer Liste,
- doch lohnt sich der dabei entstehende Mehraufwand nicht. Da der
- Array nur Zeiger enthält, vergibt man sich nicht zu viel
- Speicher, setzt man die Obergrenze z.B. auf 100 fest.
-
- ∙ "ExtVSManager erweitert "VSManagerObj" um "Weiterleitungen" von
- "WriteChr", "WriteStr" und ähnlichen Methoden an den aktuell
- selektierten (aktiven) virtuellen Bildschirm, der natürlich auch
- der richtige Bildschirm sein kann. Was für (virtuelle)
- Bildschirme verwendet werden, ist für die "Verwaltungen" ohne
- Bedeutung. Wer also z.B. ein Objekt "ExtVGAScrObj" für einen
- exotischen Textmodus von "ScrObj" ableitet, kann dieses
- benutzen, ohne daß "VSManagerObj" oder "ExtVSManager" etwas
- davon merken. Werden Bildschirmausschnitte kopiert, ist das kein
- Problem, da "ExtVSManager" immer auf den Zielbildschirm
- Rücksicht nimmt.
-
- ∙ "VSMCrt", ein Kindobjekt von "ExtVSManager", ist eine fast
- 100%ige Nachbildung der Unit "Crt" von Turbo Pascal; die einzige
- Prozedur die fehlt, ist "AssignCrt". Ansonsten kann man mit dem
- Objekt völlig analog zur Unit "Crt" arbeiten, wenn man davon
- absieht, daß Variablen (dem Prinzip der Abschottung folgend)
- nicht wie z.B. die Variable "TextAttr" der Unit "Crt" direkt
- geändert werden sollten, sondern mit den dafür vorgesehenen
- Methoden, also für dieses Beispiel "SetTextAttr" zum Setzen und
- "TextAttr" (eine Funktion !) zum Abfragen.
-
- ∙ "WinVSM" schließlich erweitert "VSMCrt" nur um die von Fenstern
- benötigte Fähigkeit, einen Ausschnitt des aktiven virtuellen
- Bildschirms zu speichern und wieder herzustellen. Es erbt von
- "VSMCrt", damit den Fenstern alle Fähigkeiten der Unit "Crt" zur
- Verfügung stehen.
-
- ∙ Eine Demo für "VSMCrt" bildet die leicht umgestrickte Demo
- "CrtDemo" von Turbo Pascal: Sie zeigt, wie VSMCrt eingesetzt
- wird und wo (noch) ihre Grenzen liegen: Spezialzeichen wie
- Backspace werden nicht interpretiert, sondern ausgegeben.
-
-
- 7. SB
- -----
-
- Das Objekt "ScrollBarObj" ist eine Art "halb-abstraktes" Objekt:
- Die entscheidenden Methoden, wie die Anzeige des Scrollbalken auf
- dem Bildschirm, sind noch nicht enthalten. Das müssen die
- spezialisierten Nachkommen "HorizScrollBar" und "VerticScrollBar"
- übernehmen; erst sie wissen, ob y1=y2 oder x1=x2 gilt, damit die
- Koordinaten gültig sind. Analoges gilt für alle anderen Methoden,
- die mit den Koordinaten hantieren. "ScrollBarObj" wurde als Vater
- der beiden eingeführt, da sie doch einiges gemeinsam haben, z.B.
- die Abfrage der Positionen oder das Setzen von Farben. Abgesehen
- davon wird der Stammbaum der Scrollbalken-Familie dadurch
- logischer; die Gemeinsamkeiten der beiden Scrollbalken-Arten
- werden schon in der Hierarchie sichtbar. Ausserdem ist der Einsatz
- flexibler, eine Instanze vom Typ "ScrollBarObj" kann auf beide
- Nachfahren initialisiert werden.
-
- Wie diese Nachfahren von "ScrollBarObj" angewendet werden, ist in
- "DataWindow" demonstriert.
-
-
- 8. Units aus \WINDOWS
- ---------------------
-
- Demonstrationsprogramme finden sich im Unterverzeichnis DEMOS zu
- allen Fensterobjekten bis auf "SAAWindow". Diese Demos genauer
- auszuprobieren lohnt sich besonders bei den aktiven Fenstern.
-
- ∙ "FrameWindow" als direkter Erbe von "Base" ist der Ausgangspunkt
- für die lange nachfolgende Vererbungskette. Es ist ein "Fenster"
- einfachster Art, wobei die Bezeichnung Fenster schon eine kleine
- Übertreibung ist, da es den Hintergrund nicht retten kann. Es
- kümmert sich nur um die Darstellung des Fenstersrahmens und -
- schattens auf dem Bildschirm, um die grundlegenden Elemente
- eines jeden Fensters (Kopf- und Fußzeile, Rahmenart,
- Schattenflag usw).
-
- ∙ "WindowObj" erweitert "FrameWindow" zu einem richtigen Fenster,
- das den Hintergrund speichern, beim Öffnen automatisch löschen
- und beim Schließen wiederherstellen kann, sofern diese Aktionen
- überhaupt gewünscht sind. Die andern, neu hinzugekommenen
- Methoden kümmern sich nur um das Setzen und Abfragen der eigenen
- Flags.
-
- ∙ Weiter in der Kette der logischen Erweiterungen bildet "Stand-
- ardWindow" das nächste Glied der Kette. Es geht einen Schritt
- weiter als "WindowObj": Als Standard-Fenster nach SAA ist ihm
- zusätzlich die Möglichkeit spendiert worden, sich zu verschieben
- und zu verkleinern/verkleinern, beides relativ oder absolut.
- Dabei ist es möglich, den Bewegungsraum und damit die maximale
- äußere Größe einzuschränken und die minmale innere Ausdehnung
- festzulegen. Der Grund dafür ist der, dass z.B. die Menüzeile in
- einem Programm nicht von Fenstern überdeckt werden soll. Und
- wenn das Fenster einen Inhalt hat, ist es meist nicht allzu
- sinnvoll, eine Mindestgröße zu unterschreiten.
-
- ∙ "ActiveStandWin" ist wiederum eine konsequente Erweiterung von
- "StandardWindow": Die Abfrage von Events in Bezug auf
- Fähigkeiten des Standardfensters (verschieben, schließen,
- vergrößern) kann "ActiveStandWin" selbständig vornehmen. Durch
- die Anzeige des "Close"-Symbols ("[■]") in der Titelzeile wird
- angezeigt, daß es sich um ein Fenster handelt, das reagieren
- kann. Die Abfrage, ob ein Event das Fenster betrifft, erfolgt
- mit der Methode "CheckEvent". Es wird nur reagiert, sofern das
- Fenster aktiv ist, d.h. durch einen doppelten, hervorgehobenen
- Rahmen gekennzeichnet ist. Der Zustand (aktiv/passiv) kann nur
- durch den Aufruf der entsprechenden Methode ("SetActive"/
- "SetPassive") umgeschaltet werden. Die Demo zeigt, wie
- "ActiveStandWin" angewandt wird und wie seine Returncodes
- ("Antworten") ausgewertet werden.
-
- ∙ Einen weiteren Schritt in der Fensterevolution macht "Data-
- Window": Es verknüpft "Buffer" bzw. "DiskBuffer" mit
- "ActiveStandWin". Deswegen sind auch soviele Methoden vorhanden:
- Viele "Messages" wie "WriteStr", "DelLine", etc. müssen nur an das
- Buffer-Objekt weitergereicht werden. Die anderen Methoden
- kümmern sich um die Verknüpfung der beiden "Ehepartner" "Buffer"
- und "Window", so z.B. "ShowBuffer", "ScrollBuf" ... Dazu ist
- einiges an internen Methoden nötig. Ein ganzes Set von "Set-..."
- und "Get-..." Methoden steht für die Manipulation der neuen
- Daten zur Verfügung. "DataWindow" verfügt auch über "ScrollBar"-
- Objekte, die die relative Pufferposition anzeigen sollen. Auf
- Wunsch kann auch die absolute Position in der unteren
- Fensterzeile eingeblendet werden.
-
- ∙ Das letzte hier implementierte Objekt dieser Kette geht -
- wie sollte es anders sein - einen logischen Schritt weiter: Nun ist es
- ein "ActiveDataWindow" geworden, das Events um die in
- "DataWindow" erweiterten Fähigkeiten abfragt; so z.B. ob der
- Scrollbalken mit der Maus betätigt wurde. Neu sind hier die
- Symbole, die mit der Maus angeklickt werden können, es sind
- Symbole, die das "Normalisieren", "Maximieren" und
- "Iconisieren" (verschiedene Grössenstufen) anzeigen. Die
- Mausabfrage, die doch ziemlich aufwendig ist, wurde auf
- veschiedene interne Methoden verteilt, so daß ein Nachkomme
- gezielt bestimmte Eigenschaften verändern kann. Soll z.B. das
- Fenster nicht mehr per Doppelklick die Größe verändern, so kann
- "CheckMouDoublC" überschrieben werden.
-
-
- ∙ "SAAWindow" ist von "ActiveStandWin" abgeleitet und
- unterscheidet sich nur in einem einzigen Punkt: Ein "SAAWindow"
- ist immer aktiv, kennt keinen Unterschied in der Darstellung
- zwischen aktiv und passiv. Ein Beispiel, wo diese Sturheit Sinn
- macht: So soll die Eingabe in einen "WindowInputField" zuerst
- beendet werden, bevor woanders mit der Arbeit weitergefahren
- wird. Dazu ein Vergleich mit Turbo Pascal: Ist man in einen
- Eingabefeld, so ist - bis auf F1 - alles andere tot, zuerst muß
- man das Eingabefeld schließen, sei es per Esc oder Enter.
-
-
- Ein anderes Design dieser ziemlich lange Vererbungskette wäre
- natürlich auch möglich. Eine Möglichkeit wäre z.B., "FrameWindow"
- in Teile aufzuspalten: "DisplayElement", davon ein "Title"-Objekt,
- ein Rahmenobjekt und ein Hintergrundobjekt usw. ableiten. Des
- weiteren könnten die Symbole ("Close", "Maximize"...) in einzelne
- Objekte gepackt werden, und "DataWindow" und "ActiveDataWindow"
- ganz neu aus anderen Teilen zu einer Baugruppe zusammengesetzt
- werden. Der Vorteil dieses Designs wäre, daß man die lange
- Vererbungskette vermeidet. Denn dadurch werden die Objekte "weiter
- hinten" sehr schnell sehr komplex, "DataWindow" z.B. hat um 120
- Methoden. Der Nachteil: Man erhält sehr schnell sehr viele
- Objekte, also eine riesige Hierarchie. Also entweder Komplexität
- der Hierarchie - oder Komplexität der Objekte tiefer in der
- Vererbungskette.
-
- Doch der Vorteil des hier verwendeten Design liegt in der
- konsequenten, stufenweisen und logischen Erweiterung von einer
- Objektstufe zur nächsten, wie es bei einer Treppe immer einen
- Schritt höher geht. Und die sonst schon ziemlich große Hierarchie
- sollte nicht noch grösser werden. Aus diesen Gründen ist dieses
- Design auch gewählt worden.
-
-
- 9. SAAItemD
- -----------
-
- "SAAItem" bildet die Grundlage für alle "Einträge", sei es in
- Menüs, in Dialogboxen oder was auch immer. Es implementiert die
- allen Nachkommen gemeinsamen Methoden wie das Anzeigen ("Show"),
- das Prüfen, ob der HotKey betätigt wurde ("CheckKeyEv), ob ein
- Mausknopf im eigenen Bereich niedergedrückt worden ist
- ("CheckMouEv") usw. Auf diesem Objekt aufbauend fügen seine Erben
- nur noch ganz spezifische Methoden, die sie benötigen, hinzu oder
- implementieren ererbte Methoden neu.
-
- Das gemeinsame Grundobjekt dient auch der Listenverarbeitung, denn
- in den Schedulern (s.u.) braucht man einen gemeinsamen Nenner für
- alle Einträge. Dieser kleinste gemeinsame Nenner ist "SAAItem".
- Über ihn spricht ein Scheduler seine Einträge an, ohne zu wissen,
- welchen Typs sie eigentlich sind. Da die entsprechenden Methoden
- virtuell sind, ist das aber auch überhaupt nicht nötig. Zur
- Laufzeit wird die Methode des betreffenden Objekttyps ausfindig
- gemacht und aufgerufen. Das ganze nennt sich bekanntlich "Late
- binding" und läuft mit Hilfe der "VMT" (virtuellen
- Methodentabelle).
- Hätte man diesen gemeinsamen Nenner nicht eingeführt und
- stattdessen die spezialisierten Nachkommen direkt von "Base"
- abgeleitet, wäre man innerhalb des Schedulers wieder bei den guten
- alten "IF THEN ELSE IF"-Statements in der Art
-
- IF TypeOf (Item)=TypeOf (MenuItem) THEN
- MenuItemPtr (ItemPtr)^.Display
- ELSE
- IF ...
-
- angelangt. Der ganze Vorteil von OOP wäre zunichte gemacht, die
- Polymorphie mit den Füßen getreten. An diesem Beispiel sieht man
- sehr eindrücklich, daß es sich wirklich lohnt, abstrakte oder
- auch "halb-abstrakte" Objekte in die Hierarchie einzufügen.
-
-
- 10. MnuItems
- ------------
-
- Wie die einzelnen Objekte aus der Unit "MnuItems" angewandt
- werden, zeigen die Demoprogramme in dem Unterverzeichnis DEMOS.
-
- ∙ "MenuItem" ist seinerseits ein Oberobjekt für alle Arten von
- Menüeinträgen und hat gleichzeitig die Eigenschaften eines SAA-
- Eintrages. Deshalb ist es ein Erbe von "SAAItem". Es ist aber
- auch noch kein konkretes, sondern ein "halbabstraktes" Objekt,
- das den Menüschedulern die Verwaltung erleichtert (wie oben
- anhand von "SAAItem" erläutert). Anders als bei "SAAItem" wird
- hier bei "SetActive" die Darstellung geändert, der Item durch
- einen "Balken" farblich hervorgehoben.
-
- ∙ Ein drittes halbabstraktes Objekt nach "SAAItem" und "MenuItem"
- ist "PopUpItem", das von "MenuItem" erbt. Allen seinen
- Nachkommen ist gemeinsam, daß sie bei Betätigung von "Cursor
- hoch" bzw. "Cursor runter" den Returncode "ActNext" bzw. "ActPrev"
- zurückgeben. Ein vertikales Menü kennt verschiedene Arten von
- Einträgen, und diese sind als Erben von "PopUpItem"
- spezialisiert. "PopUpItem" dient nur als gemeinsame Grundlage,
- damit die Abfrage auf Cursor rauf oder runter nicht von jedem
- Nachkommen von "PopUpItem" selbst vorgenommen werden muß.
-
- ∙ Nach einem abstrakten Objekt ("Base") und drei halb-abstrakten
- ("SAAItem"-"MenuItem"-"PopUpItem") ist "ExeItem" ein "konkretes"
- Objekt. Es stellt einen Menüeintrag dar, der bei seiner Anwahl
- eine Aktion auslösen soll ("ExecutableItem", natürlich ist nicht
- der Item ausführbar, sondern die hinter seinem Namen stehende
- Funktion soll ausgeführt werden).
-
- ∙ Ein spezialisierter Nachfahre von "ExeItem" ist "DirectExeItem".
- Ein solcher Menüeintrag kann noch zusätzlich zu Maus, Hotkey und
- Alt+Hotkey mit einem "DirectCode" ausgeführt werden. Ein solcher
- Menüpunkt kann auch gewählt werden, wenn sein PopUp-Menü nicht
- aktiv (angezeigt) ist, wohingegen die HotKey-Wahl nur für das
- gerade aktive PopUp-Meü möglich ist. Ein Beispiel dafür ist Alt-
- F3 in Turbo Pascal 5.5: Die Picklist erscheint direkt,
- unabhängig davon, ob man gerade im "File"-Menü ist oder nicht.
-
- ∙ "SwitchItem" ist ein Item, der zwei Zustände aufweist: "On" oder
- "Off" (Beispiel bei Turbo Pascal: "Stack Checking On/Off"). Bei
- der Wahl mit Enter wird nur der Zustand geändert, nicht aber die
- Menüwahl beendet.
-
- ∙ "GlobalSwitchItem" als Nachkomme von "SwitchItem" schaltet nicht
- nur das eigene Flag um, sondern auch eine Boolesche Variable,
- auf die es einen Pointer enthält. So können Modul- oder
- Programmglobale Variablen direkt abgefragt bzw. weiterverwendet
- werden, ohne daß die Resultate immer erst aus einem
- "SwitchItem" in diese Variablen kopiert werden müssen.
-
- ∙ "LineItem" stammt von "PopUpItem" und ist ein Trennstrich für
- ein Menü. Er kann auf keine Art und Weise aktiviert werden, hat
- keinen Hotkey, keinen Namen, einfach nichts. Wird mit der Maus
- versucht, ihn zu aktivieren, so teilt er dem übergeordneten
- Popup-Menü mit, daß es "sich beenden" soll (ReturnCode =
- "ItFinish").
-
- ∙ "SlideBarItem" ist ein direkter Nachkomme von "MenuItem" und hat
- es wesentlich einfacher als die vielen "PopUpItem"-Arten: Ein
- horizontales Menü kann nur aus "ExeItem"s bestehen, die alle das
- gleiche tun: Sie öffnen ein Untermenü.
-
-
- 11. DbxItems
- ------------
-
- ∙ "RadioBut", "PushBut" und "EndBut" sind Implementationen der
- verschiedenen Button-Typen und stammen von "SAAItem" ab. Sie
- alle sind Einträge, aber nicht wie die Nachkommen von "MenuItem"
- in einem Menü, sondern in irgendeiner Dialogbox.
-
- Es ist nicht direkt einsichtig, warum "PushBut" und "EndBut" als
- Buttons implementiert wurden, wo doch ihre Scheduler
- ("PushButton" und "EndButton") praktisch nur noch aus
- Weiterleitungen der Messages besteht. Daß sie denoch hier von
- "SAAItem" abgeleitet sind, hat den Grund, daß die Dialogbox mit
- Schedulern kommunizieren will, und diese sich wiederum nicht um
- Kleinigkeiten wie Position oder Farbe kümmern sollten. Den
- Schedulern ist es egal, welche Art Items sie beherbergen. Es
- werden eher Änderungen an den Items (in irgendwelchen
- Kindobjekten) nötig sein als an ihren Schedulern, denn diese
- kümmern sich nur um verwaltungstechnische Aspekte, nicht aber um
- die Darstellungsart, die Art zu reagieren usw. Das ist die
- Aufgabe eines Items.
-
- ∙ Um einen String konfortabel editieren zu können, ist
- "StringField" da. Es implementiert ein vollständiges,
- einzeiliges Editerfeld. Auch ein Eingabefeld, z.B. für die
- Eingabe eines Dateinamens, ist ein Bestandteil einer DialogBox.
- Aus den gleichen Gründen, die oben für "PushBut" und "EndBut"
- Ausschlag gebend waren, ist auch "StringField" ein Item. Sein
- Scheduler hat ein leichtes Spiel, braucht er doch nur die
- "Messages" weiterzugeben.
-
- ∙ "IntegerField" und "RealField", Kinder von "StringField"
- versuchen nach Beendigung der Eingabe, diese in eine Integer-
- oder Real-Zahl umzuwandeln. Bei der Eingabe lassen sie nur noch
- Zahlen, Vorzeichen und RealField zusätzlich noch den
- Dezimalpunkt zu.
-
- ∙ "PickList" ist wohl der komplexeste Item. Aus den oben
- angeführten Gründen ist auch hier ein "Item" zwischengeschaltet.
- Die Pickliste enthält einen vertikalen Scrollbalken. Die
- einzelnen Einträge in die Pickliste werden mit "Add"
- vorgenommen. Trotz seiner Komplexität ist "PickList" leicht zu
- benutzen, wie sein Scheduler "PickListSched" demonstriert.
-
- ∙ "ExtPickList" fügt seinem Vaterobjekt "PickList" noch einen
- horizontalen Scrollbalken hinzu. "StandAlonePickList" erweitert
- "ExtPickList" dahingehend, dass das Fenster ein aktives, also
- selbständiges Fenster ist, das nicht in eine Dialogbox
- integriert ist.
-
- Hier wäre auch ein anderes Design einsichtig: Ein Picklist-
- Eintrag wäre ein Erbe von SAAItem, die Pickliste selbst aber von
- SAAScheduler abgeleitet. Ein solcher PickListItem wäre aber auch
- ziemlich aufwendig geworden, da er viel von dem jetzigen
- "PickList"-Objekt hätte übernehmen müssen. Ausserdem wären dann
- viele Daten wie die Farbe unnötig oft (von jedem PickListItem)
- gespeichert worden.
-
- Außerdem ist es mit dem hier verwendeten Design möglich, dem
- Picklist-Scheduler alle Arten von Picklisten unterzuschieben,
- ohne daß er dafür geändert werden müßte. Für ihn kommt es -
- wie für so viele andere Objekte - nicht darauf an, zu welchem
- Objekttyp die Methoden gehören, die er aufruft. Das entscheidet
- der ihm in "Init" übergebene Item.
-
-
- 12. SAASched
- ------------
-
- "SAAScheduler" ist wieder ein halb-abstraktes (eigentlich
- dreiviertel-abstraktes) Objekt, das als Grundlage für alle
- Scheduler dient. Sehr viele Methoden braucht es nicht, um mit ihm
- umgehen zu können. Die Komplexität, die hinter einem solchen
- Scheduler und seinem(n) Item(s) stehen, bleibt für den Anwender
- eines Nachkommen von "SAAScheduler" verborgen, er braucht sich
- nicht darum zu kümmern. Er weiß, welche Methoden welche Parameter
- erfordern und was sie machen. Mehr ist nicht nötig, es sei denn,
- er will die Scheduler oder Items erweitern. Dann muß er sich
- genauer mit dem Aufbau eines Objekts beschäftigen.
-
-
- 13. DBxSched
- ------------
-
- Auch hier sind es wieder kleine Demoprogramme im Unterverzeichnis
- DEMOS, die den Gebrauch der Objekte aus "DBxSched" demonstrieren.
-
- ∙ "RadioButtons" ist ein etwas komplexerer Scheduler. Da Items vom
- Typ "RadioBut" immer nur in Gruppen auftreten, muss
- "RadioButtons" diese verwalten. Dadurch fällt das Prüfen eines
- Events etwas umfangreicher aus. Kann der aktive Item das Event
- nicht auswerten oder war gar kein Item aktiv, so müssen alle
- durchlaufen werden, um zu prüfen, ob sich irgendein Item
- angesprochen fühlt. Dabei wird ein Item, der schon aktiv war,
- auch noch einmal abgefragt. Doch die paar Mikrosekunden, die
- dafür verschwendet werden, sind nicht der Rede wert. Hat sich
- nun ein Item gefunden, der das Event auswerten konnte, so muß
- der vorher aktive noch passiv gesetzt werden. Der "RadioBut",
- der das Event auswerten konnte, hat sich selber automatisch
- aktiv gesetzt.
-
- ∙ "PushButton" und "EndButton" haben es wesentlich einfacher. Da
- ihre Einträge immer nur alleine auftreten, brauchen sie keine
- Liste mit Einträgen zu verwalten. Die meisten "Messages" können
- einfach an ihren Eintrag weitergeleitet werden. Was für ein
- Eintrag das ist, entscheidet der an "Init" übergebene Zeiger.
- Werden Kindobjekte von "PushBut" oder "EndBut" benutzt, so
- spielt das für "PushButton" oder "EndButton" keine Rolle.
-
- ∙ Das gleiche gilt für "InputField". "WinInputField" braucht sich
- auch nur um das Fenster zu kümmern, der Rest läuft in
- "StringField" oder dessen Nachkommen ab.
-
- ∙ Auch "PickListSched" muß die meisten Methodenaufrufe nur
- weiterleiten. "FileList" erstellt automatisch eine Liste von
- allen Dateien aus einem Verzeichnis, und "DirList" eine Liste
- mit allen Directories aus einem Verzeichnis.
-
- ∙ Das DialogBox-Objekt "DialogBox" ist dem RadioButton-Objekt sehr
- ähnlich, die Verwaltung und die Auswertung von Events läuft
- praktisch analog ab. Nur müssen die Einträge einer "DialogBox"
- vom Typ "SAAScheduler" (oder dessen Nachkommen natürlich) sein.
- "RadioButton" im Gegensatz dazu verlangt Einträge vom Typ
- "RadioBut".
-
- ∙ "DialogEndBut" fügt den eingetragenen Scheduler automatisch 2
- oder 3 (mit HelpButton) "EndButton" hinzu, so daß diese nicht
- jedesmal explizit angelegt werden müssen.
-
- ∙ "SaveBox" braucht eine Dialogbox und besteht aus einem
- Eingabefeld für den Dateinamen, einer Pickliste für die Wahl des
- Verzeichnisses und 2 oder 3 Endbuttons. "SaveBox" macht nicht
- viel mehr, als diese Einträge in eine Dialogbox einzutragen.
- Seine Anwendung demonstriert "SBDemo".
-
- ∙ Daß eine "LoadBox" von einer "SaveBox" erbt, scheint von der
- logischen Seite her nicht viel Sinn zu machen, erfüllen sie doch
- zwei gegensätzliche Aufgaben: Laden und Speichern. Schaut man
- sich aber an, was sie enthalten, so stellt man fest, daß die
- "LoadBox" bis auf eine Pickliste mit Dateinamen fast identisch
- mit der "SaveBox" ist. Das Eingabefeld ist nicht mehr für den
- Dateinamen da, sondern für die Suchmaske, doch der Rest bleibt
- sich gleich. Das ist auch der Grund, warum "LoadBox" von
- "SaveBox" erbt.
-
-
- 14. MnuSched
- ------------
-
- ∙ "MenuScheduler" implementiert die für vertikale und horizontale
- Menüs gemeinsamen Methoden, z.B. das Auswerten von Events, das
- für beide fast das gleiche ist: ActNext und ActPrev müssen ja
- nicht wissen, was für Items da aktiviert werden. In der Liste
- geht vor- oder rückwärts, auf dem Bildschirm auf- oder abwärts.
-
- ∙ "PopUpMenu" lässt nur Einträge vom Typ "PopUpItem" und dessen
- Nachkommen zu, "SlideBarMenu" will nur "SlideBarItem"s haben.
-
-
- Ein Ausblick, Anregungen
- ------------------------
-
- Da diese Spezialdiskette im Sinne einer Toolbox konstruiert ist, enthält
- sie auch keine fertigen Programme, sondern nur Demoprogramme. Und
- sie ist in allen Richtungen erweiterungsfähig. Ein paar
- Anregungen, wo noch weitere Objekte implementiert werden könnten:
-
- - ArrayObj, SListCollection, SListSorted, DListSorted
-
- - YesNoWindow, ErrorWindow, InfoWindow, HelpWindow,
- CutAndPasteWindow, EditorWindow
-
- - WindowScheduler
-
- - StandAloneMenu, PullDownMenu
-
- - UserInterFace
-
- ...
-
- Diese paar Anregungen genügen, die Liste ließe sich natürlich
- endlos fortsetzen, denn objektorientierte Programmierung kennt -
- wie schon so oft gesagt wurde - die Möglichkeit der Vererbung und
- somit sind die Objekte erweiterbar. Und die Vererbung macht OOP im
- Zusammenspiel mit Polymorphismus zu einem wirklich mächtigen
- Instrument.
-
- Raimond Reichert
-
-
-