Objektorientierte Erweiterungen für Cobol, Teil 1

OOP - eine Technologie auch für Cobol-Programmierer

29.06.1990

Objektorientierte Programmierung (OOP) ist in. Die Cobol-Anbieter haben sich jedoch von diesem Technologie-Boom bisher vornehm distanziert. Dabei wird in den Vereinigten Staaten längst darüber diskutiert, wie man die Vorteile von OOP in die führende Sprache für kommerzielle Anwendungen einbinden kann. Ken Belcher schlägt Sprach-Erweiterungen vor, die zu einem objektorientiertem Cobol führen sollen.

In diesem Arbeitspapier regen wir eine Gruppe von objektorientierten Cobol-Erweiterungen an, in der Hoffnung, daß die Cobol-Erfinder der Conference on Date Systems Languages (Codasy) einen Ansatz für die Objektorientierung finden. Die vorgeschlagenen Erweiterungen wurden unter der Vorgabe minimaler Änderungen der Cobol-Syntax entwickelt. Jede Änderung sollte eine zusätzliche Verbesserung der Sprache darstellen, keine komplette Überarbeitung.

Dieser Ansatz wurde gewählt, um leichte Erlernbarkeit, einfache Benutzung und leichte Implementierung zu gewährleisten. Die Programmierer sollten nicht gezwungen werden, OOPs (objektorientierte Programme) in einer "approbierten" Art und Weise zu benutzen. So sollte eine stückweise Übernahme unter Beibehaltung laufender Systeme möglich sein.

Ziel des objektorientierten Programmierens ist die leichte Wiederverwendbarkeit eines entwickelten Codes. OOP bietet dafür einen recht einfachen Ansatz: Zuerst erzeugt man ein Objekt (schreibt ein Programm) zur Lösung eines Problems, das erwartungsgemäß öfter als einmal entstehen wird. Dieses Objekt enthält die Schrittfolge zur Lösung des Problems sowie die notwendigen Daten zu ihrer Ausführung.

In der Vergangenheit haben wir versucht, dasselbe durch Unterroutinen zu erreichen, die von anderen Programmen aufgerufen wurden. Doch leider machten sie häuftig nicht das, was sie sollten. Deshalb wurde eine Kopie des Unterroutinen-Quellcodes gezogen und so lange verändert, bis ein "richtig" ablaufendes Programm entstand. Gegen diesen Ansatz war nichts einzuwenden solange die Unterroutine einfach genug war, um auch von Programmierern geändert werden zu können, die sie nicht geschrieben hatten. Bei tiefergreifenden Änderungen begann allerdings die Jagd nach allen vom Original erzeugten Programmen und die Überprüfung daraufhin, ob in ihnen dieselben Änderungen Programmiert werden mußten.

Was aber hat sich verbessert? Nun, das OOP-Objekt erwartet geradezu Änderungen. Es erkennt, was an seinem Ablauf geändert werden kann. In laufenden Implementierungen bricht das Objekt seine Schrittfolge auf Funktionen herunter, von denen einige oder auch alle ersetzt werden können. Das hat einige Regeln zur Folge. So muß eine Ersatzfunktion dieselbe Art und Anzahl von Parametern erwarten und analog der alten Funktion arbeiten.

Warum können Datenbeschreibungen nicht wie Funktionen ersetzt werden? Grundsätzlich vertragen sich das Ersetzen von Daten und Effizienz schlecht. Solange der ursprüngliche Quellcode allerdings noch verfügbar ist, kann ein Programm effizient arbeiten. Der Compiler muß die Quelle lediglich mit einem geeigneten Ersatz-Algorithmus kopieren.

Man kann die Wirkung, die das Ersetzen von Daten hat, aber auch durch die Definition von Funktionen simulieren, die stets auf Daten zugreifen und sie ändern. Diese Funktionen müssen kompatible Ergebnisse zurückgeben; Kompatibilität hat bei Objekten allerdings ein breiteres Bedeutungsspektrum: Solange Pointer für Parameter und Ergebnisse verwendet werden, kann jedes Objekt von dem Objekt abstammen (sein descendant sein), das für den entsprechenden Parameter oder das Ergebnis erwartet wird.

Die grundsätzliche hier vorgeschlagene Cobol-Erweiterung heißt "Object-class". Diese fußt auf einem wörtlichen Verständnis dessen, was ein OOP-Objekt ist: Ein Objekt enthält Funktionen mit verschiedenen Namen sowie die Definition der Daten, die diese Funktionen bearbeiten sollen. Cobol besitzt bereits eine Struktur, die Funktionen und Daten enthalten kann. Diese Struktur ist ein "Source Program", das andere Programme und Daten, auf die diese Programme zugreifen können, enthalten kann.

Ein Object-class muß eingeführt werden

Eine Object-class ist folglich eine spezielle Art von Programm. Die meisten der für eine Object-class-Definition notwendigen Syntaxänderungen sind in der Program-ID der Identification Division zu finden. Die einschneidendste Änderung betrifft eine Programmergänzung, nämlich die Object-class, die mit diesem reservierten Wort bezeichnet wird.

Eine Object-class muß die Information definieren, die für einen sogenannten "Instance" der Object-class benötigt werden, nämlich einen Object-instance, sowie die jeweils "Method" genannten Funktionen, die diese Information bearbeiten. Es ist zu beachten, daß die Information für einen Object-instance weder der ihn definierenden Object-class eigen sind noch irgendeiner der Object-class-methods. Sie ist konzeptionell enger an EXTERNAL-Objekte angebunden, nur daß keine, eine oder mehrere Darstellungen des Object-instance in einer Objektprogramm-Einheit sein können.

Zusätzlich können Teile der Object-instance-Information von Code außerhalb der Object-class angesprochen oder auf Referenzierung von den Object-class-methods begrenzt werden. Ähnlich sind einige der Methods von Code außerhalb der Object-class aufzurufen oder außer Kraft zu setzen.

Die Informationen und Methods, die extern angesprochen werden können, sind durch das reservierte Wort PUBLIC identifiziert. Ähnlich sind

Informationen und Methods, die nur von Code der Object-class angesprochen werden können, durch das reservierte Wort PRIVATE identifiziert. PRIVATE-Methods können allerdings durch eine abstammende (descendant) Object-class außer Kraft gesetzt werden.

Mit Ausnahme einer Method, die durch das reservierte Wort CANCEL identifiziert ist, gehören sämtliche Programme und

Informationen zur Object-class und sind nicht Bestandteil eines Object-instance. Diese reservierten Wörter können ausschließlich für die Information, die in der Object-class definiert ist, und für enthaltene Methods benutzt werden.

Nach dieser ersten Definition einer Object-class können wir uns der Vorgehensweise zuwenden, mit der wir einen Object-instance erhalten. Der direkte Weg geht über die folgende neue "USAGE: USAGE INSTANCE OF Object-class-Name" (USAGE und OF sind optional).

Referenz und Deklaration von Object-Instances

Damit wird ein statischer Object-instance zugewiesen, dessen Instance-Daten durch den Object-class-Namen der Class definiert sind. Beachten Sie, daß dieser Object-instance auf Stufennummer 49 definiert sein und sieben Subskripte erfordern kann. Es ist sogar möglich, ihn als Teil der Instance-Daten oder einer anderen Object-class anzugeben.

Wenn er Teil der PUBLIC-instance-Daten im Object-class-Namen ist und einen Bedingungsnamen der Stufe 49 hat, die sieben Subskripte in diesen Instance-Daten erfordert, können wir den Bedingungsnamen auf bis zu 100 Stufen und mit 14 Subskripten ansprechen. Diese Namensverschachtelung läßt sich unendlich fortsetzen, so daß bessere Ansprechmöglichkeiten erforderlich sind. Wir schlagen hierfür ein Verfahren vor, das einen Zusatz für "UNIQUENESS OF REFERENCE" (JOD III-3.4-8) darstellt und unter der Bezeichnung INSTANCE MODIFICATION läuft. Die Syntax lautet: Object-instance-Name (Name-in-object-class [qualifiziert, subskribiert]).

Um eindeutig angesprochen werden zu können, kann PUBLIC-instance-Daten-Information in einer Object-class durch ihren Object-class-Namen nach allen anderen Bedingungsstufen qualifiziert werden. Ist der Name-in-Object-class ein weiterer PUBLIC-object-instance, unterliegt der Name-in-Object-class selbst der Instance-Modifikation, die direkt vor der rechten Klammer des äußeren Instance-modifiers angegeben wird. Das läßt sich beliebig fortsetzen.

Dieser Zusatz ist einem Ausdruck in der Referenzänderung vergleichbar und gestattet den unbeschränkten Zugriff auf alle allgemein zugänglichen Daten, während die Anzahl der Subskripte und Bedingungsstufen fest begrenzt sind. Ohne weitere Zusätze können wir nun spezielle elementare Daten definieren, die aus zusätzlichen elementaren Daten zusammengesetzt sein können, und ihre Felder wie erforderlich mit Bedingungen und Subskripten ansprechen. Diese Möglichkeit allein ist für jeden Programmierer sehr hilfreich, der Routinen in Windows, OS/2, Presentation Manager und jedem anderen System aufruft, das viele und reichstrukturierte Daten benutzt.

Wie können wir jetzt den Object-instance nutzen? Die erste zu erörternde Erweiterung ist CALL mit einer neuen Möglichkeit für Bezeichner-1: CALL object-instance-Name.

Hierdurch wird der Code in der Procedure Division aufgerufen (bis EXIT PROGRAM), der in der Object-class-Definition zur Verfügung steht, zu der der Object-instance-Name gehört. Diese Method soll einen Object-instance initialisieren, sie ist also PUBLIC. Der CALL übergibt BY REFERENCE den Object-instance-Namen an die Method als einen impliziten Parameter, der nicht Bestandteil der USING-Angabe der Procedure Division ist. Für den Aufruf anderer für die Object-class definierter PUBLIC-methods benutzen wir die Instance-Modifikation: CALL object-instance-Name (Method-Name)

Einsatzmöglichkeiten für den CALL-Aufruf

Dieser Aufruf übergibt den impliziten Object-instance-Parameter an die Method. Wenn der Object-instance-Name bereits Instance-modifiziert ist, muß der (Method-Name) die rechte oder - nach einer anderen Sprachregelung - die innere Instance-Modifikation sein. Die implizierte Object-class ist die vor dem Method-Namen ausgewählte Klasse, es sei denn, diese Bezeichnung ist durch, seinen Object-class-Namen qualifiziert.

Alle CALL-Anweisungen einschließlich der Object-class-Namen-method müssen implizit einen zugehörigen Object-instance an die Method übergeben. Der Code in einer Method bezieht sich auf Instance-Daten entweder durch den normalen Namensumfang oder durch Instance-Modifikation des impliziten Parameters, der in einer Method über den Object-class-Namen verfügbar ist.

Wir haben somit den CALL zur Initialisierung unserer Object-instance genutzt und weitere CALL-Anweisungen für die Anwendung der benötigten Methods. Bleibt noch das Säubern unseres Object-instance, wenn wir ihn nicht mehr brauchen: CANCEL Object-instance-Name.

Hiermit wird unser Obect-instance in seinen Anfangsstatus zurückversetzt. Möglicherweise hat Cobol jedoch nicht genügend Informationen, um eine spezielle Datenstruktur an diesen Anfangsstatus zurückzugeben. Für spezielle Anforderungen kann eine Method in jeder Object-class das CANCEL-Attribut haben, das festlegt, daß die CANCEL-method vor etwaigen Systemmaßnahmen mit Bezug auf den Object- instance aufgerufen werden sollte.

Der Befehl CANCEL Object-instance-Name hat keinerlei Auswirkungen auf die internen Daten des Object-class-Programms.

Diese können ganz normal Ziel der CANCEL-Anweisung sein. Wird CANCEL so eingesetzt, dann sind die Object-instance-Daten nicht anders als bei programminternen Object-Instances nicht betroffen. Es gibt für die CANCEL-Method keine Einschränkungen, darf allerdings lediglich den impliziten Object-instance als Parameter erwarten. Sie kann COMMON und/oder INITIAL sein und den Cobol-Konventionen entsprechend aufgerufen werden.

Die Handhabung einer komplexen Objekt-Class

Um zu gewährleisten, daß Object-instances und ihre Methods normale Programmfunktionen ausführen, sind viele Cobol-Datenfelder als Instance-Daten zugelassen. Hier sind FD, SD, CD und RD mit den dazugehörigen Sätzen eingeschlossen. Da es müßig ist zu entscheiden, wie statische Object-instances mit solchen Feldern aussehen oder wie groß sie sein sollten, werden sie hier verboten, und zwar mit einer zusätzlichen Klausel im Programm-ID-Eintrag für eine Object-class: RESERVE Integer AREAL [Pointer].

Hierdurch wird dem Compiler die maximal erlaubte Anzahl pseudo-dynamischer Zuweisungen von Object-instances mitgeteilt. Die Pointer-Angabe läßt einen statischen Object-instance nicht zu. Es gibt allerdings eine wichtige Ausnahme: Ein statischer Object-instance kann auf Satzebene in der Linkage Section definiert werden und wird als Pointer dargestellt, wenn der Parameter BY REFERENCE Übergeben wird (für einen solchen Object-instance ist beim CALL ausschließlich dieser Weg zugelassen).

Nur Angaben in der Working-Storage-Section dürfen PUBLIC oder PRIVATE in einer Object-class sein, die die Pointer-Angabe nicht hat. Da wir einen statischen Instance einer komplexen Object-class nicht zuweisen können, weisen wir einen Pointer zu: USAGE Pointer TO Object-class-Name (USAGE und TO sind optional).

Tatsächlich können wir einen Pointer für jede Object-class zuweisen, selbst wenn sie gar keine komplexen Daten enthält oder pseudo-dynamische Zuweisung nicht zuläßt. Intern wird diese Möglichkeit gebraucht, um Object-instances BY REFERENCE zu Übergeben.

Bevor wir einen Pointer benutzen, müssen wir ihn entweder auf einen existierenden Object-instance richten oder einen neuen Object-instance für den Pointer zuweisen. Die Angabe hierfür lautet: CALL Objekt-Pointer-Name.

Diese Angabe weist einen Object-instance zu, bringt den passenden Wert in den Objekt-Pointer-Namen und initialisiert unseren neuen Object-instance mit Hilfe des Codes aus der Procedure Division der Object-class. Wenn keine pseudo-dynamischen Instances zum Zuweisen übrig sind, wird der CALL beim Aufruf der EXCEPTION-Angabe fehlerhaft sein. Wenn eine Object-class sowohl statische als auch pseudo-dynamische Object-instances haben darf, muß der Hersteller dafür

sorgen, daß die Object-class erkennt, daß sie als erstes einen neuen Object-instance zuweisen muß. Der Method-Aufruf hat folgende Syntax: CALL Objekt-Pointer-Name (Method-Bezeichnung). Die Syntax für die Säuberung des Object-instance lautet: CANCEL Objekt-Pointer-Bezeichnung.

Nach dem optionalen CANCEL-method-Aufruf muß ein pseudo-dynamisch zugewiesener Object-instance an den freien Pool der Object-class zurückgegeben werden. Das geschieht nicht, wenn eine Object-class auch statische Object-instances zuläßt, weil sie sonst nicht mehr zu erkennen sind.

Es ist vorstellbar, daß Objectinstances nur dann freigegeben werden, wenn kein Pointer mehr auf sie zeigt. Hierfür kann man einen Use-Zähler benutzen. Eine echte Freigabe erfolgt nur, wenn der Zähler Null erreicht hat. Hierfür hat am besten der Compiler die Steuerung. Wir haben auf diese Weise vermieden, die Reservierung zusätzlichen Speicherbedarfs in die OOP-Semantik aufzunehmen.

Ken Belcher ist Präsident der Realia Inc. in Chicago.