202 59 10MB
German Pages 179 [180] Year 1978
de Gruyter Lehrbuch Schulz • Methoden des Softwareentwurfs und Strukturierte Programmierung
Arno Schulz
Methoden des Softwareentwurfs und Strukturierte Programmierung
w DE
_G Walter de Gruyter • Berlin • New York 1978
CIP -Kurz titelaufnahme
der Deutschen
Bibliothek
Schulz, Arno: Methoden des Software-Entwurfs und Strukturierte Programmierung. - 1. Aufl. - Berlin, New York: de Gruyter, 1978. (De-Gruyter-Lehrbuch) ISBN 3-11-007336-6
© Copyright 1978 by Walterde Gruyter & Co., vormals G. J. Göschen'sche Verlagshandlung, J. Guttentag, Verlagsbuchhandlung, Georg Reimer, Karl J. Trübner, Veit & Comp., Berlin 30. Alle Rechte, insbesondere das Recht der Vervielfältigung und Verbreitung sowie der Übersetzung, vorbehalten. Kein Teil des Werkes darf in irgendeiner Form (durch Photokopie, Mikrofilm oder ein anderes Verfahren) ohne schriftliche Genehmigung des Verlages reproduziert oder unter Verwendung elektronischer Systeme verarbeitet, vervielfältigt oder verbreitet werden. Satz: Satzstudio Frohberg, 6463 Freigericht 1. - Druck: Color-Druck, Berlin. Bindearbeiten: Dieter Mikolai, Berlin. - Printed in Germany.
Vorwort Dieses Buch wendet sich an alle diejenigen unserer modernen Informationsgesellschaft, die Softwareprodukte benutzen, planen, entwerfen oder implementieren. Es ist aus Vorträgen und Vorlesungen entstanden, die der Verfasser in den letzten Jahren über die Methodik des Softwaredesigns und der Softwaredokumentation gehalten hat. Das Schlagwort von der „Softwarekrise" weist daraufhin, daß unter den Anwendern der Datenverarbeitung teilweise ein Unbehagen über die Qualität und die Fähigkeiten großer Programmsysteme besteht. Während der Computer als Hardwaregebilde durch die Verwendung modularer Bauelemente keine Probleme mehr bietet, ist die Situation auf dem Softwaregebiet eine ganz andere. Wir stehen dort erst am Anfang einer Entwicklung, die uns lehren soll, mit komplexen Systemen, wie beispielsweise integrierten Informationssystemen, umzugehen. Lange Zeit hindurch verfolgte jede Programmierausbildung das Ziel, die Syntax einer speziellen Programmiersprache zu lernen, um sie in exemplarischen Fällen richtig anwenden zu können. Demgegenüber wurde die „Konstruktion" und die „Wartung" von Softwarebausteinen nicht gelehrt, sondern der Intuition und der Erfahrung des einzelnen Programmierers und Benutzers überlassen. Insofern ist es gerechtfertigt, diese Stufe der Softwaretechnologie als handwerkliche Tätigkeit einzuordnen, bei der allein die Fähigkeiten des einzelnen Programmierers die Güte eines Programmes bestimmen. Dieser Zustand ist für eine hochindustrialisierte Gesellschaft aber äußerst unbefriedigend. Mit Dijkstras Aufforderung, Programme ohne GOTO-Anweisungen zu schreiben, begannen Universitäten und Laboratorien der Computerindustrie systematisch, eine Softwaretechnologie zu entwickeln. Erfahrungen, die wir auf diesem Gebiet, sowohl in der Programmierausbildung als auch in eigenen Forschungsprojekten gesammelt haben, werden in diesem Buch zusammengefaßt und flir den Praktiker aufbereitet weitergegeben. Sie sind aber nicht nur für den Software-Entwurf interessant, sondern haben bereits allgemeine Bedeutung für den Systementwurf und den Einsatz von DV-Anlagen erlangt. Eine Fallstudie durchzieht als „roter Faden" die gesamte Darstellung dieses Buches. Grundlage aller Programmierbeispiele ist die problemorientierte Programmiersprache PL/1, die durch die internationale Norm ANSI X3.53—1976 vom 14. Oktober 1976 allgemeine Bedeutung erlangt hat. Meine langjährige Sekretärin, Frau Ulrike Koppler, übernahm wiederum die mühevolle Übertragung des Manuskriptes vom Tonband in Reinschrift. Ihr und dem de Gruyter-Verlag, der bemüht war, alle Wünsche des Autors zu erfüllen, sei an dieser Stelle herzlich gedankt. im Juli 1978
Arno Schulz
Inhalt
1. Einleitung
9
2. Programmierung als Softwaretechnologie
11
3. Einführung in eine Fallstudie 3.1 Aufgabenstellung 3.2 Eine klassische Lösung
15 15 16
4. Kritik der herkömmlichen Programmierung: Zur Vermeidung von GOTO-Anweisungen 4.1 Strukturierte Programmierung (Bauelementekunde des Softwareentwurfs) 4.2 Ein erster Versuch, die Fallstudie ohne GOTO-Anweisungen zu programmieren
40
5. Strukturierter Softwareentwurf (Konstruktionslehre des Softwareentwurfs) 5.1 Zielsetzung 5.2 Methoden des Top-down Entwurfs 5.2.1 Die HIPO-Technik 5.2.2 Die Jackson-Methode 5.2.3 Der Ansatz von Warnier (LCP-Methode) 5.2.4 Die Constantine-Methode 5.2.4.1 Grundlagen 5.2.4.2 Das Entwurfsverfahren 5.2.5 Die SADT-Methode 5.3 Zusammenfassung
49 49 53 53 56 59 62 62 68 72 75
6. LITOS (Linzer Technique of Softwaredesign), eine neue Methode des Softwareentwurfs und der Softwaresimulation 6.1 Zielsetzung 6.2 Die vier Entwurfsphasen in LITOS 6.2.1 Der Grobentwurf 6.2.2 Der Feinentwurf 6.2.3 Die Ableitung der Programmsteuerung aus dem Feinentwurf 6.2.4 Simulation und Test der Programmsteuerung 6.3 Strukturierte Programmierung der Fallstudie 6.4 Ein Vergleich der LITOS-Methode mit der Jackson-Methode 6.5 Zur Änderungsfreundlichkeit von LITOS-Produkten
28 28
80 80 81 81 85 93 95 104 114 134
8
Inhalt
7. Weiterführende Untersuchungen zur Software-Konstruktionslehre . . . . 7.1 Anwendung der LITOS-Methode auf allgemeine Entwurfsaufgaben 7.2 Organisation von Programmierprojekten in LITOS 7.3 Globale und lokale Programmschleifen 7.4 Strukturierter Entwurf simultaner Prozesse
149 149 153 155 159
8. Ausblick
168
Anhang
169
Literaturverzeichnis
171
Sachregister
175
1. Einleitung
Programmierer schreiben seit mehr als 20 Jahren größere Programmsysteme. Obwohl sich in dieser Zeit die verwendeten Programmiersprachen stark gewandelt haben, sind erstaunlicherweise zwei Problemkomplexe gleich geblieben: (1) (2)
Programmprodukte sind seit eh und je fehlerbehaftet; jede Programmiertätigkeit ist kostenaufwendig.
Aus der Systemprogrammierung stammt der Erfahrungswert, daß im Abschlußtest im Mittel auf 1 K-Byte Programmgröße zwei Fehler gefunden werden. Der größte Teil dieser Fehler, nämlich 64 %, gehen bereits auf den Entwurf des Programms zurück [4, S. 104]. Sie wurden aber erst im Abschlußtest entdeckt, das zeigt, daß der Programmentwurf, wie er bisher ausgeführt wurde, offenbar nicht direkt zu überprüfen war. Viele Programmierer haben sich mit dem Faktum der Programmierfehler wie mit einem Naturgesetz abgefunden. Sie versuchen, durch Testen die Situation zu verbessern. Testen kann aber nur zeigen, daß noch Fehler in einem Programm vorhanden sind. Es kann nie die Fehlerfreiheit in einem Programm beweisen [12, S. 864]. Hinsichtlich der Programmierkosten kommt aus den USA die Behauptung, daß derzeit 70 % bis 80 % der gesamten Kosten einer DV-Installation Softwarekosten sind [5]. In den nächsten 10 Jahren soll dieser Anteil sogar bis auf 90 % ansteigen. Hinter diesen Zahlen verbirgt sich zunächst das Faktum, daß die Programmiertätigkeit primär von Menschen ausgeübt wird. Sie läßt sich nur sehr schwer automatisieren. Damit wirkt sich aber auch der Anstieg der Personalkosten, der in allen Bereichen unserer modernen Wirtschaft zu beobachten ist, in der Programmierung besonders drastisch aus. Hingegen zeigen die Hardwarekosten aufgrund des hohen Automationsgrades der Hardwareproduktion und der Hardwareentwicklung eine stark fallende Tendenz. Interessant ist eine Analyse der Softwarekosten
[7,18]:
70 % der Softwarekosten sind Wartungskosten, 30 % Entwicklungskosten. Aufteilung der Entwicklungskosten: 30 % Programmentwurf 20 % Programmierung 50 % Programmtest. Diese Zahlen deuten darauf hin, daß es bisher in der Programmierung keine ausgereifte, auf wissenschaftlicher Forschung basierende Entwurfsmethodik gab; denn sonst würden die Wartungs- und Testkosten kleiner sein. Vom Produktions-
Einleitung
10
verfahren her gesehen, leben viele Programmierer noch im vorindustriellen Zeitalter der handwerklichen Fertigung, in der allein die Fähigkeiten des einzelnen für die Qualität eines Produktes ausschlaggebend waren. Programme sind aber nichts weiter als Industrieprodukte, wenn auch immaterieller Art. Soll Software als Produkt und nicht als Kunstwerk entstehen, dann benötigt man eine Programmiertechnologie, die den Entwurfsprozeß, die Umsetzung seiner Ergebnisse in eine maschinenorientierte oder problemorientierte Programmiersprache sowie die Programmdokumentation systematisiert. Eine solche Technologie entwickeln wir in diesem Buch an Hand von Beispielen1.
1
Alle Programmierbeispiele beziehen sich auf den PL/l-Optimizing Compiler (Release 3.0).
2. Programmierung als Softwaretechnologie
Seit 30 Jahren gibt es nun den Computer. Der Begriff Software kam aber erst Mitte der 60er Jahre auf, und zwar mit der 3. Rechenmaschinengeneration. Dies zeigt, daß Programmierfragen in der Datenverarbeitung lange Zeit hindurch nur eine untergeordnete Rolle spielten, eine Entwicklung, unter deren Folgen wir noch heute leiden; denn die Konstruktionsprinzipien der Hardware sind seit langem bekannt und wissenschaftlich abgesichert, eine Aussage, die analog für das Softwaregebiet erst seit kurzem gilt. Wenn man in der Fachliteratur nach einer Definition des Begriffs Software sucht, dann stößt man auf eine weite und eine enge Fassung. (1)
Weite Bestimmung des Begriffs Software: Sie ergänzt die Hardware zu einem funktionsfähigen datenverarbeitenden System und umfaßt alle die Betriebsmittel, die immateriellen Charakter haben, also nicht nur Programme, sondern auch ihre Dokumentation bis hin zu allgemeinen Systembeschreibungen [42, S. 317].
(2)
Enge Bestimmung des Begriffs Software: Alle Programme eines digitalen Rechensystems, die zusammen mit den Eigenschaften der Rechenanlage die Grundlage möglicher Betriebsarten und Anwendungsarten des digitalen Rechensystems bilden [14].
Die weite Begriffsbestimmung hat den Vorteil, daß sie die wichtige Frage der Softwaredokumentation in eine wissenschaftliche Bearbeitung mit einbezieht. Jahrelange Erfahrungen haben gezeigt, daß eine Programmdokumentation nur dann mit genügender Sorgfalt erstellt wird, wenn sie quasi als Abfallprodukt beim Programmentwurf von selbst mit entsteht. Jede nachträgliche Dokumentation führen Programmierer schon allein aus Zeitgründen entweder gar nicht oder nur sehr oberflächlich aus. Oder anders ausgedrückt, der Programmentwurf muß so beschaffen sein, daß er selbstdokumentierend wirkt. Auf die Bedeutung der Dokumentation von Programmprodukten braucht man nicht besonders hinzuweisen. Es sei nur der Aspekt der Änderungsfreundlichkeit genannt, der inhärent mit einer guten Dokumentation verbunden ist. Als nächstes ist der Begriff Technologie zu untersuchen, um anschließend die Zusammensetzung Softwaretechnologie einzuführen. Bestimmung des Begriffs Technologie: Wissenschaftliche Disziplin von der Gewinnung und Verarbeitung materieller und immaterieller Stoffe zu betriebssicheren technischen Produkten. Ein wesentliches Merkmal jeder Technologie, egal ob es sich um Hardware oder Software handelt, ist darin zu sehen, daß sie betriebssichere Produkte quasi ga-
12
2. Programmierung als Softwaretechnologie
rantiert. Von einer Technologie kann also erst dann gesprochen werden, wenn sie Fertigungsverfahren anbietet, die die Merkmale einer industriellen Produktion aufweisen. Dasselbe gilt für das Gebiet der Softwaretechnologie. Bestimmung des Begriffs Softwaretechnologie: Jener Teil der Informatik, der sich mit den Konstruktionsprinzipien und den Werkzeugen für die Produktion betriebssicherer Systemsoftware und Anwendungssoftware befaßt. Um die Forderung nach Betriebssicherheit erfüllen und überprüfen zu können, benötigt man Kriterien für die Beurteilung der Güte von Programmsystemen. In der nachfolgenden Aufstellung werden einige qualitative Qualitätsmerkmale flir Softwareprodukte definiert: (1)
Benutzerfreundlichkeit:
Leicht verständliche und anwendungsorientierte Schnittstelle zwischen Benutzer und Programmprodukt
(2)
Zuverlässigkeit:
Wahrscheinlichkeit, daß ein Programmprodukt unter vorgegebenen Bedingungen während eines definierten Zeitintervalls seine Funktion erfüllt [28, S. 12]
(3)
Fehlerfreiheit:
Einhaltung der vom Benutzer vorgegebenen Programmspezifikationen [28, S. 24]
(4)
Lesbarkeit:
Verwendung von Elementen der natürlichen Sprache sowohl in der Programmierung als auch in der Programmdokumentation
(5)
Änderungsfreundlichkeit:
hierarchische Gliederung eines Programms in unabhängige Module, die austauschbar sind
(6)
leichte Wartbarkeit:
Programmstrukturierung, selbstdokumentierende Programme
(7)
Universalität:
für mehrere Aufgaben einsetzbar
(8)
geringe Kosten:
standardisierte Konstruktion, Verringerung des Testaufwandes, rationelles Management
(9)
hoher Wirkungsgrad:
geringer Speicherbedarf, kleine Durchlaufzeit.
In vielen Softwareprodukten sind die Benutzerschnittstellen ausgesprochen unsystematisch von den Entwicklungsingenieuren definiert worden. Sie sind damit für den Benutzer schwer verständlich. Diese Beobachtung gilt nicht nur für den Bereich der Betriebssysteme, sondern auch für Standardanwendungssoftware, die von Computerherstellern oder Softwarehäusern angeboten wird. Von einer Benutzerfreundlichkeit kann dabei keine Rede sein. Die beiden Kriterien Zuver-
2. Programmierung als Softwaretechnologie
13
lässigkeit und Fehlerfreiheit sind nicht identisch, obwohl sie in der Praxis häufig synonym verwendet werden. Fehlerfreiheit ist die Voraussetzung für die Zuverlässigkeit. Beim Testen, wie es heute üblich ist, werden gleichzeitig beide Anforderungen überprüft, so daß Abweichungen von den Spezifikationen nicht eindeutig zu identifizieren sind. Beim Programmdesign sollte versucht werden, sie getrennt auszutesten. Dazu ist es notwendig, zunächst die Spezifikationen durch einen Simulationslauf zu überprüfen. Verfahren dafür sind unter der Bezeichnung „RSL" (Requirements Statements Language) in der Literatur bekannt geworden [3, S. 6]. Um die Wartungskosten eines Programmproduktes zu senken, ist ein weiterer wichtiger Faktor beim Programmdesign die Änderungsfreundlichkeit. Der Schlüssel hierfür liegt in der Modularisierung von Programmen. Programmodule mit mehr als tausend Anweisungen, wie sie tatsächlich noch vorkommen sollen, sind natürlich in ihrer Struktur nicht mehr zu übersehen und damit absolut änderungsfeindlich. Die Kardinalfrage, die in diesem Zusammenhang zu untersuchen sein wird, ist darin zu sehen, nach welchen Kriterien Module zu entwerfen sind. In der Praxis findet man leider häufig Programme, die so schlecht lesbar sind, daß es für den Nichteingeweihten leichter ist, ein Programm neu zu schreiben, als sich in ein von einem anderen Programmierer geschriebenes Programm einzuarbeiten. Dabei können ganz einfache Regeln, wie sprechende Bezeichner für Variable, schon ein Stück weiterhelfen. Beispiel:
herkömmliche Programmierung: besser:
READ FILE (A) INTO (B); READ FILE (LOCHKARTE) INTO (BEWEGUNGSSATZ _ 1);
Aus wirtschaftlichen Gründen sollte jedes Programmprodukt für mehrere Aufgaben einsetzbar sein. Je universeller aber ein Programm ist, desto aufwendiger wird es bezüglich der Kosten und der benötigten Betriebsmittel. Ein typisches Beispiel dafür sind die Standardsoftwarepakete der Computerfirmen. Hier das richtige Maß zu finden, ist eine Hauptaufgabe des Programmdesigns. Softwaretechnologie, auch Software-Engineering genannt, ist ein Teilgebiet der Informatik. Diese Disziplin, als die Ingenieurwissenschaft von der Konstruktion und dem Einsatz computerorientierter Informationssysteme, basiert wie jede ingenieurwissenschaftliche Fachrichtung auf zwei Säulen, nämlich einer für dieses Fachgebiet spezifischen Theorie und der Erfahrung vieler Praktiker. Sie hat die Aufgabe, zu einer Symbiose beider Grundlagen zu kommen. Die Ergebnisse ihrer Forschungsarbeit lassen sich in drei Kategorien zusammenfassen: — — —
einer fachspezifischen Konstruktionslehre einer systematischen Ordnung der Bauelemente und Werkstoffe, die sie verwendet und fachspezifische Prüfverfahren zur Qualitätskontrolle.
14
2. Programmierung als Softwaretechnologie
Dieses Schema, angewendet auf Software-Engineering als dem Teil der Informatik, der sich mit der Programmkomponente eines Informationssystems befaßt, liefert auf der Grundlage des gegenwärtigen Erkenntnisstandes folgende Detaillierung: (1)
Software-Konstruktionslehre: 1.1 Top-down Design 1.2 Top-down Programmierung 1.3 Methode der Abstraktionsstufen 1.4 Projektorganisation in Form des Chefprogrammiererteams
(2)
Software-Bauelementekunde: Verwendung einer eingeschränkten Menge von Programmbausteinen, wobei das markanteste Merkmal die Vermeidung der GOTO-Anweisung ist (Strukturierte Programmierung).
(3)
Software-Prüfverfahren: Hierfür werden bis jetzt nur organisatorische Maßnahmen angeboten, aber noch keine wissenschaftlich fundierten Prüfverfahren, die einem Vergleich mit der Qualitätskontrolle in der Hardwarefertigung standhalten.
In dieser Arbeit liegt der Schwerpunkt auf der Seite der Softwarekonstruktionslehre und zwar beim Top-down Design und der Top-down Programmierung. Die Bauelemente der strukturierten Programmierung werden nur soweit dargestellt, wie es für das Verständnis von Konstruktionsverfahren notwendig ist. Damit unsere Ausfuhrungen aber nicht zu abstrakt bleiben, sollen sie mit Beispielen aus der Praxis belegt werden.
3. Einführung in eine Fallstudie
3.1 Aufgabenstellung In Berichten über strukturiertes Programmieren konstruieren die Autoren häufig kleine Programmierbeispiele, die wenig Bezug zur Praxis haben. Wir wollen dagegen im Sinne der „Case Study Method" einen Fall aus der betrieblichen Datenverarbeitung verwenden, der exemplarische Bedeutung hat. Damit die Methoden des Top-down Entwurfs überhaupt dargestellt werden können, muß dieses Beispiel relativ umfangreich sein, andererseits aber auch nicht zu komplex, weil es sonst den Rahmen dieses Buches sprengen würde. Es hat sich gezeigt, daß eine der klassischen Aufgaben aus dem Gebiet der betrieblichen Datenverarbeitung, nämlich das Verarbeiten einer Bewegungsdatei zusammen mit einer Bestandsdatei, für unsere Zwecke geeignet ist. Diese Aufgabe ist auch zum Modell der normierten Programmierung geworden (s. Kap. 5.3). Aus Gründen der Übersichtlichkeit werden wir viele Teilaufgaben im Rahmen dieser Fallstudie nur andeuten und nicht in allen Einzelheiten entwerfen und programmieren. Nach Langers gibt es grundsätzlich drei Möglichkeiten, den Ablauf dieses informationellen Arbeitsprozesses zu organisieren [31, S. 236 ff. und S. 273 ff.]: Verfahren A: Sortierung der Bewegungsdaten, sequentieller Zugriff zu den Stammdaten Verfahren B: Sortierung der Bewegungsdaten, wahlfreier Zugriff zu den Stammdaten Verfahren C: keine Sortierung der Bewegungsdaten, wahlfreier Zugriff zu den Stammdaten. Das Verfahren „A" wird vor allem bei Magnetbanddateien, „B" und „C" bei Magnetplattendateien verwendet. Obwohl in der betrieblichen Datenverarbeitung der Magnetplattenspeicher häufig den Magnetbandspeicher verdrängt hat und damit auch die Verfahren „B" und „C" dominieren, soll trotzdem das Verfahren „A" zunächst aufgegriffen werden 1 ; denn es ist didaktisch gesehen interessanter. Wenn ein Organisator dieses Verfahren einsetzt, fallen folgende Probleme beim Programmentwurf an: (1)
1
Es sind zwei in aufsteigender Folge des Ordnungsmerkmals sortierte Eingabedateien (Bewegungsdatei und Stammdatei) auf Identität ihrer Ordnungsmerkmale (Kontonummern) zu überprüfen (Mischproblem).
Im Kapitel 6.5 wird eine Lösung für das Verfahren „B" angegeben.
3. Einführung in eine Fallstudie
16
Je nach dem Vergleichsergebnis: gleich (bewegte Stammsätze) höher (nichtbewegte Stammsätze, aber auch Gruppenwechsel) niedriger (Bewegungen ohne Stammsätze) durchläuft das Programm unterschiedliche Arbeitsprozesse. (2)
Wenn die Dateien keine fiktiven Endkontonummern (z. B. „99999") aufweisen, können Dateiendebedingungen an verschiedenen Stellen im Programm auftreten. Sollen im Sinne der strukturierten Programmierung GOTO-Anweisungen vermieden werden, dann ist die Programmierung des Dateischließens, wie das Verarbeiten der restlichen Bewegungsdatei, nachdem die Stammdatei ihr Ende erreicht hat, kein triviales Problem mehr.
(3)
Die Aufgabe ist bereits so komplex, aber auch nicht zu komplex, um eine Reihe von Entwurfsfragen und Entwurfsstrategien untersuchen zu können, wie: — schematische Darstellung eines Programmentwurfs — Strukturierungsstrategien (Methoden des Top-down Entwurfs) — Trennung Steuerfluß - Prozeßfluß — Simulation des Steuerflusses — Simultanisierbarkeit der Lösung (strukturierte Programmierung in einer Multitasking-Umgebung) — Laufzeitproblematik (statische Programmlänge — dynamische Programmlänge).
Um einen Bezugspunkt für die weiteren Untersuchungen zu haben, soll zunächst eine „klassische Lösung" für diese Aufgabenstellung erarbeitet werden.
3.2 Eine klassische Lösung Ausgangspunkt der klassischen Lösung ist der Datenflußplan für diese Aufgabe, wie ihn Abbildung 1 zeigt. Eine Stammdatei, die auf Magnetbändern steht, ist durch Bewegungsdaten, die mit Lochkarten in die Datenverarbeitungsanlage eingegeben werden, periodisch auf den neuesten Stand zu bringen. Das Ordnungsmerkmal beider Dateien soll eine Kontonummer sein. Sowohl die Stammdaten als auch die Bewegungsdaten liegen in aufsteigender Ordnung sortiert vor. Eine Kontrolle auf die Sortierfolge soll zunächst nicht in das Programm eingebaut werden. Die Datenverarbeitungsanlage gibt die auf den neuesten Stand gebrachte Magnetbanddatei wieder aus. Weiterhin ist eine Liste zu drucken, die den alten Saldo, die Bewegungen und den neuen Saldo enthält. Das geforderte Listenbild zeigt Abbildung 2. Der alte Saldo wird zusammen mit den Daten des ersten Bewegungssatzes in einer Zeile ausgedruckt. Liegen mehrere Bewegungssätze für
17
3.2 Eine klassische Lösung
BEWEGUNGSDATEN, SORTIERT N.
KONTONUMMER
Abb. 1 Datenflußplan für die klassische Lösung der Dateiverarbeitung im „Verfahren A" nach Langers [31].
eine Kontonummer vor, dann ist für jeden weiteren Satz eine neue Zeile vorzusehen. Der neue Saldo scheint in einer eigenen Summenzeile auf. Im Fall der „Bewegungen ohne Stammsätze" ist als alter Saldo der Betrag Null einzusetzen. Nichtbewegte Stammsätze sind nur mit ihrem alten Saldo zusammen mit einem Datum aus der Stammdatei auszugeben. In der klassischen Programmierung gab es keine Restriktionen hinsichtlich der Programmbausteine, die dem Programmierer für seine Arbeit zur Verfugung standen. Sie resultierten aus der Syntax der verwendeten Programmiersprache. Außerdem sollte jede Programmiertätigkeit damit beginnen, daß der ProgramK
X
000001
ALTER H
SAU»
10188.50
BELEG-NR. 1005
DATUM
SOLL
711020
HABEN
NEUER
H
2905.00
000003
S
13713.91
000001
S
1170.80
000005
H
1103.11
000020
0.00
BEWEGTER
1005.50
S T A M M S A T Z
H 000002
SAU»
11191.00
711011 711011
NICHT-
711023
B E W E S T E
STAMMSÄTZE
711011
8533
711020
8531
711020
9000,01
BEWEGUNGEN 0 H N E
8000.01
STAMMSATZ H
Abb. 2 Druckliste der Dateiverarbeitung im „Verfahren A".
1000.00
3. Einführung in eine Fallstudie
18
WURDE NICHT EINGEZEICHNET
DRUCKAUFBEREITUNG LINKS
'
GRUPPENWECHSEL BEWEGUNGEN OHNE
V
STAMMSÄTZE
\1
GRUPPEN-
.f
ARBE I TEN
¡mSFI
/SCHREIBENF /STAMMS. / ELSE
ANFANG J J
Abb. 3 siehe auch S. 19.
OCHALTER K *
THEN
Q ANFANG )
[O
SYMBOL F, ZUSAMMENFUHRUNG (NICHT DIU 66 001)
3.2 Eine klassische Lösung
19
GZ)
ON ENDFILE-TEIL (BEWEGUNGSDATEI)
B 999999
ELSE
^SCHALTE
THEN
GRUPPENWECHSEL
NICHTBEWEGTE STAMMSÄTZE
BEWEGUNGEN OHNE
UND GRUPPENWECHSEL
STAMMSÄTZE
0D CD ENDE.
Abb. 3 Programmablaufplan für die klassische Lösung der Dateiverarbeitung im „Verfahren A".
mierer zunächst einen Programmablaufplan entwirft. Diesen Regeln folgend zeigt Abbildung 3 den Programmablauf für die klassische Lösung der Dateiverarbeitung. Er soll zunächst, ohne daß wir die Dateiendebedingungen „EOF" (Abkürzung für: End of File) berücksichtigen, kurz erläutert werden. Der Programmablauf beginnt mit dem Lesen des 1. Bewegungssatzes und des 1. Stammsatzes. Die Daten des Stammsatzes, die auszudrucken sind, nämlich Kontonummer, alter Saldo und ein Hinweis, ob es sich um einen Habenbetrag („H") oder einen Sollbetrag („S") handelt, werden in der Funktionseinheit „Druckaufbereitung links" zum Drucken vorbereitet. Der Druckprozeß selbst wird an dieser Stelle noch nicht programmiert. Anschließend ist das Ordnungsmerkmal der Bewegungsdatei (B) mit dem Ordnungsmerkmal der Stammdatei (S) zu vergleichen, um festzustellen, ob es sich um bewegte Stammsätze (B = S), nichtbewegte Stammsätze bzw. Gruppen wechselarbeiten (B > S) oder Bewegungen ohne Stammsätze (B < S) handelt. Aufgrund dieser Identifizierung werden drei verschiedene Wege im Programmablaufplan durchlaufen. Im Fall der bewegten Stammsätze wird jeweils ein Bewegungssatz verarbeitet und seine Daten werden, gegebenenfalls mit den Daten des Stammsatzes, ausgedruckt. Außerdem ist ein Schalter zu setzen, um im Fall B > S den Gruppenwechselprozeß von der Teilfunktion „nichtbewegte Stammsätze" unterscheiden zu können. Nach dem Lesen eines neuen Bewegungssatzes verzweigt das Programm zurück zum Identifizierungsprozeß.
20
3. Einführung in eine Fallstudie
Für das Identifizierungsergebnis B > S sind im Fall der Schalterstellung „Ein" die üblichen Gruppenwechselarbeiten durchzuführen, wie Druckaufbereitung der Endsumme, um sie anschließend ausdrucken zu können. Für die nichtbewegten Stammsätze, d. h. Schalterstellung „Aus", wird an dieser Stelle der alte Saldo ausgegeben. Unabhängig von dieser Schalterstellung muß anschließend das Programm immer einen neuen Stammsatz in die Stammdatei schreiben und den nächsten Stammsatz lesen (Programm-Marke: „Anfang"). Für den Fall B < S (Bewegungen ohne Stammsätze) ist als erstes ein neuer Stammsatz anzulegen. Anschließend sind Bewegungssätze solange zu lesen, bis ein Wechsel im Ordnungsmerkmal auftritt. Da zu dieser Zeit bereits ein neuer Stammsatz im Hauptspeicher steht, nämlich der, für den die Bedingung B < S gilt, muß das Programm den neu anzulegenden Stammsatz in einem eigenen Speicherbereich aufbauen, in den nachfolgenden Programmbeispielen immer P U F F E R S T AMMSATZ" genannt. Aus demselben Grund kann das Programm in diesem Zweig auch nicht für die Gruppenwechselarbeiten, das Drucken der Summenzeile und das Schreiben des Stammsatzes auf den Gruppenwechselzweig von B > S zurückgreifen. Er muß neu programmiert werden 2 . Da die binäre Variable „SCHALTER 1" erst durch die EOF-Arbeiten aktiviert wird, geht die Programmausführung am Ende des Zweiges B < S zurück zur Druckaufbereitung des im Hauptspeicher stehenden Stammsatzes 3 (Marke: Anfang_l). Damit kommen wir nun zur Frage, welche Arbeiten durchzuführen sind, wenn entweder die Bewegungsdatei oder die Stammdatei ihr Ende erreicht hat. Der einfachere Fall ist der, daß keine Bewegungssätze mehr vorliegen. Dann sind zunächst noch die Gruppenwechselarbeiten für die letzte Kontonummer durchzuführen und gegebenenfalls noch der Rest der Stammdatei als nichtbewegte Stammsätze zu verarbeiten. Dafür ist es aber notwendig, die Bedingung B > S zu erfüllen. Wir erreichen dies dadurch, daß im „ON ENDFILE-Teil" der Bewegungsdatei die Kontonummer der Bewegungssätze auf einen Wert gesetzt wird, von dem man annehmen kann, daß er in dieser Datei nie erreicht wird. Da die Bewegungsdatei an zwei Stellen im Programmablauf gelesen wird, nämlich entweder im Zweig der bewegten Stammsätze oder der Bewegungen ohne Stammsätze, muß das Programm als nächstes die Stellung des „Schalters" abfragen. Ist er eingeschaltet, dann trat die Dateiendebedingung offensichtlich bei der Verarbeitung von bewegten Stammsätzen auf. In diesem Fall ist die Programmausfuhrung bei der Marke „Nichtbewegte Stammsätze und Gruppenwechsel" fortzusetzen, andernfalls bei den Gruppenwechselarbeiten der Bewegungen ohne Stammsätze. Nachdem die Gruppenwechselarbeiten für die letzte bewegte Kontonum2
3
Dies würde dann nicht erforderlich sein, wenn man die Gruppenwechselarbeiten in einem eigenen Unterprogramm zusammenfaßt. Die herkömmliche Programmierung zeichnete sich aber gerade dadurch aus, daß die Programmierer das Mittel der Modularisierung nur sparsam einsetzten. Dieser Stammsatz wurde bereits einmal zum Druck aufbereitet, aber durch die Bewegungen ohne Stammsatz im Hauptspeicher überschrieben.
3.2 Eine klassische Lösung
21
mer ausgeführt sind, muß das Programm zum Lesen der Stammdatei verzweigen. Durch die beiden Bedingungen „B > S" und „Schalter ausgeschaltet" erreicht man, daß das Programm den Rest der Stammdatei als nichtbewegte Stammsätze verarbeitet. Schließlich wird auch beim Lesen der Stammdatei irgendwann einmal die Dateiendebedingung ansprechen. Dann verzweigt der Programmablauf über den THENZweig der Abfrage „B = 999999?" zum Programmende (Marke: „ E n d e _ l " ) . Tritt allerdings die Dateiendebedingung in der Stammdatei vor dem Ende der Bewegungsdatei auf, dann ist ihr Rest als Bewegungen ohne Stammsätze zu verarbeiten. Wir erreichen dies im Programmablauf dadurch, daß jetzt bei der Abfrage „B = 999999?" der ELSE-Zweig anspricht. Dort wird ein „Schalter_l" gesetzt und zum Anlegen eines neuen Stammsatzes verzweigt. Das Programm verarbeitet dann die Bewegungen ohne Stammsätze wieder wie bisher. Bei jedem Gruppenwechsel wiederholt sich dieser Ablauf, bis die Bewegungsdatei ihr Ende erreicht hat. Zusammenfassend kann bereits an dieser Stelle unserer Untersuchungen festgestellt werden, daß sich bei der klassischen Lösung der vorgegebenen Aufgabe aufgrund der vielen Verzweigungen zusammen mit der Verwendung von Programmschaltern ein sehr unübersichtlicher Programmablauf ergibt, der eo ipso fehleranfällig ist. Weiterhin fällt auch sofort auf, daß die drei Teilfunktionen dieser Aufgabe, nämlich die Verarbeitung der bewegten Stammsätze, der nichtbewegten Stammsätze und der Bewegungen ohne Stammsätze, im Programmablaufplan kaum zu erkennen sind, obwohl sie den Hauptteil der Lösung konstituieren. Es ist deshalb sinnvoll, schon jetzt kritisch zum Darstellungsmittel „Programmablaufplan" Stellung zu nehmen. Es ist heute allgemein anerkannte Lehrmeinung der strukturierten Programmierung, daß die herkömmlichen Programmablaufpläne für die moderne Programmierung überholt sind. Als Begründung wird angeführt: (1)
Für komplexe Programme sind sie zu unübersichtlich.
(2)
Sie zeigen nur einen Ablauf, aber keinen funktionellen Zusammenhang von Programmbausteinen im Sinne eines Organisationsschaubildes auf.
(3)
Man kann sie deshalb auch nur sehr schwer schrittweise verfeinern.
(4)
Der Programmablaufplan stellt nur den Steuerfluß, aber nicht den Datenfluß dar.
Als Grundlage für die weiteren Untersuchungen wollen wir jetzt noch diesen Programmablaufplan in ein Programm umsetzen. Als Programmiersprache greifen wir dabei die problemorientierte Sprache PL/1 auf, die sich für den strukturierten Entwurf und die strukturierte Programmierung gut eignet. Hinsichtlich ihrer Sprachsyntax verweisen wir auf zwei frühere eigene Arbeiten [43,44],
22
3. Einführung in eine Fallstudie
MAGNETBAND_DATEIVERARBEITUNG_KLASSISCH: / * DATEIVEREINBARUNGEN DCL LEINGAB F I L E RECORD I N P U T , MEINGAB F I L E
*/ / * LOCHKARTENEINGABE
RECORD I N P U T ,
MAUSGAB F I L E RECORD OUTPUT, LISTE
PROC OPTIONS(MAIN); */
/ * MAGNETBANDEINGABE
*/
/* MAGNETBANDAUSGABE
*/
F I L E RECORD OUTPUT ENV /*
(F(88)
CTLASA)
DRUCKLISTE
*/;
/ * * * * * * * • * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
/************
SATZVEREINBARUNGEN
************/
/ * VEREINBARUNG BEWEGUNGSSATZ
*/
DCL 1 LOCHKARTENSATZ, 2 KONTONUMMER_BEWEGUNG
CHAR(6) ,
2 BELEGNUMMER_BEWEGUNG
CHAR(4),
2 DATUM_BEWEGUNG
CHAR(6),
2 BETRAG_BEWEGUNG P I C 1 S9999V99 1 , 2 TEXT
CHAR(56),
2 KARTENART
CHAR( 1 ) ;
/ * VEREINBARUNG STAMMSATZ
*/
DCL 1 STAMMSATZ, 2 SATZART
CHAR(1),
2 KONTONUMMER
CHAR(6),
2 DATUM
CHAR(6),
2 SALDO
DEC F I X E D ( 9 , 2 ) ;
/ * VEREINBARUNG PUFFERSPEICHER 2 . DCL 1 PUFFER_STAMMSATZ LIKE
STAMMSATZ
*/
STAMMSATZ;
/ * VEREINBARUNG UEBERSCHRIFTZEILE
*/
UEBERSCHRIFT, 2 VORSCHUBSTEUERUNG CHAR(1)
INIT
('1') ,
2 KONTONUMMER
CHAR(15)
INIT
( 'KONTO-NR.' ) ,
2 ALTER_SALDO
CHAR(15)
INIT
('ALTER
2 BELEG_NUMMER
CHAR(15)
INIT
('BELEG-NR.'),
2 DATUM
CHAR(10)
INIT
('DATUM'),
2 SOLL
CHAR(10)
INIT
('SOLL'),
2 HABEN
CHAR ( 1 0 )
INIT
('HABEN') ,
SALDO')
3.2 Eine klassische Lösung
2 NEUER_SALDO
CHAR(12)
INIT
(1 N E U E R SALDO'
/* V E R E I N B A R U N G DRUCKLISTE */ DCL 1 ZEILE, 2 VORSCHUBSTEUERUNG
CHAR(1)
2 LEERZEICHEN^
CHAR(3)
2 KONTONUMMER
CHAR(6)
2 LEERZEICHEN_2
CHAR(5)
2 AN Z EI GE_S OLL_iHABEN
CHAR(1)
PIC* (6) Z 9 V . 9 9 '
2 ALTER_SALDO 2 LEERZEICHEN_3
CHAR(9)
2 BE LE G_N U M M E R
CHAR(4)
2 LEERZEICHEN_4
CHAR(6)
2 DATUM
CHAR(6)
2 LEERZEICHEN_5
CHAR(3)
2 SOLL
PIC 'ZZZ9V.99'
2 LEERZEICHEN_6
CHAR(3)
2 HABEN
PIC 'ZZZ9.V99' CHAR(2)
2 LEE RZ EICHEN_7
2 ANZEIGE_SOLL_i HABEN_NEU CHAR(1) 2 NEUER_SALDO
PIC'(6)Z9V.99'
2 REST
CHAR(4)
/* BLANKS N A C H ZEILE */ DCL ZEILE_1 C H A R (88) DEFINED DCL (SCHALTER,SCHALTER_1) OPEN F I L E
(LEINGAB),
FILE
(MEINGAB),
FILE
(MAUSGAB),
FILE
(LISTE);
ZEILE;
BIT(1) INIT
/* DRUCKEN U E B E R S C H R I F T Z E I L E W R I T E FILE
(LISTE) F R O M
('O'B);
*/
(UEBERSCHRIFT);
/* DRUCKEN 1 LEERZEILE */ ZEILE_1 = WRITE F I L E ON E N D F I L E
'O'; (LISTE) F R O M
(ZEILE);
(LEINGAB) GOTO ENDE;
24
3. Einführung in eine Fallstudie
ON ENDFILE (MEINGAB) BEGIN; IF KONTONUMMER_BEWEGUNG = '999999' THEN GOTO ENDE_1; ELSE DO; SCHALTER_1 = "TB; GOTO NEUE R_STAMM; END; END; READ FILE (LEINGAB) INTO (LOCHKARTENSATZ); ANFANG: READ FILE (MEINGAB) INTO (STAMMSATZ); ANFANG_1:ZEILE_1 = '0'; ZEILE.KONTONUMMER = STAMMSATZ.KONTONUMMER; ZEILE.DATUM = STAMMSATZ.DATUM; ZEILE.ALTER_SALDO = STAMMSATZ.SALDO; IF STAMMSATZ.SALDO > O THEN ANZEIGE_SOLL_HABEN = 'H'; ELSE IF STAMMSATZ.SALDO < O THEN ANZEIGE_SOLL_HABEN = 'S'; ELSE AN ZEIGE_SOLL_HABEN = ' '; VERARBEITUNG_BEWEGUNGSSAETZE: IF KONTONUMMER_BEWEGUNG = STAMMSATZ.KONTONUMMER THEN DO; /* VERARBEITEN BEWEGUNGSSATZ */ STAMMSATZ.SATZART = KARTENART; STAMMSATZ.DATUM = DATUM_BEWEGUNG; STAMMSATZ.SALDO = STAMMSATZ.SALDO + BETRAG_BEWEGUNG; /* RESTLICHE DRUCKZEILE ERSTELLEN UND DRUCKEN */ ZEILE.BELEG_NUMMER= BELEGNUMMER_BEWEGUNG; ZEILE.DATUM = DATUM_BEWEGUNG; IF BETRAG_BEWEGUNG > O THEN ZEILE.HABEN = BETRAG BEWEGUNG;
3.2 Eine klassische Lösung
25
ELSE ZEILE.SOLL - BETRAG_BEWEGUNG; WRITE FILE (LISTE) FROM (ZEILE); ZEILE_1 = ' /* SCHALTER SETZEN FUER GRUPPENWECHSEL */ SCHALTER - '"TB; READ FILE (LEINGAB) INTO (LOCHKARTENSATZ); GOTO VERARBEITUNG_BEWEGUNGSSAETZE; END; NICHTBEWEGTE_STAMMSAETZE_UND_GRUPPENWECHSEL: IF KONTONUMMER_BEWEGUNG > STAMMSATZ.KONTONUMMER THEN DO; IF SCHALTER THEN DO; /* G RUPPENWECHSELARBEITEN */ SCHALTER = 'O'B; IF STAMMSATZ.SALDO > 0 THEN ANZEIGE_SOLL_HABEN_NEU = 'H'; ELSE IF STAMMSATZ.SALDO < 0 THEN ANZEIGE_SOLL_HABEN_NEU - 'S'; ELSE ANZ EIGE_SOLL_HABEN_NEU • ' '; ZEILE .NEUER_SALDO •= STAMMSATZ .SALDO; END; WRITE FILE (LISTE) FROM (ZEILE); WRITE FILE (MAUSGAB) FROM (STAMMSATZ); GOTO ANFANG; END; /* ANLEGEN NEUEN STAMMSATZ */ NEUER_STAMM: ZEILE_1 « 'O'; PUFFER_STAMMSATZ.SATZART = KARTENART; Z EILE.KONTONUMMER, PUFFER_STAMMSATZ.KONTONUMMER = KONTONUMMER_ PUFFER STAMMSATZ.SALDO
BEWEGUNG; = O;
26
3. Einführung in eine Fallstudie
ZEILE.ALTER_SALDO = O; /* VERARBEITEN BEWEGUNGEN_OHNE_STAMMSAETZE */ BEWEGUNGEN_OHNE_STAMMSAETZE: PUFFER_STAMMSATZ.DATUM = DATUM_BEWEGUNG; PÜFFER_STAMMSATZ.SALDO = PUFFER_STAMMSATZ. SALDO + BETRAG_BEWEGUNG; ZEILE.BELEG_NUMMER « BELEGNÜMMER_ BEWEGUNG; ZEILE.DATUM = DATUM_BEWEGUNG; IF BETRAG_BEWEGUNG > O THEN ZEILE.HABEN = BETRAG_BEWEGUNG; ELSE ZEILE.SOLL = BETRAG_BEWEGUNG; WRITE FILE (LISTE) FROM (ZEILE)j ZEILE_1 - ' '; READ FILE (LEINGAB) INTO (LOCHKARTENSATZ); IF KONTONUMMER_BEWEGUNG = PUFFER_STAMMSATZ. KONTONUMMER THEN GOTO BEWEGUNGEN_OHNE_STAMMSAETZE; GRUPPENWECHSEL_BEWEGUNGEN_OHNE_STAMMSAETZE: ZEILE.NEUER_S ALDO = PUFFER_STAMMSATZ. SALDO; IF PUFFER_STAMMSATZ.SALDO > O THEN ANZEIGE_SOLL_HABEN_NEU = 'H'; ELSE IF PUFFER_STAMMSATZ.SALDO < O THEN ANZEIGE_SOLL_HABEN_NEU = 'S'; ELSE ANZEIGE_SOLL_HABEN_NEU = 1 '; WRITE FILE (LISTE) FROM (ZEILE);
3.2 Eine klassische Lösung
27
ZEILE_1 WRITE F I L E
= ' (MAUSGAB) FROM
' ;
(PUFFER_STAMMSATZ);
I F SCHALTER_1 = ' 1 ' B THEN GOTO ANFANG; ELSE GOTO ANFANG_1; ENDE: KONTONUMMER_BEWEGUNG
=
'999999';
I F SCHALTER THEN GOTO NICHTBEWEGTE_STAMMSAET 2E_UND_ GRUPPENWECHSEL; ELSE GOTO GRUPPENWECHSEL_BEWEGUNGEN_OHNE_ STAMMSAETZE; ENDE_1:
END MAGNETBAND_DATEIVERARBEITUNG_ KLASSISCH;
Aufgrund der sprechenden Bezeichner für Variable zusammen mit dem Programmablaufplan erklärt sich dieses Programm, wie wir glauben, von selbst. Dabei wurde aus Gründen der Benutzerfreundlichkeit die von der Sprachsyntax vorgegebene maximale Länge von Bezeichnern häufig bewußt ignoriert. Moderne Kompilierer begrenzen sie in diesem Fall automatisch auf die maximal zugelassene Wortlänge, drucken aber in den Programmlisten die vom Programmierer verwendeten Bezeichner in ihrer vollen Länge aus. An diesem Programmierbeispiel lassen sich bereits sehr deutlich die Schwächen der herkömmlichen Programmierung darstellen. Wir kommen damit zum Ausgangspunkt der strukturierten Programmierung und der modernen Softwarekonstruktionslehre, nämlich der Frage, inwieweit GOTO-Anweisungen nützlich sind.
4. Kritik der herkömmlichen Programmierung: Zur Vermeidung von GOTO-Anweisungen
4.1 Strukturierte Programmierung (Bauelementekunde des Softwareentwurfs) Die Fachdiskussion über die Konstruktionsprinzipien von Programmen begann im Jahre 1968 mit einer Leserzuschrift von Professor Dijkstra in den „Communications of the ACM", in der er darauf hinwies, daß die Fehlerdichte eines Programms direkt proportional ist der Anzahl der GOTO-Anweisungen, die in diesem Programm vorkommen [10, S. 147 f.]. Er begründet diese Beobachtung mit der Feststellung, daß viele Programmierer überfordert sind, wenn sie genau alle Programmzustände an dem Punkt kennen sollen, zu dem der Programmablauf mit einer GOTO-Anweisung verzweigt. Kritisch ist also in der herkömmlichen Programmierung nicht die Programmverzweigung, sondern die Programmzusammenfuhrung. Um die Fehlerrate drastisch zu senken, fordert deshalb Dijkstra, die GOTO-Anweisung grundsätzlich nicht zu benutzen bzw. Programmiersprachen zu schaffen, die diese Anweisung gar nicht mehr vorsehen. Es erhebt sich natürlich sofort die Frage, ob es überhaupt grundsätzlich möglich ist, jedes beliebige Programm ohne GOTO-Anweisungen zu formulieren. Der Dijkstrasche Ansatz wird erst auf dem Hintergrund einer Untersuchung von Böhm und Jacopini aus dem Jahr 1966 verständlich, in der bewiesen wird, daß jeder Programmierer mit einer minimalen Menge von Programmsteuerungsanweisungen auskommt [6, S. 366 ff.]. Sie besteht aus nur zwei Elementen, nämlich der bedingten Programmverzweigung (IF THEN ELSE-Anweisung) und einer Anweisung zur Steuerung iterativer Programmschleifen (z.B. DO WHILE-Anweisung). Rein intuitiv ist dieses Ergebnis verständlich; denn jeder informationelle Arbeitsprozeß (z.B. stellenweise Addition von zwei mehrstelligen Zahlen) kann als Iterationsprozeß (Schleife) verstanden werden, der so lange durchlaufen wird, bis die Iterationsbedingung erfüllt ist (z.B. keine Stelle mehr zu addieren). Weiterhin kann jede Programmausführung aufgrund eines Auswahlvorganges zu zwei alternativen Anschlußpunkten verzweigen (z. B. Drucken einer Summe ohne Vorzeichen bei positivem Ergebnis und mit Vorzeichen bei negativem Ergebnis). Diese Grundstruktur informationeller Arbeitsprozesse kann man auch aus Abbildung 3 ablesen. Beispielsweise läßt sich dort der Weg durch den Programmablaufplan, der mit dem Vergleich „B = S" beginnt und über das Verarbeiten eines Bewegungssatzes bis zur Abfrage der End of File-Bedingung der Bewegungsdatei verläuft, in eine DO WHILE-Schleife überführen, die von den beiden Bedingungen „B = S" und „ — i EOF Bewegungsdatei" gesteuert wird. Trotzdem wäre es
29
4.1 Strukturierte Programmierung
falsch, strukturierte Programme in der Weise zu erstellen, daß der Programmierer zunächst konventionelle Programmablaufpläne entwirft und sie dann anschließend in der angedeuteten Weise in Schleifenstrukturen zusammen mit IF THEN ELSE-Anweisungen überführt; denn die moderne Form der Programmierung verlangt einen neuen Programmierstil, der schon beim Programmentwurf und nicht erst bei der Programmierung einer Aufgabe einzusetzen hat. Ein Programmierer, der sich an die Dijkstrasche Regel, GOTO-Anweisungen zu vermeiden, halten will, kommt dann in eine Sackgasse, wenn ihm nur eine problemorientierte Programmiersprache zur Verfügung steht, die keine Schleifenanweisung kennt, wie beispielsweise FORTRAN. In diesem Fall kommt er nicht umhin, eine Schleife durch eine IF THEN ELSE-Anweisung zusammen mit einer GOTO-Anweisung zu simulieren. Abbildung 4 zeigt an Hand eines kleinen Beispiels in schematischer Darstellung die Struktur einer solchen Schleifenkonstruktion. Das Programm liest dort vor Eintritt in die Schleife die beiden Variablen „A" und „B". Anschließend erfolgt in der IF-Anweisung der Vergleich auf Identität dieser beiden Variablen bzw. ihrer Ordnungsmerkmale. Bei einem positiven Ergebnis dieser Abfrage wird ein Arbeitsprozeß „A" eingeleitet und an seinem
I F_E IN6AI THEN
ELSE
( J
PROCESS A. READ A ; GOTO IF_ EINGANG;
2uSAMMENfUHRUNG
Abb. 4 IF THEN GOTO-Konstruktion zur Schleifenbildung.
30
4. Kritik der herkömmlichen Programmierung
Ende wiederum eine Variable „A" gelesen. Um diese Programmschleife zu schließen, muß die Programmausführung am Ende dieses Weges mit einer GOTOAnweisung zum Eingang der IF-Anweisung zurückkehren. Rein aus Zweckmäßigkeitsgründen wird in der strukturierten Programmierung, neben der bedingten Anweisung und der Schleifenkonstruktion, auch noch die Fallunterscheidung (CASE-Anweisung) zugelassen, an der ein Programmablauf in mehr als zwei Wege verzweigt [44, S. 154]. Hier herrschte in der PL/l-Programmierung lange Zeit hindurch eine ähnliche Situation wie in FORTRAN bezüglich der DO WHILE-Anweisung. Sie war nur durch eine GOTO-Anweisung in Verbindung mit einer Markenvariablen oder auch durch verschachtelte IF-Anweisungen zu realisieren. Diese beiden Beispiele zeigen schon, daß man aus der Dijkstraschen Regel, GOTO-los zu programmieren, kein Dogma machen sollte. Knuth, der untersucht hat, wie sich die GOTO-lose Programmierung in der Praxis auswirkt, behauptet, daß die strikte Vermeidung jeder GOTO-Anweisung nur zu umständlichen Programmformen führt, unter denen die Leistungsfähigkeit von Programmen leidet [26, S. 261 ff.], weil sie zwangsläufig länger werden. Es ist deshalb sinnvoll, zwischen schädlichen GOTO-Anweisungen im Sinne von Dijkstra und solchen, die konstruktiv bedingt und damit unvermeidbar sind („unschädliche" GOTO-Anweisungen), zu differenzieren. Strukturierte Programmierung steht als Synonym für die im Kapitel 2. eingeführte Bauelementekunde auf dem Softwaregebiet. Es ist üblich, die Anweisungen einer problemorientierten oder auch maschinenorientierten Programmiersprache in zwei Hauptklassen einzuteilen, nämlich Verarbeitungsanweisungen und Anweisungen zur Programmsteuerung. Die Restriktionen, die von der strukturierten Programmierung gefordert werden, liegen ausschließlich auf der Seite der Programmsteuerung. Bei den Verarbeitungsanweisungen sind sämtliche, von der Syntax einer problemorientierten Programmiersprache definierten Anweisungen und Anweisungsarten zugelassen. Es wird zusätzlich nur eine Hierarchie eingeführt, die bei der Ebene der Anweisungen und Vereinbarungen beginnt. Ihr sind die Strecke 1 und der Strukturblock überlagert. Ein Strukturblock ist eine Sonderform des allgemeinen Blockbegriffs problemorientierter Programmiersprachen. Er ist wie dieser dadurch gekennzeichnet, daß Variablennamen nur innerhalb eines Blocks gelten und deshalb auch Speicherplatzreservierungen nur für die Dauer der Aktivität eines Blocks vorgenommen werden (dynamische Speicherplatzzuweisung). Zusätzlich gilt die einschränkende Bedingung, daß ein Strukturblock nur einen Eingang und einen Ausgang haben soll und damit mehrere Eintrittspunkte, wie sie in PL/1 der Begriff „sekundäre Eingangsstelle" [43, S. 158] darstellt, verboten sind. Ein strukturiertes Programm besteht dann aus einer linearen Folge von Strukturblöcken (Liste), die der Steuerfluß in Richtung ihrer Notation durchläuft und ausführt, so daß zwischen den Blöcken keine Ver1
zur Definition s. [44, S. 154]
4.1 Strukturierte Programmierung
31
zweigungen auftreten. Dabei gilt die Faustregel, daß ein Strukturblock nicht länger sein soll als eine Seite DIN A 4. Ein solches Programm ist sehr übersichtlich, und die Programmausführung ist auch von einem Außenstehenden leicht zu verfolgen; denn Programmschleifen und Verzweigungen werden innerhalb eines Blocks abgearbeitet. Da sein Steuerfluß eine Liste darstellt, kann er mit Sprachen zur Listenverarbeitung simuliert werden. Die Fachdiskussion über die Struktur von Programmen, die mit der Dijkstraschen Leserzuschrift begann, hat sich in der Zwischenzeit in zwei Richtungen weiterentwickelt. Wir haben schon auf den Einwand von Knuth hingewiesen, der befürchtet, daß in der Praxis der Dijkstrasche Ansatz die Länge eines Programms vergrößert. Er läßt deshalb für bestimmte Programmfunktionen, wie die Programmierung von Ausnahmebedingungen (z. B. Dateiende), GOTO-Anweisungen weiterhin zu. Andererseits wurde untersucht, ob nicht neben der DO WHILE-Schleife auch andere Schleifenkonstruktionen möglich sind, die leistungsfähigere Programme liefern [37, S. 503 ff.]. Ohne auf den theoretischen Hintergrund einzugehen, wollen wir versuchen, diese Frage hier rein pragmatisch zu beantworten. Das Release 3.0 des PL/l-Optimizing Compilers führt neue Anweisungen zur strukturierten Programmierung ein, nämlich die SELECT-Anweisung, die DO UNTIL-Schleife, die DO REPEAT-Schleife und die LEAVE-AnWeisung2. An einigen kleinen Beispielen soll jetzt kurz ihre Funktion und ihre Bedeutung für die strukturierte Programmierung dargestellt werden. Die DO UNTIL-Schleife unterscheidet sich von der „herkömmlichen" DO WHILE-Schleife darin, daß die Schleifenbedingung am Ende der Schleife abgefragt wird. Deshalb kann jetzt ein Programm, im Gegensatz zur Schleifensteuerung mit „DO WHILE" [44, S. 158], bei der, bevor die Programmausführung in die Schleife eintritt, die Schleifenbedingung einen Wert haben muß, die folgende Anweisungssequenz ausfuhren: I = 0; DO UNTIL (A < B) I = I + 1000 A = 9876.54 B = 1234.56 A = A-1 ; END; Verwendet ein Programmierer die DO WHILE-Schleife, dann muß er häufig, bevor die Programmausführung in die Schleife eintritt, die Iterationsbedingung durch zusätzliche Anweisungen künstlich erzeugen. In solchen Fällen kann die DO UNTIL-Schleife die Programmlänge verkürzen. So müßte beispielsweise ein Programmierer im obigen Programmausschnitt, wenn er die DO WHILE-Anweisung verwendet, vor dem ersten Schleifendurchlauf den beiden Variablen A und B Anfangswerte in der Weise zuweisen, daß die Schleifenbedingung A > B im 2
formale Definition s. Anhang
32
4. Kritik der herkömmlichen Programmierung
ersten Durchlauf gilt. Um zwei verschiedene Schleifenkriterien unabhängig voneinander anwenden zu können, läßt die Sprachsyntax jetzt auch zu, daß iterative Schleifen gemeinsam von einer DO WHILE- und einer DO UNTIL-Anweisung gesteuert werden (s. Anhang). In der DO WHILE-Schleife und der DO UNTIL-Schleife kann der Zustand des Iterationsprozesses nur am Anfang oder am Ende der Schleife abgefragt werden. Knuth und Floyd haben daraufhingewiesen, daß es in der Praxis aber Fälle gibt, wo es notwendig ist, die Schleifenbedingung an jeder beliebigen Stelle innerhalb der Schleife überprüfen zu können, damit die Programmausführung auch an dieser Stelle die Schleife verlassen kann [27, S. 23 ff.]. Um diese Aufgabe zu bewältigen, benötigt der Programmierer zwei zusätzliche Anweisungen, nämlich eine Schleifensteuerung, die ohne eine Schleifenbedingung arbeitet, und eine Anweisung zum Verlassen der Schleife. In PL/1 bewirkt eine bedingungslose Schleifenwiederholung die Notation DO WHILE ( ' l ' B ) oder DO UNTIL ('O'B); denn nach der PL/1-Sprachsyntax wird jede WHILE-Bedingung in eine Bitkette konvertiert und die Schleife so lange durchlaufen, wie irgendein Bit dieser Kette den Wert Eins hat [43, S. 91]. Dieselbe Wirkung hat die neue DO REPEAT-Anweisung. Sie ist ein Sonderfall der herkömmlichen DO-Anweisung, die Zählschleifen steuert. Der Zusatz REPEAT elementausdruck > ersetzt den Anweisungsteil TO < elementausdruck > BY elementausdruck [44, S. 43], Dabei beschreibt der Elementausdruck, in welcher Weise die Schleifenvariable verändert werden soll, ohne aber für sie eine obere Grenze anzugeben. Deshalb wird eine solche spezielle Zählschleife, wie auch immer der REPEAT-Zusatz lauten mag, ohne Ende durchlaufen. Beispiel:
DO K = 0 REPEAT K + 1;
Natürlich muß in der Praxis die Anzahl der Schleifendurchläufe begrenzt werden. Das kann der Programmierer in der Weise erreichen, daß er in der Schleife zusätzlich eine DO WHILE- oder DO UNTIL-Anweisung notiert oder mit der LEA VE-Anweisung die Schleife verläßt. Beispiel:
IF K = 10 THEN LEAVE;
Sobald die IF-Abfrage positiv beantwortet wird, führt der LEA VE-Teil zum Schleifenabbruch. Ein typisches Beispiel für die Notwendigkeit, Programmschleifen an jeder beliebigen Stelle verlassen zu können, sind Sortierprozesse. Wir haben in [44, S. 96 f.] ein Sortierprogramm für Listen entwickelt, das rekursiv abläuft. Dieses Programm arbeitet in der Weise, daß der Sortieralgorithmus eine Liste vom Listenkopf her durchsucht und prüft, ob die Datensätze mit ihren Sortiermerkmalen in der richtigen Reihenfolge gespeichert sind. Immer dann, wenn diese Bedingung nicht erfüllt ist, vertauscht er an der Stelle, an der sich die Sortierfolge än-
33
4.1 Strukturierte Programmierung
dert, die Zeiger in den beiden benachbarten Datensätzen. Anschließend ruft das Sortierprogramm sich selbst rekursiv auf und beginnt damit den Suchprozeß von vorn. Um dieses Sortierverfahren zu beschleunigen, soll jetzt der Sortieralgorithmus nach dem Vertauschen von zwei Zeigern nicht mehr abbrechen, sondern die Liste bis zum Listenende in derselben Weise abarbeiten, indem jeweils zwei in falscher Sortierfolge liegende Datensätze durch Vertauschung ihrer Zeiger ihren Platz in der Liste tauschen. In der strukturierten Programmierung dieses Problems bilden wir zunächst eine Sortierschleife, die so lange durchlaufen wird, bis das Listenende erreicht ist (Erkennung durch Abfrage mit der Funktion „NULL"). In diese Schleife wird eine zweite DO WHILE-Schleife eingebaut, die prüft, ob die Sortierbegriffe in richtiger Reihenfolge geordnet sind. Ist diese zweite WHILE-Bedingung nicht erfüllt, dann muß die Programmausführung diese innere Schleife verlassen und die paarweise Zeigervertauschung durchführen. Anschließend arbeitet der Sortierprozeß an der Stelle in der gespeicherten Liste weiter, an der er die zweite DO WHILE-Schleife verlassen hat. Diese beiden Schleifen werden in eine Endlosschleife eingebettet. Diese dritte Schleife ist dann mit einer LEA VE-Anweisung zu verlassen, so bald der Sortieralgorithmus die Liste vom Listenkopf bis zum Listenende vollständig durchsucht hat, ohne eine Vertauschung vorgenommen zu haben. Außerdem m u ß die Programmausführung die Sortierschleife auch dann verlassen, wenn sie das Listenende erreicht hat. Damit läßt sich ein Programm „LISTENSORTIEREN" mit zwei LEAVE-Anweisungen schreiben. Der „SCHALTER" in diesem Programm gibt an, ob beim Durchlaufen der innersten Schleife eine Vertauschung von Zeigern vorgenommen wurde. LISTENSORTIEREN:
PROC(ZEIGER); DCL /*
(P,Q,R, HILFSZEIGER FUER SORTIERPROZESS ZEIGER) 1 SATZ
POINTER,
BASED(ZEIGER_1),
2 WORT
CHAR(10),
2 ZEIGER_2
POINTER,
SCHALTER B I T ( 1 ) /*
ZUR PRUEFUNG OB SORTIERPROZESS ABGESCHLOSSEN
ENDLOSSCHLEIFE:
INITCO'B)
*/;
DO K = 0 REPEAT K = ZEIGER_1,
Q =
1; ZEIGER;
R = Q -> SCHALTER =
'O'B;
ZEIGER_2;
*/
34 SORTIERSCHLEIFE:
4. Kritik der herkömmlichen Programmierung DO WHILE /*
(R -,= N U L L O ) ;
AUFSTEIGENDE SORTIERFOLGE
DO WHILE
*/
(R - > WORT > Q - > WORT); P -
Qf
Q -
R;
R •
R ->
ZEIGER_2;
I F SCHALTER • R = NULLO
'O'B &
THEN
LEAVE ENDLOSSCHLEIFE; IF
R = NULLO THEN
LEAVE SORTIERSCHLEIFE; END; SCHALTER =
"T B;
IF
Q = ZEIGER THEN ZEIGER =
/*
ERSTES LISTATOM
R;
*/ ELSE
P ->
ZEIGER_2 = R;
Q ->
ZEIGER_2 = R - >
R ->
ZEIGER_2 = Q;
ZEIGER_2;
P = Q; Q =
R;
R = R ->
ZEIGER_2;
END SORTIERSCHLEIFE; END ENDLOSSCHLEIFE; END LISTENSORTIEREN;
Bisher war es in der problemorientierten Programmiersprache PL/1 notwendig, die CASE-Konstruktion entweder mit verschachtelten IF-Anweisungen oder mit einer GOTO-Anweisung zusammen mit Markenvariablen zu programmieren. Die SELECT-Anweisung, die wie eine DO-Gruppe arbeitet, stellt von der Notation her gesehen eine elegantere Lösung des CASE-Problems dar. Die Funktion dieser
35
4.1 Strukturierte Programmierung
Anweisung soll mit einem kleinen Beispiel belegt werden. In Programmen der betrieblichen Datenverarbeitung ist es häufig notwendig, die Vorzeichen von Salden abzufragen, um je nachdem ob das Ergebnis positiv oder negativ ist, ein anderes Druckbild anzusprechen. In den folgenden beiden Programmausschnitten wird eine solche Aufgabe einmal mit der SELECT-Anweisung und ein zweites Mal mit verschachtelten IF-Anweisungen gelöst (s. z. B. Programm „MAGNETB AND_D ATEIVERARBEITUNG _KLASSISCH"). SELECT; WHEN (SIGN(STAMMS ATZ .SALDO) = 1) ANZEIGE SALDO = 'H' WHEN (SIGN(STAMMS ATZ .SALDO) = - 1 ) ANZEIGE SALDO = 'S' ANZEIGE SALDO = ' ' OTHERWISE END, IF-Lösung: IF STAMMSATZ.SALDO > 0 THEN ELSE IF STAMMSATZ.SALDO < 0 THEN ELSE
ANZEIGESALDO = 'H'; ANZEIGESALDO = 'S'; ANZEIGE SALDO = '
';
Bei einer 3-Wege-Verzweigung kommt der Vorteil der SELECT-Anweisung noch nicht voll zum Tragen; denn die Schachtelungstiefe der IF-Anweisungen kann der Programmierer noch relativ leicht überblicken. Damit stehen für die strukturierte Programmierung in PL/1 folgende Bauelemente zur Verfügung: (1)
Strukturblock: Sonderform: BEGIN;
(2)
Reihung von Strukturblöcken:
I
END;
36 (3)
4. Kritik der herkömmlichen Programmierung IF
THENELSE-Anweisung: Sonderform: IF THEN-Anweisung
(4)
DO WHILESchleife
(Abfrage am Schleifenbeginn):
(5)
DO UNTILSchleife
(Abfrage am Schleifenende):
3
Der Kreis als Symbol für die Zusammenführung wird hier aus Gründen der besseren Übersichtlichkeit entgegen den genormten Symbolen für Programmablaufpläne [15] benutzt.
4.1 Strukturierte Programmierung (6)
CASE-Konstruktion
(7)
Endlos-Schleife
37
(SELECT-AnWeisung):
mit Unterbrechung (LEAVE-Anweisung):
Mit Hilfe dieses Repertoires an Symbolen kann man versuchen, nichtstrukturierte Programmablaufpläne graphisch nach den Regeln der strukturierten Programmierung umzugestalten. Beispiel:
Es sei folgender Programmablaufplan gegeben.
38
4. Kritik der herkömmlichen Programmierung
In diesem Programmablauf sind vier GOTO-Anweisungen zu erkennen. So verzweigt er am ELSE-Ausgang der zweiten IF-Anweisung (Abfrage auf „Y") mit einem GOTO zurück zum Eingang der ersten IF-Anweisung. Eine zweite GOTOAnweisung stellt die Verbindung zwischen dem Programmteil „C" und dem Eingang der bedingten Anweisung „Z" her. Dasselbe gilt für den Weg „B — D", der auch mit einer GOTO-Anweisung nach „ Z " verzweigt. Schließlich geht die Programmausfuhrung, nachdem „E" durchlaufen wurde, wiederum mit einem bedingungslosen Sprung zurück zum Eingang von „D". Damit keine endlosen Programmschleifen entstehen, müssen wir noch unterstellen, daß der Programmteil „A" die Variable X, „D" oder „E" die Variable Z verändert. Um schon jetzt Eigenschaften der GOTO-losen Programmierung zu erkennen, wollen wir versuchen, diesen Programmablaufplan in der Weise umzustrukturieren, daß die vier GOTO-Anweisungen vermieden werden. Um dieses Ziel zu erreichen, ist es notwendig, die Steuerung dieses Programmablaufs allein mit IF THEN ELSE-AnWeisungen und Schleifen zu entwerfen.
4.1 Strukturierte Programmierung
39
Der umstrukturierte Programmablauf beginnt mit der Abfrage, ob X den Wert 'O'B o d e r ' 1 'B hat. Ein Rücksprung auf diese IF-Anweisung ist jetzt aber nicht mehr zugelassen. Es muß sich deshalb im Programmablauf an den Programmteil A eine DO WHILE-Schleife anschließen, in die X eingeht und die solange durchlaufen wird, wie X und Y den Wert 'O'B haben. Tritt der Programmablauf nicht in diese Schleife ein oder verläßt er sie, dann ist zu prüfen, welche der beiden Schleifenbedingungen nicht erfüllt war. Diese Prüfung führt die nächste IF-Anweisung aus. Ihr ELSE-Zweig wird dann durchlaufen, wenn X in A den Wert ' l ' B angenommen hat, und der THEN-Zweig im Falle von Y = ' l ' B . Der Pro-
4. Kritik der herkömmlichen Programmierung
40
grammablauf endet mit einer DO WHILE-Schleife, die von der Variablen Z gesteuert wird. Auch dieser modifizierte Programmablaufplan bestätigt wieder die Beobachtung, daß strukturierte Programme länger sein können als unstrukturierte. Um GOTOAnweisungen zu vermeiden, war es notwendig, die Programmteile A und B zu duplizieren und D sogar dreifach auszulegen. In der Fachliteratur wird diese Methode, GOTO-Anweisungen zu umgehen, Auftrennung von Knoten („nodesplitting") genannt [27, S. 23 ff.]. Sie geht vom Ansatz aus, einen Programmablaufplan auf einen gerichteten Graphen abzubilden und dabei GOTOs durch Schleifen zu ersetzen. Man kann diese Programmverlängerung vermeiden, wenn A, B und D als Unterprogramme ausgelegt werden. Aus diesem kleinen Beispiel schließen wir, daß Unterprogramme in der strukturierten Programmierung ein wichtiges Konstruktionselement sind, um die Konstruktionsregeln in der Praxis besser einhalten zu können. An dieser Stelle m u ß aber noch einmal davor gewarnt werden, wohlstrukturierte Programme aus konventionellen Programmablaufplänen ableiten zu wollen.
4.2 Ein erster Versuch, die Fallstudie ohne GOTO-Anweisungen zu programmieren Es sollen nun in einem ersten Versuch die im Kapitel 4.1 dargestellten Bauelemente der strukturierten Programmierung in der Fallstudie des Kapitels 3. eingesetzt werden, ohne daß wir weitere methodische Hilfen geben. Hierbei wird die Frage untersucht, welche Eigenschaften eine Lösung hat, die GOTO-Anweisungen strikt vermeidet. Dabei müssen wir uns mit der Behauptung von Knuth auseinandersetzen, daß die Einhaltung dieser Regel vor allem beim Auftreten von Ausnahmebedingungen, wie dem Dateiende, zu umständlichen Programmstrukturen führt [26, S. 261 ff.]. In der herkömmlichen Programmierung hat sich die Programmierregel eingebürgert, daß der Programmierer alle die Aktionen, die das Programm selbständig durchführen m u ß , wenn Ausnahmefälle auftreten, am Programmende zusammenfaßt, um mit GOTO-Anweisungen zu dieser Stelle verzweigen zu können (s. auch Abbildung 3). Dieser Technik kamen auch die Spracharchitekten von PL/1 entgegen, indem sie ein eigenes Sprachelement in Form der ON-Anweisung schufen, die Ausnahmebedingungen programmiert abfragt. So lautet eine typische ON-Anweisung in PL/1: ON ENDFILE (LK) GOTO ENDE; In diesem Beispiel verzweigt die Programmausführung beim Ende der Lochkartendatei „ L K " zur Marke " E N D E " , wo die Dateiendeaktionen zu finden sind. ONAnweisungen haben im Gegensatz zu den üblichen Verarbeitungsanweisungen
4.2 Ein erster Versuch
41
dynamische Nachfolger. Sobald eine ON-Bedingung, wie Division durch Null oder Überlauf eines Rechenfeldes, in einem Programm aufgetreten ist, aktiviert sie die dazugehörige ON-Anweisung. Diese Anweisung bleibt so lange aktiv, bis das Blockende erreicht ist. Darüber hinaus gilt sie auch noch für alle die Blöcke, die der Block a u f r u f t , in dem eine ON-Bedingung ansprach. ON-Anweisungen notiert deshalb der Programmierer in der Regel am Beginn eines Blocks, um alle Ausnahmebedingungen in diesem Block abfangen zu können. Wir versuchen, dieses grundlegende Konzept von ON-Bedingungen beizubehalten, es aber an die Forderungen der strukturierten Programmierung anzupassen. Das bedeutet, daß ON-Anweisungen nach wie vor am Blockanfang notiert werden. Sie setzen eine binäre Variable, die später entweder mit IF-Anweisungen abgefragt wird oder die DO WHILE-Schleifen steuert. In unserer Fallstudie kommen als Ausnahmebedingungen zunächst nur das Ende der Bewegungsdatei und das Ende der Stammdatei zum Tragen. Für den Entwurf der Programmsteuerung gehen wir von folgenden Überlegungen aus. Wenn das Programm alle Sätze der Bewegungsdatei verarbeitet hat, ist die Dateiverarbeitung abgeschlossen. Es ist deshalb naheliegend, alle Arbeitsprozesse, die zum Verarbeiten der Bewegungsdatei und der Stammdatei gehören, in eine DO WHILE-Schleife zu legen, die von der Endfile-Bedingung der Bewegungsdatei gesteuert wird. Die weitere Modularisierung der Dateiverarbeitung basiert auf dem Vergleich der Ordnungskriterien der Bewegungs- und Stammdatei, woraus drei Hauptmodule resultieren, nämlich „ B E W E G T E S T A M M S A E T Z E " (Ordnungsmerkmal Bewegungsdatei = Ordnungsmerkmal Stammdatei), „NICHTBEWEGTE STAMM SAETZE UND GRUPPENWECHSEL" (Ordnungsmerkmal Bewegungsdatei > Ordnungsmerkmal Stammdatei), „BEWEGUNGEN OHNE STAMMSAETZE" (Ordnungsmerkmal Bewegungsdatei < Ordnungsmerkmal Stammdatei). Um den Gruppenwechselprozeß von der Teilfunktion „nichtbewegte Stammsätze", die beide in derselben Prozedur abgewickelt werden, unterscheiden zu können, wird wie in der klassischen Lösung ein binärer Programmschalter „SCHALTER" eingeführt (s. Abbildung 3). Die weitere Unterteilung der drei Hauptmodule in Submodule erfolgt mehr oder weniger intuitiv. Die Druckaufbereitung der Stammdaten, die im linken Teil einer Zeile ausgegeben werden (s. Abbildung 2), führt die Prozedur „DRUCKAUFBEREITUNG_ LINKS" durch. Die Daten selbst (Kontonummer und alter Saldo) zusammen mit dem ersten Bewegungssatz druckt der Modul „DRUCKAUFBEREITUNG_ RECHTS UND DRUCKEN" aus. Für das Verarbeiten der Bewegungsdaten bilden wir eine eigene Prozedur „VERARBEITEN BEWEGUNGSDATEN", die wieder eine DO WHILE-Schleife steuert. Steuerungsvariable sind das Ergebnis des Vergleichs der Kontonummer des Bewegungssatzes mit der Kontonummer des Stammsatzes und wiederum die Endfile-Bedingung der Bewegungsdatei. Beide sind konjunktiv verbunden. In analoger Weise führen wir die Prozedur ,,BEWE GUNGEN OHNE STAMMSAETZE" aus mit dem Unterschied, daß die Verar-
42
4. Kritik der herkömmlichen Programmierung
beitung im Hauptspeicher in einem eigenen Speicherbereich erfolgt ( , , P U F F E R _ S T A M M S A T Z " ) . Schließlich liegt auch die Steuerung der Prozedur „ N I C H T B E W E G T E S T A M M S A E T Z E U N D G R U P P E N W E C H S E L " in einer DO WHILESchleife mit den beiden Schleifenvariablen Endfile-Bedingung der Stammdatei und dem Vergleich, ob das Ordnungsmerkmal der Bewegungsdatei größer ist als das Ordnungsmerkmal der Stammdatei. Mit Hilfe dieser zusätzlichen verbalen Erklärungen wird nun die Lösung „ M A G N E T B A N D D A T E I V E R A R B E I T U N G S T R U K T U R I E R T _ V E R S I O N _ 1 " verständlich.
MAGNETBAND_DATEIVERARBEITUNG_STRUKTURIERT_VERSION_1: PROC OPTIONS
(MAIN)
/* DATEIVEREINBARUNGEN, VEREINBARUNG LOCHKARTENSATZ, STAMMSATZ, UEBERSCHRIFT, ZEILE,ZEILE_1 IDENTISCH MIT PROGRAMM MAGNETBAND_DATEIVERARBEITUNG_KLAS SISCH * / / * VEREINBARUNG SCHALTER */ DCL (SCHALTER /* GRUPPENWECHSEL
*/,
SCHALTER_1 /* DATEIENDE */) DCL
BIT(1)
INITCO'B);
(ENDFILE_BEDINGUNG_MAGNETBAND_EINGABE, ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE)
BIT (1) I N I T C O ' B ) ; /******************************************************* OPEN FILE
(LEINGAB),
FILE
(MEINGAB),
FILE
(MAUSGAB),
FILE
(LISTE);
/* DRUCKEN UEBERSCHRIFTZEILE WRITE FILE
(LISTE)
FROM
(UEBERSCHRIFT);
/* DRUCKEN 1 LEERZEILE ZEILE_1 =
*/
'0';
WRITE FILE
(LISTE)
ON ENDFILE
(LEINGAB)
FROM
*/
(ZEILE);
4.2 Ein erster Versuch
43
ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE = '1'B; ON ENDFILE
(MEINGAB)
ENDFILE_BEDINGUNG_MAGNETBAND_EINGABE READ FILE
(LEINGAB) INTO
(LOCHKARTENSATZ);
READ FILE
(MEINGAB) INTO
(STAMMSATZ);
= '1'B;
/*******************************************************/ DATEIVERARBEITUNG: DO WHILE (ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE = 'O'B); IF KONTONUMMER_BEWEGUNG « STAMMSATZ.KONTONUMMER THEN CALL BEWEGTE_STAMMSAETZE; IF KONTONUMMER_BEWEGUNG > STAMMSATZ.KONTONUMMER THEN CALL NICHTBEWEGTE_STAMMSAETZE_UND_GRUPPENWECHSEL; IF KONTONUi*MER_BEWEGUNG < STAMMSATZ . KONTONUMMER THEN CALL BEWEGUNGEN_OHNE_STAMMSAETZ E; END DATEIVERARBEITUNG;
BEWEGTE_STAMMSAETZE: PROC; CALL DRUCKAUFBEREITUNG_LINKS; CALL VE RARBEITEN_BEWEGUNGSDATEN; END BEWEGTE_STAMMSAETZE; VERARBEITEN_BEWEGUNGSDATEN: PROC; DO WHILE
(KONTONUMMER_BEWEGUNG • STAMMSATZ. KONTONUMMER &
ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE = 'O'B); STAMMSATZ.SATZART = KARTENART; STAMMSATZ.DATUM
= DATUM_BEWEGUNG;
STAMMSATZ.SALDO
= STAMMSATZ.SALDO + BETRAG_BEWEGUNG;
CALL DRUCKAUFBEREITUNG RECHTS UND DRUCKEN;
44
4. Kritik der herkömmlichen Programmierung SCHALTER
=
READ F I L E
'1'B;
(LEINGAB)
INTO
(LOCHKARTENSATZ);
END BEWEGUNGSSCHLEIFE; I F ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE
«= ' 1 ' B
THEN CALL FINALISIERUNG; END VERARBEITEN_BEWEGUNGSDATEN; NICHTBEWEGTE_STAMMSAETZE_UND_GRUPPENWECHSEL: DO WHILE
PROC RECURSIVE;
(KONTONUMMER_BEWEGUNG > STAMMSATZ. KONTONUMMER &
ENDFILE_BEDINGUNG_MAGNETBAND_EINGABE
=
*0'B);
I F SCHALTER THEN DO; /*
SCHALTER = ' 1 ' B GRUPPENWECHSEL, STAMMSAETZE
' O ' B NICHTBEWEGTE
*/
I F STAMMSATZ.SALDO > O THEN ANZEIGE_SOLL_HABEN_NEU =
'H';
ELSE I F STAMMSATZ.SALDO < 0 THEN ANZEIGE_SOLL_HABEN_NEU =
'S';
ELSE AN Z EIGE_SOLL_HABEN_NEU = '
';
ZEILE.NEUER_SALDO = STAMMSATZ.SALDO! SCHALTER =
'O'B;
END; ELSE DO; CALL DRUCKAUFBEREITUNG_LINKS; ZEILE.DATUM = STAMMSATZ.DATUM; END; WRITE F I L E
(LISTE)
FROM
(ZEILE);
/ * DRUCKEN SALDO UND NICHTBEWEGTE STAMMSAETZE WRITE F I L E
(MAUSGAB) FROM
(STAMMSATZ);
READ
(MEINGAB)
(STAMMSATZ);
FILE
END SCHLEIFE;
INTO
*/
45
4.2 Ein erster Versuch I F ENDFILE_BEDINGUNG_MAGNETBAND_EINGABE
=
'1'B
THEN DO; IF
SCHALTER_1 =
ELSE CALL
' 1 ' B THEN CALL ENDE;
BEWEGUNGEN_OHNE_STAMMSAETZE;
END; END NICHTBEWEGTE_STAMMSAETZE_UND_GRUPPENWECHSEL; /*************************** BEWEGUNGEN_OHNE_STAMMSAETZEs ZEILE_1
=
PROC; 1
'O ;
PUFFER_STAMMSATZ.SATZART
=
KARTENART;
ZEILE.KONTONUMMER, PUFFER_STAMMSATZ.KONTONUMMER
=
KONTONUMME R_BEWEGUNG; PUFFER_STAMMSATZ.SALDO
= 0 ;
ZEILE.ALTER_SALDO DO WHILE
=
0;
(KONTONUMMER_BEWEGUNG =
PUFFER_
STAMMSATZ.KONTONUMMER & E N D F I L E _ BEDINGUNG_BEWEGUNGSDATEN_EINGABE 1
=
O ' B) ;
PUFFER_STAMMSATZ.DATUM
= DATÜM_
PUFFER_STAMMSATZ.SALDO
=
BEWEGUNG; PUFFER_
STAMMSATZ.SALDO + BETRAG_BEWEGUNG; CALL
DRUCKAUFBEREITUNG_RECHTS_UND_ DRUCKEN;
READ F I L E
(LEINGAB)
INTO
(LOCHKARTEN SATZ);
END
SCHLEIFE; ZEILE.NEUER_SALDO =
PUFFER_STAMMSATZ. SALDO;
I F PUFFER_STAMMSATZ.SALDO
> 0 THEN
ANZEIGE_SOLL_HABEN_NEU = ELSE
'H';
46
4. Kritik der herkömmlichen Programmierung I F PUFFER_STAMMSATZ.SALDO < 0 THEN ANZEIGE_SOLL_HABEN_NEU =
'S';
ELSE ANZEIGE_SOLL_HABEN_NEU = ' WRITE F I L E
(LISTE)
FROM Z E I L E ; ZEILEJI
WRITE F I L E
';
(MAUSGAB) FROM
=
'
' ;
(PUFFER_ STAMMSATZ);
I F ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE
=
THEN CALL FINALISIERUNG; END BEWEGUNGEN_OHNE_STAMMSAETZE;
DRUCKAUFBEREITUNG_LINKS: ZEILE_1 =
PROC;
'O';
ZEILE.KONTONUMMER = STAMMSATZ.KONTONUMMER; ZEILE.ALTER_SALDO = STAMMSATZ.SALDO; I F STAMMSATZ.SALDO > O THEN ANZEIGE_SOLL_HABEN =
'H'; ELSE
I F STAMMSATZ.SALDO < O THEN ANZEIGE_SOLL_HABEN =
'S'; ELSE
ANZEIGE_SOLL_HABEN =
'
1
;
END DRUCKAUFBEREITUNG_LINKS; /***********************************************, DRUCKAUFBEREITUNG_RECHTS_UND_DRUCKEN: / * DRUCKEN BEWEGUNGSKARTEN
PROC;
*/
ZEILE.BELEG_NUMMER = BELEGNUMMER_BEWEGUNG; ZEILE.DATUM = DATUM_BEWEGUNG; I F BETRAGJ3EWEGUNG > O THEN ZEILE.HABEN = BETRAG_BEWEGUNG; ELSE
'1'B
47
4 . 2 Ein erster Versuch ZEILE.SOLL WRITE
END
FILE
=
BETRAG_BEWEGUNG;
(LISTE)
FROM
ZEILE_1
=
'
(ZEILE); ';
DRUCKAUFBEREITUNG_RECHTS_UND_DRUCKEN;
/ * * * * *
FINALISIERUNG: PROC; KONTONUMMER_BEWEGUNG
=
'999999';
SCHALTER_1
=
*1'B;
CALL
NICHTBEWEGTE_STAMMSAETZE_UND_GRUPPENWECHSEL;
END F I N A L I S I E R U N G ; /******************************************************/ ENDE:
PROC; END MAGNET BAN D _ D A T E I V E R A R B E I T U N G _ S T RUKTURIERT VERSION
1;
Tab. 1 Analyse der Endbedingungen in MAGNETBAND STRUKTURIERT VERSION 1 Kontonummer Bewegungss. Stamms. Dateiende in Prozedur 4711 4715
4711 4715 4716
4711 4715
4711 4715
4711 4715
4711 4715
Programmablauf bei Dateiende
BEWEGTE_STAMMSAETZE
FINALISIERUNG -» NICHTBEWEGTE_STAMMSAETZE - > 2 x DO WHILE Schleife ENDE
BEWEGTE
FINALISIERUNG NICHTBEWEGTE_STAMMSAETZE - > l x DO WHILE Schleife ENDE
STAMMSAETZE
NICHTBEWEGTE_ STAMMSAETZE
4711 4716
DATEIVERARBEITUNG
BEWEGUNGENOHNE STAMMSAETZE
BEWEGUNGEN_OHNE_STAMM SAETZE -»• FINALISIERUNG NICHTBEWEGTE_STAMMSAETZE (DO WHILE Schleife wird übergangen) -»• ENDE FINALISIERUNG -»• NICHTBEWEGTE_STAMMSAETZE ->1 x DO WHILE Schleife -»ENDE
Es stellt sich nun die Aufgabe, das Ergebnis dieses ersten Versuchs, die Dateiverarbeitung strukturiert zu programmieren, kritisch zu untersuchen. Dieses Programmierbeispiel scheint Knuth recht zu geben, wenn er behauptet, daß man Ausnahmebedingungen nach wie vor mit GOTO-Anweisungen programmieren soll; denn in der Tat ist die vorgelegte Lösung hinsichtlich des Programmablaufs beim Er-
48
4. Kritik der herkömmlichen Programmierung
reichen einer Dateiendebedingung unübersichtlich. Diese Beobachtung soll mit Tabelle 1 belegt werden. Wir haben dort an Hand von willkürlich gewählten Beispielen für die beiden Ordnungsmerkmale Kontonummer des Bewegungssatzes und Kontonummer des Stammsatzes gezeigt, wie sich das Dateiende (durch Unterstreichung markiert) auf den Programmablauf auswirkt. Die Dateiendearbeiten wurden analog zur klassischen Lösung programmiert. Falls die Bewegungsdatei ihr Ende erreicht hat, wird ihre Kontonummer wieder vom Programm her auf '999999' gesetzt und der Rest der Stammdatei als nichtbewegte Stammsätze verarbeitet. Im Sinne der strukturierten Programmierung fassen wir diese beiden Operationen aber jetzt in einer eigenen Abschlußprozedur „FINALISIERUNG" zusammen, zu der die Programmausführung mit einer CALL-Anweisung verzweigt. Auch brauchen wir wieder einen binären Programmschalter „SCHALTER 1", u m das Faktum, daß die Bewegungsdatei ihr Ende erreicht hat, speichern zu können. Die Prozedur „ N I C H T B E W E G T E S T A M M S A E T ZE UND GRUPPENWECHSEL" benötigt diese Information, damit sie beim Ende der Stammdatei weiß, ob noch Bewegungssätze vorliegen, die als Bewegungen ohne Stammsätze zu verarbeiten sind. Die in dieser Lösung erzielte Programmstruktur nennen wir „GOTO-ähnliche" Struktur; denn viele der CALL-Anweisungen könnte der Programmierer auch durch GOTO-Anweisungen ersetzen. Diese GOTO-ähnliche Struktur äußert sich auch darin, daß dieses Programm mit einer Nullprozedur „ E N D E " abschließt. Man erkennt, daß die Eliminierung der GOTO-Anweisungen allein noch nicht zu einer leichter lesbaren und benutzerfreundlichen Programmstruktur führt [27, S. 23 ff.], was am Dateiendeablauf besonders drastisch zutage tritt. Wir schließen aus diesem Programmierbeispiel, daß der Programmierer das Ziel, eine Aufgabe in einen übersichtlichen linearen Programmablauf umzusetzen, mit dem Instrumentarium der Software-Bauelementelehre allein nicht erreichen kann. Sie bietet ihm nur Programmbausteine an, ohne ihr Zusammenspiel in einem größeren System aufzuzeigen. Wir benötigen eine Software-Konstruktionslehre, die den Programmdesigner anleitet, eine Aufgabe systematisch so anzugreifen, daß das hierbei entstehende Softwareprodukt von selbst den Regeln der strukturierten Programmierung gehorcht. Wir kommen damit zu den Methoden des strukturierten Softwareentwurfs als der ingenieurmäßigen Entwicklung von Programmen. Man kann sie auch als strukturierte Programmierung im weiteren Sinn verstehen.
5. Strukturierter Softwareentwurf (Konstruktionslehre des Softwareentwurfs)
5.1 Zielsetzung Wenn ein Architekt ein Haus entwirft, dann wird er zunächst versuchen, die Vorstellungen des Bauherrn nach künstlerischen und technischen Gesichtspunkten in einen Grobentwurf des zu planenden Objektes umzusetzen. Er beginnt bei dieser Arbeit mit einer Außenansicht des Hauses und der Raumaufteilung im Innern aber nicht mit den Fenstern und den Türen. Oder kein Maschinenbauingenieur entwirft bei der Konstruktion eines Kraftfahrzeuges als erstes die Einspritzpumpe und die Radlager. Diese Beispiele zeigen, daß in den herkömmlichen Ingenieurwissenschaften seit langem ein Arbeitsstil anerkannt wird, der die Komplexität eines Systems in der Weise reduziert, daß es der „Designer", ausgehend von der Aufgabenstellung, in Teilsysteme und Untersysteme von Teilsystemen zerlegt. Diese Arbeitsmethode, die eine Aufgabenstellung schrittweise von oben nach unten — top down — verfeinert und dabei auf Module im Sinne von Teilaufgaben abbildet, haben die Programmierer lange Zeit hindurch ignoriert. Sie schufen größere Programmsysteme bottom up. Ausgehend vom Anweisungsvorrat einer problemorientierten oder maschinenorientierten Programmiersprache wurden einzelne Programmkomponenten von verschiedenen Programmierteams oder auch nur von einzelnen Programmierern geplant, programmiert und getestet, um am Projektende zu einem einzigen Programmprodukt integriert zu werden. Es liegt nahe, daß bei einem solchen Programmierstil wegen unklarer Schnittstellen Anpassungsprobleme zwischen den einzelnen Programmodulen auftraten. Man hat sie wohl oder übel akzeptiert. Sie wurden entweder durch zusätzliche Programmkomponenten oder auch nur durch Modifikationen an bestehenden Programmmodulen gelöst. Von einem solchen „zusammengebastelten" Entwurf kann man nicht erwarten, daß sein Ergebnis optimal ist. Das ist der Grund dafür, daß in der Praxis viele Programmprodukte existieren, deren Struktur unsystematisch definiert wurde und die überflüssige Teile enthalten, die von früheren Verwendungen herrühren. Es traut sich aber kein Programmierer, sie zu entfernen; denn er überblickt infolge der Bottom-up-Struktur nicht, wie sich eine solche Änderung auf das gesamte Programmsystem auswirkt. Die Qualitätsmerkmale Benutzerfreundlichkeit, Änderungsfreundlichkeit und leichte Wartbarkeit werden nicht erfüllt. Da aber auch die Schnittstellen zwischen den einzelnen Programmkomponenten häufig nicht klar definiert und dokumentiert sind, liegt die Zuverlässigkeit und die Fehlerfreiheit im argen. Der strukturierte Softwareentwurf verfolgt das Ziel, zwischen der Entwurfsphase eines Softwareproduktes und der Programmierung zu differenzieren, um zu-
50
5. Strukturierter Softwareentwurf
nächst unabhängig von einer Programmiersprache die „Architektur" eines Programmes planen zu können. Im Grunde genommen wird seit eh und je in der Fachliteratur gefordert, eine Aufgabenstellung nicht unmittelbar in ein Programm umzusetzen, sondern eine Planungsphase, Systemanalyse genannt, einzuschalten. In der Praxis haben sich aber häufig Programmierer nicht an diese Regel gehalten. Insbesondere wenn eine leistungsfähige, problemorientierte Programmiersprache wie COBOL oder PL/1 zur Verfügung steht, ist der einzelne Programmierer verleitet, sofern ihn nicht die Organisation eines Programmierteams zu einem anderen Programmierstil zwingt, direkt aus der Aufgabenstellung heraus einen Programmnukleus zu programmieren und diesen dann von „innen nach außen" zu erweitern. Um noch einmal zum Analogieschluß der Arbeit des Architekten zurückzukehren, auch er beginnt seine Arbeit nicht mit dem „Polierplan", nach dem ein Haus gebaut wird, sondern bei einem „Einreichplan", der grob ein Haus darstellt, um eine Baugenehmigung zu erhalten. Die Grundidee des strukturierten Programmentwurfs, die auf Dijkstra und Wirth [11,51 S. 221 ff.] zurückgeht, postuliert, daß die Entwurfsphase bei der Analyse der zu lösenden Aufgabe zu beginnen hat und von ihrer Implementierung und damit dem Arbeitsmittel, das diese Aufgabe später einmal übernimmt, abstrahieren soll. Ausgehend von der Aufgabenstellung wird eine Aufgabe sukzessive top down solange in Teilaufgaben aufgelöst, bis Funktionen vorliegen, die leicht in ein Programm umzusetzen sind. Daraus resultiert eine baumartige Aufgabenstruktur (s. Abbildung 5), für die in der Fachliteratur synonym auch die Bezeichnungen „schrittweise Verfeinerung" und „hierarchische Programmstruktur" zu
Abb. 5 Prinzip des strukturierten Entwurfs.
5.1 Zielsetzung
51
finden sind [51, S. 221 ff.]. Die Aufgabenstellung ist dabei der Wurzelknoten und die einzelnen Teilaufgaben sind Blätter an diesem Strukturbaum. Ein Softwaresystem wird in dieser Weise auf mehrere Programmebenen abgebildet, auch Abstraktionsstufen genannt, die nach oben hin größere Aufgabennähe und nach unten Realisierung bedeuten (Methode der Abstraktionsstufen). Eine solche Programmodularisierung bietet eine Reihe von Vorteilen: (1)
Die Produktivität der Programmierung wird verbessert, weil die Programmierer an weitgehend unabhängigen Teilaufgaben arbeiten können, so daß die Fehlersuche einfacher ist.
(2)
Unabhängige Module sind nicht so fehleranfällig wie eng miteinander verkoppelte Programmteile oder nichtmodularisierte Programme.
(3)
Wohlstrukturierte Programmprodukte sind leichter wartbar-, denn Programmänderungen, ausgelöst durch Funktionsänderungen, betreffen nur noch wenige Module und sind nicht mehr über ein ganzes Programmsystem verstreut.
(4)
Die Projektleitung kann den Arbeitsfortschritt leichter verfolgen. Er ist durch die Anzahl der fertiggestellten Module gegeben.
(5)
Das Programmtesten kann bereits beginnen, sobald alle Module einer Abstraktionsstufe fertiggestellt sind. Dadurch verringert sich die Durchlaufzeit eines Projektes.
(6)
Die Entwicklungskosten thode verwendet wird.
sinken, weil eine einheitliche Konstruktionsme-
Zwischen einer abstrakten funktionellen Betrachtungsweise informationeller Systeme und einer konstruktiv bedingten, konkreten zu unterscheiden, ist heute gängige Lehrmeinung der Informatik (s. z. B. [13, S. 19]). Aber auch die betriebswirtschaftliche Organisationslehre geht in gleicher Weise vor. Ausgangspunkt jeder organisatorischen Tätigkeit ist dort die Betrieb saufgabe. Sie wurde durch eine unternehmerische Grundsatzentscheidung gesetzt und kann deshalb als gegeben vorausgesetzt werden. Infolge ihrer Komplexität ist sie wiederum nur nach Auflösung in Teilaufgaben zu bewältigen, wie Vertrieb, Produktion, Beschaffung und Entwicklung. Organisation, als gestaltende Tätigkeit verstanden, beginnt deshalb bei der Analyse derjenigen Teilaufgaben, die sich induktiv aus der Betrieb saufgabe ableiten lassen und die zu organisieren sind. Dabei abstrahiert der Organisator zunächst von der Übertragung betrieblicher Teilaufgaben auf Menschen und Maschinen. Für diese Aufgabenanalyse benutzt er ein Merkmalschema, das zur Bestimmung der Teilaufgaben herangezogen wird und das aus fünf Elementen besteht [29, S. 61]. Es sind das der Verrichtungsvorgang (Arbeitsprozeß), der zu einer Aufgabenerfiillung notwendig ist, das Objekt, auf das sich eine Teilaufgabe bezieht, die Sach- und Arbeitsmittel, die in die Auf-
52
5. Strukturierter Softwareentwurf
gabenerfüllung eingebracht werden, sowie die beiden Kategorien Raum und Zeit, in die sich jede betriebliche Teilaufgabe einordnet. Kramer hat versucht, das von Kosiol angegebene Merkmalschema auf informationelle Aufgaben auszudehnen, indem er eine Merkmalmenge definiert, die aus den Elementen Kommunikationsverrichtung, Kommunikationsobjekt, Kommunikationshilfsmittel, Kommunikationszeit und Kommunikationsraum besteht [30]. Die theoretischen Untersuchungen über die Prinzipien des Softwareentwurfs gehen allerdings von anderen Kategorien aus, als sie Kosiol und Kramer benutzen. Sie lassen sich in zwei Hauptklassen einteilen, nämlich in aufgabenorientierte 1 und prozeßorientierte Verfahren. So hat Parnas vorgeschlagen, informationelle Arbeitsprozesse, wie Eingabe, Ausgabe oder Zugriff zu einer Datei, zur Aufgabendekomposition heranzuziehen [36, S. 1053 ff.], Teilaufgaben also auf die Betriebsmittel hin auszurichten, die das Programmsystem zur Aufgabenerfüllung benötigt. Hingegen definiert Wirth Aufgaben ohne Berücksichtigung der Aufgabenträger [51, S. 221 ff.]. Hierbei entsteht eine Programmodularisierung, die ein direktes Abbild der Aufgabenmodularisierung ist, so daß man direkt jeder Teilaufgabe einen Programmodul zuordnen kann (s. Abb. 5). Die prozeßorientierte Variante hat den Vorteil, daß sie änderungsfreundlicher ist und Änderungen weniger Module betreffen. Wenn sich beispielsweise die Form der Dateneingabe verändert, dann ist bei einer prozeßorientierten Dekomposition von dieser Maßnahme nur der Eingabemodul betroffen, hingegen bei Wirth mehrere Module, nämlich alle die, in denen Daten eingegeben werden. Allerdings haben wir die Erfahrung gemacht, daß die Frage der Änderbarkeit nicht nur vom Programmentwurf, sondern vor allem auch von der Güte der Programmdokumentation abhängt. Als Nachteil ist bei prozeßorientierten Entwurfsverfahren ein starker Datenaustausch zwischen den einzelnen Modulen zu beobachten. So muß ein Eingabemodul mit allen Modulen kommunizieren, die Eingabedaten benötigen. Dabei wirken sich aber Schnittstellenprobleme stärker aus als bei einem aufgabenorientierten Design. In der Praxis hat sich deshalb der prozeßorientierte Ansatz in der Anwendungsprogrammierung bisher nicht durchsetzen können. Ausschlaggebend für die Wahl eines Entwurfsverfahrens sollte das Kriterium sein, inwieweit der Programmdesigner mit diesem Verfahren das Prinzip der Topdown-Dekomposition streng einhalten kann. Verwendet er eine prozeßorientierte Entwurfsmethode, dann besteht die Gefahr, daß er der Versuchung nachgibt, möglichst viele schon vorhandene Prozeßmodule aus anderen Programmen und damit auch aus ganz anderen Aufgabenstellungen fiir einen neuen Entwurf unverändert oder nach geringfügiger Modifikation zu übernehmen. Uns scheinen deshalb auch von der Theorie her gesehen aufgabenorientierte Entwurfsverfahren 1
Genauer gesagt handelt es sich um ablauforganisatorische Aufgaben.
5.1 Zielsetzung
53
der Prämisse des Top-down-Entwurfs adäquater zu sein als der prozeßorientierte Ansatz. Auf jeden Fall muß aber die alte Methode aufgegeben werden, für die Programmodularisierung die herkömmliche Organisation eines Programmierprojektes, die sich mehr oder weniger zufällig aus der vorhandenen Manpower und ihrer Qualifikation ergab, zugrunde zu legen. Softwareentwurf soll zu einer rein organisatorischen Tätigkeit werden, wofür allerdings Entwurfsmittel, die bisher noch wenig entwickelt sind, zur Verfügung gestellt werden müssen. Folgende Fragen sind mit einem Softwareentwurf inhärent verbunden: (1)
Was ist die Aufgabel Aufgaben werden in der Regel durch Spezifikationen beschrieben. Dabei erhebt sich sofort die Frage nach der Vollständigkeit und der Verträglichkeit von Spezifikationen. Jeder Programmierer, der schon für einen Auftraggeber gearbeitet hat, mußte die Erfahrung machen, daß die ihm übergebenen Spezifikationen unvollständig waren, was sich leider häufig erst am Endprodukt erkennen läßt.
(2)
Auch wenn man versucht, nach dem Merkmal der Aufgabenorientiertheit ein Programmprodukt zu entwerfen, ist damit noch nicht sehr viel über die Detailkriterien gesagt, nach denen die einzelnen Programmodule zu definieren sind.
(3)
Wenn der Programmentwurf systematisch betrieben wird, entsteht die Frage, wie kann der Programmdesigner das Ergebnis seiner Tätigkeit auf logische Richtigkeit hin überprüfen? Wenn wir davon ausgehen, daß die Frage der Korrektheit eines Programms bisher theoretisch nicht zu beantworten ist, dann bleibt zunächst nur die Überprüfung in einem Test übrig.
(4)
Welche Darstellungsmöglichkeiten zu dokumentieren?
bestehen, um einen Programmentwurf
Im nächsten Kapitel sollen nun nach den dargelegten Kriterien die derzeit bekannten und angewendeten Verfahren des strukturierten Softwareentwurfs dargestellt und analysiert werden.
5.2 Methoden des Top-down Entwurfs 5.2.1 Die HIPO-Technik Die Ideen der strukturierten Programmierung und des strukturierten Entwurfs wurden zum ersten Mal von der IBM Anfang der 70er Jahre in einem größeren Programmierprojekt bei der New York Times eingesetzt, das die „Leichenhalle" dieser Zeitung automatisieren sollte. Darunter wird dort ein Dokument-Retrieval System verstanden, in dem alle Artikel, die jemals in dieser Zeitung erschienen sind, aufbewahrt werden und den einzelnen Redakteuren zur Verfügung stehen.
54
5. Strukturierter Softwareentwurf
Das Softwaresystem besteht aus 83 000 PL/1-Anweisungen und wurde in einer Zeit von 22 Monaten mit einem Arbeitsaufwand nur 11 Mann-Jahren entwickelt und programmiert. Die Bedeutung der strukturierten Programmierung und des strukturierten Entwurfs läßt sich daran erkennen, daß der erste Fehler in diesem System 13 Monate nach Freigabe auftrat [21, S. 14]. Durch diesen Erfolg ermutigt, gab die Firma IBM das in diesem Projekt verwendete Entwurfs- und Dokumentationsverfahren auch außerhalb ihres eigenen Hauses frei. Es trägt die Bezeichnung HIPO als Abkürzung für Hierarchy Input Process Output. Wie dieser Name andeutet, handelt es sich hierbei um ein Verfahren zur hierarchischen Strukturierung informationeller Arbeitsprozesse [23]. In einer ersten Entwurfsphase, dem Grobentwurf, leitet der Programmdesigner, wie im Abschnitt 5.1 dargestellt, top down aus der vorgegebenen Aufgabe Teilaufgaben und Unteraufgaben zusammen mit ihren hierarchischen Beziehungen ab. Diesen Entwurf hält er in einem informellen Organisationsschaubild fest. Anschließend definiert er in einer zweiten Entwurfsphase, dem Feinentwurf, für jede Teilaufgabe die Menge der Arbeitsprozesse, die notwendig sind, um diese Teilaufgabe zu lösen. Dabei werden auch für jeden Prozeß die Eingabe- und Ausgabedaten festgelegt. Hinter dieser Entwurfstechnik verbirgt sich der mathematische Ansatz, daß jeder informationelle Arbeitsprozeß und damit auch jede Strecke bzw. jeder Strukturblock eines Programms als Funktion im mathematischen Sinn interpretiert werden kann: 0 = P(I) P bildet die Menge I der Eingangszustände eines Prozesses (Eingabedaten) eindeutig auf die Menge der Ausgangszustände 0 (Ausgabedaten) ab [33, S. 15]. Die Ausgabedaten werden also durch einen Arbeitsprozeß P aus den Eingabedaten abgeleitet. In der HIPO-Technik soll der Programmdesigner im Softwareentwurfsprozeß gedanklich diese Funktionalisierung nachvollziehen, indem er als ersten Schritt im Feinentwurf die geforderten Ausgabedaten 0 festlegt und anschließend angibt, in welcher Weise eine Menge von Eingabedaten I die Ausgabedaten erzeugt. Damit der Programmdesigner konsequent diese Entwurfsstrategie einhält, wird ihm ein formales Entwurfsschema in die Hand gegeben, das aus drei Teilen besteht, einer Input-Section zur Definition der Eingabedaten, einer Process-Section für die Auflistung der Arbeitsprozesse und einer Output-Section zur Festlegung der Ausgabedaten. Abbildung 6 zeigt in schematischer Form das Prinzip dieses Entwurfsschemas. Die sequentielle Folge der Arbeitsprozesse, die eine Teilaufgabe konstituieren, legt der Programmdesigner durch Zahlen fest. Dabei kann er an anderer Stelle im Programm mit Hilfe einer solchen Zahl einen Prozeß genauer beschreiben. Den Fluß der Eingabe- und Ausgabedaten symbolisiert ein offener Pfeil, den Steuerfluß ein ausgezogener Pfeil. Die Eingabedaten eines Prozesses trägt der Pro-
55
5.2 Methoden des Top-down Entwurfs
INPUT
( P R O C E S S
OUTPUT
grammdesigner in der Input-Section in Kästchen symbolisch ein (z. B. Lohndatensatz). In analoger Form werden die Ausgabedaten in der Output-Section definiert. Beispielsweise setzt der Prozeß P! in Abbildung 6 das Eingabedatenelement I[ in die beiden Ausgabeelemente Oj und 0 2 um, wobei 0 2 in den nachfolgenden Prozeß P 2 als Eingabedatum eingeht. Abbildung 6 gibt auch einige Variationsmöglichkeiten für die Darstellung der Eingabe- und Ausgabedaten an. So haben die beiden Prozesse P 3 und P 4 zum Teil dieselben Eingabedaten, die sich aus zwei Datenelementen zusammensetzen. Äußere Datenträger, wie Magnetband und Lochkarte, werden durch die herkömmlichen Symbole der Programmablaufpläne repräsentiert. Im Grunde genommen verlangt die HIPO-Technik, daß eine Vereinheitlichung der Daten und der Datenträger, die in einer Aufgabe vorkommen, anzustreben ist und daß sie vor dem Programmentwurf erfolgt. Zum Steuerfluß ist zu sagen, daß er im Normalfall in die Process-Section von oben eintritt und sie am unteren Rand wiederum verläßt. In diesem Fall ist es nicht notwendig, den Steuerfluß mit einzuzeichnen. Hingegen zeigt Abbildung 6
5. Strukturierter Softwareentwurf
56
einen Sonderfall, bei dem aufgrund einer Verzweigung im Prozeß P 3 der Steuerfluß entweder an dieser Stelle oder nach dem Prozeß P 4 die Process-Section verläßt. Ein solches Schema stellt nicht nur die Ergebnisse des Feinentwurfs dar, sondern es kann auch zur Programmdokumentation herangezogen werden. Einen praktischen Anwendungsfall zeigt Abbildung 20. Da die Firma IBM diese Entwurfstechnik propagiert, kommt ihr ein gewisser normativer Charakter zu, wodurch sie sich von anderen Methoden unterscheidet. Allerdings kann die starke Datenbezogenheit ein Nachteil sein, indem dadurch ein Programm, das eine bestimmte Aufgabe löst, zu starr mit den Eingangs- und Ausgangsdaten dieser Aufgabenstellung verbunden ist, worunter die Änderungsfreundlichkeit leidet. Außerdem ist der Arbeits- und Platzaufwand für die Erstellung eines HIPO-Diagramms sehr hoch. Die Stärke dieser Technik liegt mehr auf der Seite der Dokumentationshilfe als der Entwurfsmethodik [45, S. 74],
5.2.2 Die Jackson-Methode Jackson hat seine Ideen zur Frage des Softwareentwurfs im Buch „Principles of Program-Design" 1975 veröffentlicht [25], Seine Entwurfsmethode wird dort an Hand vieler Programmierbeispiele entwickelt. Wegen dieser praxisnahen Darstellung fand sie bei Anwendungsprogrammierern große Beachtung, was sich darin ausdrückt, daß bereits eine „Usergroup" existiert, die auch Schulungsaktivitäten entfaltet. Die Jackson-Methode ist noch stärker als die HIPO-Technik datenorientiert; denn Jackson geht beim Entwurf eines Softwareproduktes vom Grundsatz aus: „Program structures should be based on data structures" [25, S. 10]. Er begründet dieses Axiom damit, daß ein Computer seine Umgebung nur über Daten und ihre Strukturen „wahrnehmen" kann. Jackson stellt deshalb die Datenstruktur über den Algorithmus und damit auch über die Programmstruktur. Als Konsequenz dieses Ansatzes besteht in der Jackson-Methode jeder Softwareentwurfsprozeß aus zwei Entwurfsphasen: (1)
der Analyse und dem Entwurf der Datenstrukturen einer zu programmierenden Aufgabe
(2)
der 1:1-Abbildung der in der Phase 1 entworfenen Datenstrukturen auf eine Programmstruktur.
Jackson denkt bei seiner ersten Entwurfsphase nicht nur in den Kategorien einer funktionellen Datenstruktur, sondern glaubt auch schon, ihre physische Realisierung berücksichtigen zu müssen. Das ist ein erster Ansatzpunkt für eine Kritik seiner Methode; denn mancher Systemdesigner hat gerade erst jetzt aus dem Ent-
5.2 Methoden des Top-down Entwurfs
57
wurf von Informationssystemen gelernt, daß eine strenge Unterscheidung von funktioneller und physischer Datenstruktur den Entwurfsprozeß erleichtert. Weiterhin nimmt Jackson an, daß sich in der Regel eine Datenstruktur auch tatsächlich 1:1 auf die Programmstruktur abbilden läßt. Zu dieser Annahme ist er deshalb berechtigt, weil sein Verfahren immer sequentiell organisierte Dateien unterstellt (Verfahren A nach Langers). Für Ausnahmefälle führt er das Verfahren der Programminversion ein [25, S. 169 ff.]. Da die Datenstrukturen häufig Datenelemente im Sinne von COBOL sind, entstehen in der Jackson-Methode daraus Programmstrukturen, die aus elementaren COBOL-Anweisungen bestehen. Man kann das an vielen Programmierbeispielen im Buch von Jackson beobachten (z. B. [25, S. 56, 62], Damit wird aber auch das Abstraktionsstufenkonzept, das ursächlich mit dem modernen Softwareentwurf verbunden ist, weitgehend hinfällig. Neben dieser Entwurfsstrategie zeichnet sich die Jackson-Methode, ähnlich der HIPO-Technik, auch noch durch eigene Darstellungsmittel für drei Programmbausteine aus, nämlich die Sequenz, die Iteration und die Selektion. Diese Darstellungsmethode wird in gleicher Weise für Programmstrukturen und Datenstrukturen angewendet. So stellt Jackson die sequentielle Folge von Programmbausteinen oder Datenelementen, die er Sequenz nennt, durch einen Baum dar. Besteht beispielsweise eine Programmkomponente A aus den drei Elementen B, C und D, dann ergibt sich dafür folgende Baumdarstellung:
Programmschleifen, die in der strukturierten Programmierung natürlich häufig vorkommen, nennt Jackson Iterationen und markiert sie durch einen hochgestellten Stern. Die Selektion, die eine Auswahl aus zwei oder mehr alternativen Möglichkeiten darstellt (IF THEN ELSE-Anweisung oder CASE-Verzweigung), symbolisiert er durch einen hochgestellten kleinen Kreis. Abbildung 7 bringt ein Beispiel für die Struktur eines Programms, das einen Bericht druckt. Ausgehend vom Ansatz, daß die Programmstruktur ein Abbild der Datenstruktur sein soll, legt der Programmdesigner in diesem Fall dem Programmentwurf die Struktur des Ausgabedatenträgers, d. h. der zu druckenden Liste, zugrunde. Sie besteht aus einem Berichtskopf, einem Hauptteil und einem Endeteil, die nacheinander von der Datenverarbeitungsanlage auszugeben sind. Im Hauptteil druckt das Programm mehrere Berichtsseiten, so daß die Pro-
58
5. Strukturierter Softwareentwurf
Abb. 7 Schema eines Programmentwuifs nach Jackson [38, S. 111].
grammkomponente „Seite" eine Iteration ist. Jede Seite beginnt mit einem eigenen Seitenkopf und endet in einem Endteil, in dem beispielsweise eine Zwischensumme ausgedruckt wird. Im Mittelteil einer Seite sind mehrere „Nachrichten" nacheinander auszugeben, wobei drei Nachrichtenformate zur Auswahl zur Verfügung stehen. Die Programmkomponente „Nachrichtenklasse" ruft deshalb durch eine CASE-Anweisung eine der drei Komponenten Format 1, Format 2 oder Format 3 auf. Die Programmstruktur der Abbildung 7 haben wir bewußt in der Weise verbal beschrieben, daß wir den Begriff „Programmkomponente" anstelle von „Programmodul" verwendet haben. Dadurch soll ausgedrückt werden, daß die Mehrfachverwendung einer Programmkomponente, wie sie der Begriff „Modul" impliziert, in der Jackson-Methode nicht zugelassen ist. So bringt Jackson in seinem Buch [25, S. 34] die nachfolgende Struktur, die er verbietet:
5.2 Methoden des Top-down Entwurfs
59
Er begründet diese Regel damit, daß sich bei einer solchen Struktur, die kein Baum mehr ist, jede Änderung in „PD" implizit auch auf „PE" und „PF" auswirkt und vice versa. Er fordert deshalb eine Entflechtung in Form einer Duplizierung von PJ, wodurch sich jede Änderung in einer übergeordneten Programmkomponente immer nur auf die ihr eindeutig zugeordnete Unterkomponente auswirken kann. Diese Entwurfsstrategie verlängert offensichtlich ein Programm. Sie kann schon jetzt als Nachteil der Jackson-Methode vermerkt werden. Die Forderung nach einer 1:1 -Abbildung der Datenstruktur auf die Programmstruktur ist nicht immer realisierbar. Als Beispiel fuhrt Jackson selbst eine Lochkartendatei an, wobei auf jeder Lochkarte eine Zeile einer Matrix gespeichert ist [25, S. 151 ]. Es ist ein Programm zu entwerfen, daß die Matrix spaltenweise ausdruckt. Hier versagt zunächst die Jackson-Methode; denn den Zeilenelementen der Matrix entsprechen nicht die Spaltenelemente. Im allgemeinen wird auch die Anzahl der Spalten nicht identisch sein mit der Anzahl der Zeilen. Diese Konfliktsituation läßt sich beheben, wenn ein Leseprogramm zunächst die gesamte Lochkartendatei in den Hauptspeicher überträgt, so daß das Druckprogramm anschließend die Matrix spaltenweise ansprechen kann. Es sind also zwei Programme zu schreiben, die auf verschiedenen Datenstrukturen beruhen. Es gibt Beispiele, wo das zweite Programm nach einigen Änderungen, die nur den Code aber nicht die Programmstruktur betreffen, simultan oder teilweise simultan zum ersten Programm als Subtask laufen kann. Jackson nennt eine solche Programmstruktur eine „Programminversion". An diesem Beispiel wird noch einmal deutlich, welche Schwierigkeiten entstehen, wenn sich der Programmdesigner in seiner Arbeit unbedingt an Datenstrukturen klammern muß. Wir werden diese Kritik auch noch an unserer Fallstudie explizit aufzeigen (s. Kap. 6.4).
5.2.3 Der Ansatz von Warnier (LCP-Methode) Die Entwurfsmethode „Logical Construction of Programs" (abgekürzt LCP) hat Warnier 1974 in seinem gleichnamigen Buch niedergelegt [49], Auch Warnier strukturiert Programme hierarchisch und top down. Er geht dabei vom Grundsatz aus:
60
5. Strukturierter S o f t w a r e e n t w u r f
„The hierarchical structure of the program is deduced from that of the input data" [49. S. 24], Aus dieser Definition folgt, daß dieses Verfahren methodisch dem Ansatz von Jackson nahesteht. Es gehört ebenfalls zur Klasse der datenorientierten Entwurfsverfahren. Da Warnier aber nur die Eingabedaten im Entwurfsprozeß berücksichtigt, nimmt sein Ansatz eine Zwitterstellung zwischen der JacksonMethode und der HIPO-Technik ein. Warnier hat seine Softwarekonstruktionsmethode nicht so weit entwickelt wie Jackson oder auch Constantine, über den noch zu berichten sein wird. Deshalb haben Praktiker sie bisher wenig beachtet. Wie Jackson modularisiert auch Warnier Daten und Programme nach den gleichen Strukturierungsprinzipien. Er geht dabei vom informatorischen Charakter, den beide Objekte gemeinsam haben, aus und gewinnt durch Abbildungsvorgänge hierarchisch gegliederte Teilinformationen, die wiederum Daten oder Programmteile sein können [49, S. 19]. Die Strukturen, die aus diesem Entwurfsprozeß resultieren, klassifiziert er als „repetitive Struktur", die der Iteration bei Jackson entspricht, oder „alternative Struktur" (Selektion). Allerdings gibt er für die Darstellung dieser Strukturelemente keine eigenen Darstellungsmittel an, sondern benutzt für diesen Zweck entweder eine verbale informelle Beschreibung der Strukturen, oder er greift die herkömmliche Symbolik der Programmablaufpläne auf. Bemerkenswert ist auch das Faktum, daß er GOTOAnweisungen nicht verbietet. Zusammen mit IF THEN ELSE-Anweisungen kommen sie in seinen Programmierbeispielen häufig vor [49, S. 51 ]. Obwohl in den Fachdiskussionen über Softwaretechnologie der Ansatz von Warnier bisher kaum beachtet wurde, erwähnen wir ihn doch in dieser zusammenfassenden Darstellung des Top-down Entwurfs, weil er einen neuen Gesichtspunkt in die Fachdiskussion einbringt, der weder in der HIPO-Technik, noch in der Jackson-Methode und auch nicht in den noch zu diskutierenden Entwurfsmethoden zu finden ist, nämlich die Unterscheidung zwischen der Programmlogik und den ausführenden Programmteilen. Aus dieser neuartigen Denkungsart resultieren bei Warnier zwei Entwurfsphasen: (1) (2)
der Entwurf der „logischen Programmstruktur" die Ableitung informationeller Arbeitsprozesse aus den Eingabedaten.
Die logische Programmstruktur repräsentiert den Steuerfluß durch ein Programm. Für den Entwurf dieses Steuerflusses greift Warnier auf die Schaltalgebra und ihre Minimierungsmethoden, wie das Karnaugh-Diagramm, zurück [49, S. 91 ff.]. Die in dieser Weise entstandenen Ablaufstrukturen realisiert er mit IFAnweisungen. Dieses Entwurfsprinzip erscheint uns deshalb interessant zu sein, weil hiermit zum ersten Mal die langjährigen Erfahrungen des Hardwareentwurfs auf Softwarekomponenten übertragen werden. Wir haben die Unterscheidung von Warnier in Steuerfluß und Datenfluß in der LITOS-Methode aufgegriffen
5.2 Methoden des Top-down Entwurfs
61
und weiterentwickelt. In deii beiden Abschnitten 6.2.3 und 6.2.4 wird dieser Aspekt eingehend untersucht. An dieser Stelle soll, unabhängig von der Warnier-Methode, die Anwendbarkeit der Schaltalgebra auf Softwareprobleme an einem Beispiel dargestellt werden. Gegeben ist eine Datei, zu der in einem Programm verschiedene Teilaufgaben (Tasks) gleichzeitig zugreifen. Der Programmdesigner steht vor der Aufgabe zu verhindern, daß gegenseitige Beeinflussungen der Teilaufgaben stattfinden, die schädlich sind. Unschädlich ist der Fall, daß verschiedene Teilaufgaben zu gleicher Zeit denselben Datensatz lesen. Kritisch ist hingegen ein Prozeß, bei dem eine Teilaufgabe einen auf den neuesten Stand gebrachten Datensatz gerade zu dem Zeitpunkt in die Datei zurückschreibt, zu dem eine andere Teilaufgabe ihn liest. Um diese Situation zu verhindern, ist in dieses Softwaresystem eine Verriegelung einzubauen. Der Einfachheit halber wollen wir dieses Verriegelungsproblem auf zwei Teilaufgaben beschränken. Dann läßt es sich schematisch wie folgt darstellen:
y.
Wir bezeichnen in der Taks T j die READ-Anweisung als Ereignis .jcj " und den REWRITE-Zugriff als „ x 2 " . In analoger Form werden in T : die Ereignisvariablen ,pc 3 " und , , x 4 " vergeben. Wir stellen nun eine Wertetafel der 16 Variationen über diese vier Ereignisvariablen auf. In diese Tafel wird außerdem eine Verriegelungsfunktion „ V " eingetragen. Sie hat den Wert Eins, wenn eine Verriegelung stattfinden muß.
62
5. Strukturierter Softwareentwurf
Xi
x2
X3
x4
Verriegelung V
0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1
0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1
0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1
0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
0 0 0
Bemerkungen
kann nicht auftreten 0 1 1 kann nicht auftreten 0 11 0 kann kann kann kann kann
nicht nicht nicht nicht nicht
auftreten auftreten auftreten auftreten auftreten
Aus dieser Tafel können wir jetzt die Verriegelungsfunktion als disjunktive Normalform ableiten und, falls notwendig, auch vereinfachen. V = (->Xi AX 2 A-1X3 AX 4 )V(->X 1 AX 2 AXJ A-1X4) 33 v ( x ! A-IX 2 A-TX3AX4) Vor der Durchführung jedes Dateizugriffs prüft das Programm, ob V den Wert Eins hat. Ist das der Fall, dann muß die Programmausfuhrung in dieser Task so lange warten, bis V den Wert Null angenommen hat. In PL/1 kann man diesen Verriegelungsmechanismus mit Hilfe der Pseudovariablen „COMPLETION" realisieren. Zum Schluß dieses Kapitels müssen wir darauf hinweisen, daß die Anwendbarkeit der Schaltalgebra auf Softwareprobleme auch von Warnier nur grundsätzlich angedeutet wird. Grundlegende Untersuchungen zu diesem Problemkomplex stehen noch aus.
52.4 Die Constantine-Methode 5.2.4.1 Grundlagen Der Ansatz von Constantine unterscheidet sich von den bisher beschriebenen
5.2 Methoden des Top-down Entwurfs
63
Verfahren, wie der HIPO-Technik und der Jackson-Methode, darin, daß die Entwurfsstrategie von der Aufgabe ausgeht, die ein Computerprogramm lösen soll und nicht von den Datenstrukturen, die in die Aufgabenlösung eingehen. Wenn wir zunächst einmal von reinen Druckprozessen, wie beispielsweise dem Schreiben von Adreßaufklebern, absehen, sind die Dateneingabe und die Datenausgabe keine originären informationellen Funktionen, sondern nur Hilfsmittel, um eine Aufgabe auf einer Datenverarbeitungsanlage lösen zu können. Die ConstantineMethode gehört deshalb zur Klasse der aufgabenorientierten Entwurfsverfahren und nicht zu den datenorientierten Methoden. Im strukturierten Entwurf leitet Constantine aus der Aufgabenstellung Programmodule ab, die möglichst einfach und unabhängig sein sollen. Er geht dabei von der Überlegung aus, daß es schon immer schwierig war, ein Problem zu lösen, wenn man dabei gleichzeitig alle Problemaspekte beachten muß. Eine Problemlösung ist schneller und mit einem geringeren Fehlerrisiko zu erarbeiten, wenn es gelingt, das Problem in Teilprobleme aufzuspalten, die unabhängig voneinander analysiert und gelöst werden können [47, S. 116]. Ein solches Teilproblem identifiziert Constantine mit dem Begriff Modul. Das zeigt, daß auch er sehr schnell in die Denkkategorien der Programmierung überwechselt, anstelle den Programmentwurf als organisatorische Tätigkeit zu sehen, bei der aus übergeordneten Aufgaben Teilaufgaben induktiv abgeleitet werden. In seinem Ansatz läuft deshalb die Aufgabenanalyse parallel zur Synthese der Programmstruktur ab [35, S. 255]. Da der Begriff „Modul" bei Constantine zum Leitmotiv jeder Strukturierung wird, muß er ihn präzisieren. Hierbei untersucht Constantine zwei Aspekte eines modular aufgebauten Systems, nämlich die Unabhängigkeit eines Moduls von den anderen Modulen in diesem System und die Einfachheit eines Moduls. In erster Näherung kann man sagen, daß, je kleiner ein Modul ist, desto einfacher auch seine Struktur sein wird. Für den Grad der Unabhängigkeit eines Moduls bringt Constantine einen neuen Begriff in die Softwaretechnologie ein, die „Modulkopplung". Je lockerer Module gekoppelt sind, desto größer ist auch ihre Unabhängigkeit. Allerdings können die beiden Begriffe Einfachheit und Unabhängigkeit nicht isoliert voneinander gesehen werden. Wenn wir von einer vorgegebenen Aufgabe ausgehen, gibt es die Möglichkeit, sie entweder durch einen einzigen Modul zu lösen, der dann komplex strukturiert ist, aber dafür unabhängig arbeitet, oder diese Aufgabe wird auf viele kleine Module abgebildet, wodurch sich ihre Einfachheit vergrößert, ihre Unabhängigkeit aber abnimmt. Hier zeichnet sich die Frage nach der optimalen Modulgröße ab. Constantine legt dar, daß die Modulkopplung zwei Komponenten aufweist, nämlich die Datenflußkopplung und die Steuerflußkopplung. Eine Datenflußkopplung liegt beispielsweise vor, wenn ein Modul mit der CALL-Anweisung einen anderen Modul aufruft und dabei Daten in Form von Parametern übergibt. Eine Steuerflußkopplung ist dann gegeben, wenn diese Parameter den Steuerfluß im aufge-
64
5. Strukturierter Softwareentwurf
rufenen Modul beeinflussen, in dem sie in IF THEN ELSE-Anweisungen abgefragt werden. Für Constantine bindet eine Steuerflußkopplung zwei Module stärker als die Datenflußkopplung. Diese Beobachtung tritt besonders deutlich in der simultanen Programmverarbeitung zutage, wo der Steuerfluß in den verschiedenen parallelen Wegen, beispielsweise mit Hilfe von Ereignisvariablen, von Zeit zu Zeit synchronisiert werden muß. Außerdem kann sich hierbei auch jede Änderung im Steuerfluß eines Moduls auf die anderen Module auswirken. Ein typisches Beispiel für diese Erscheinung ist das Deadlockproblem [44, S. 138], Deshalb sind vom Steuerfluß her gesehen in der seriellen Programmverarbeitung die Module unabhängiger als in der simultanen. In der Datenflußkopplung nimmt der Grad der Unabhängigkeit mit der Anzahl der Parameter, die zwischen Modulen übergeben werden, ab. Ein besonderes Problem sind hierbei die internen Prozeduraufrufe, bei denen keine explizite Parameterübergabe erfolgt. Ebenfalls sind Daten kritisch, die von mehreren Modulen gemeinsam verwendet werden (EXTERNALDefinition in PL/1 oder COMMON-Bereich in FORTRAN). Sie führen zu einer engen Modulkopplung, ohne daß diese Eigenschaft nach außen hin sichtbar wird. Dadurch können sich Programmierfehler eines Moduls in einem zweiten auswirken, ohne daß die Ursache leicht erkennbar ist [28, S. 42], Deshalb gibt es in der Softwarekonstruktionslehre Bestrebungen, solche „globalen" Variablen in der strukturierten Programmierung analog der GOTO-Anweisung zu verbieten [52, S. 28 ff.]. Die Unabhängigkeit eines Moduls ist aber nicht nur eine Funktion seiner Umgebung, sondern wird auch bestimmt durch den Zusammenhalt der Elemente innerhalb eines Moduls. Je größer diese interne Bindung in einem Modul ist, desto weniger besteht die Notwendigkeit, mit anderen Modulen kommunizieren zu müssen. In Analogie zu den Anziehungskräften von Atomen und Molekülen nennt Constantine diese interne Bindungskraft von Modulen „Kohäsion" [53, S. 143], Er führt sieben Kohäsionsstufen ein, wobei die Bindungsstärke in einem Modul von Stufe zu Stufe zunimmt. Die deutschen Bezeichnungen für diese Stufen lauten [28, S. 44]: zufällige Bindung (coincidental association) logische Bindung (logical association) zeitliche Bindung (temporal association) prozedurale Bindung (procedural association) kommunikative Bindung (communicational association) sequentielle Bindung (sequential association) funktionelle Bindung (functional association). Eine zufällige Bindung liegt dann in einem Modul vor, wenn keinerlei operativer oder deklarativer Zusammenhang zwischen seinen Elementen besteht.
5.2 Methoden des Top-down Entwurfs Beispiel:
65
A = B + C; GET DATA (X, Y); PUT LIST (U. V); I F Z = O THEN CALL M;
Zwischen diesen vier Anweisungen bestehen offensichtlich keinerlei operative Beziehungen. Ein Programmierer hat sie aus rein pragmatischen Gründen, beispielsweise wegen der dynamischen Speicherplatzreservierung, gemeinsam in einer Prozedur notiert, so daß der prozedurale Zusammenhang zufällig diese Anweisungen verbindet. Logische Bindung bedeutet bei Constantine die Zusammenfassung von Anweisungen und Vereinbarungen ein und derselben Art in einer Prozedur. Beispiele dafür sind eine Eingabepxozedux, eine arithmetische Prozedur oder eine EditingProzedur. Der Begriff zeitliche Bindung unterscheidet sich von der logischen Bindung durch das zusätzliche Merkmal, daß die Anweisungen und Vereinbarungen eines Moduls nicht nur logisch gesehen eine Einheit bilden, sondern daß sie auch zu derselben Zeit abgearbeitet werden. Diese Zeitkomponente ist mit dem Programmablauf immanent verbunden. Typische Beispiele für diese Kohäsionsstufe sind Initialisierungs- und Terminierungsmodule [47, S. 123]. Constantine legt dar, daß eine prozedurale Bindung dadurch zustande kommt, daß ein Arbeitsprozeß existiert, an dem alle Bausteine eines Moduls beteiligt sind, so daß das Fehlverhalten auch nur eines Elementes das Ergebnis des gemeinsamen Arbeitsprozesses beeinflußt. Als Beispiel nennt er die Anweisungen innerhalb einer DO WHILE-Schleife. Allerdings unterscheidet die Constantine-Methode zwischen der prozeduralen Bindung und der funktionellen, indem prozedurale Module Komponenten einer Funktion sind. Insofern ist die prozedurale Bindung schwächer als die funktionelle. Das Attribut kommunikativ vergibt Constantine dann an einen Modul, wenn seine Anweisungen auf dieselben Daten zurückgreifen. Als Beispiel nennt er einen informationellen Arbeitsprozeß, dessen Anweisungen dieselben Eingabedaten benutzen [53, S. 161], Um zu erkennen, ob die Elemente eines Moduls eine kommunikative Bindung eingehen, führt Constantine einen Datenflußgraphen ein. Werden im Programmablauf die Ausgabedaten eines Prozesses zu Eingabedaten des nächsten Arbeitsprozesses, dann ist die Bindung zwischen diesen beiden Modulen noch stärker als die kommunikative Form. Constantine nennt sie sequentielle Bindung. Ein Modul, der auf dieser Kohäsionsstufe aufbaut, kann mehrere Funktionen enthalten oder auch nur Teil einer Funktion sein. Deshalb ist die sequentielle Bindung schwächer als die funktionelle. Ist ein Modul in der Lage, einen informationellen Arbeitsprozeß völlig unabhängig von anderen Modulen auszuführen, dann ist seine intramodule Kraft und damit auch die Bindung zwischen den Anweisungen und Vereinbarungen im Modul am stärksten. Repräsentative Beispiele sind mathematische Funktionen, wie die Be-
5. Strukturierter Softwareentwurf
66
rechnung einer Quadratwurzel oder auch ein Zufallszahlengenerator. Constantine nennt diese Form der Kohäsion funktionelle Bindung, obwohl ein solcher Modul nicht unbedingt eine Funktion im mathematischen Sinn darstellt; denn für Constantine bedeutet Funktion nichts weiter als Aufgabe mit Gliedcharakter. Dieser Auffassungsunterschied zwischen Software-Engineering und der Mathematik soll mit dem Beispiel einer internen PL/1-Prozedur belegt werden. DRUCKEN: PROC; PUT LIST ('ERGEBNIS'); PUT LIST ('Y = '. Y. 'X = PUT LIST ('ENDE'); END DRUCKEN;
X);
Diese Prozedur bildet die Menge der Eingabedaten {X, Y} auf die Menge der Ausgabedaten {'ERGEBNIS', 'Y = ', Y, 'X = ', X, 'ENDE'} ab und erfüllt damit das Merkmal des Funktionsbegriffs der Mathematik. In der Terminologie von Constantine sind die Anweisungen dieser internen Prozedur aber nur zeitlich und nicht funktionell gebunden; denn sie geben das Ergebnis eines Arbeitsprozesses zeitlich gesehen „gemeinsam" aus. Constantine ist sich darüber klar, daß an dieser Stelle seiner Methode Definitionsschwierigkeiten entstehen können. Er nennt deshalb vier Regeln, aus denen man ableiten kann, ob es sich um eine funktionelle Bindung im Sinne seiner Definition handelt [47, S. 124]. Diese Regeln gehen davon aus, daß der Programmdesigner die Aufgabe eines Moduls mit einem Satz beschreiben kann. Es wird geprüft, ob er folgende Bedingungen erfüllt: (1)
Wenn dieser Satz ein zusammengesetzter Satz ist, ein Komma oder mehrere Verben enthält, dann liegt eine logische, kommunikative oder sequentielle Bindung vor, aber keine funktionelle. Beispiel: „Lies Stammsatz und Bewegungssatz, verarbeite Bewegungsdaten und speichere die Ergebnisse in der Stammdatei." Dieser Programmodul führt mehr als eine Funktion aus. Als erstes liest er zwei Dateien. Anschließend wird ein Stammsatz auf den neuesten Stand gebracht, indem der Programmodul einen Bewegungssatz verarbeitet. Die Ausgabedaten des Leseprozesses sind also Eingabedaten des Verarbeitungsprozesses und dessen Ergebnisse Eingabedaten für das Schreiben eines neuen Stammsatzes. Es liegt deshalb in diesem Beispiel eine sequentielle Bindung vor. Um eine funktionelle Bindung zu erreichen, ist es notwendig, Module so zu definieren, daß sie sich durch ein einziges Verb beschreiben lassen. Auf das obige Beispiel übertragen, würde diese Forderung bedeuten, daß die Aufgabenstellung lautet: „Bringe einen Stammsatz auf den neuesten Stand."
(2)
Enthält der Satz, der die Funktion des Moduls verbal darstellt, Wörter, die eine zeitliche Folge beschreiben wie Zahl- und Zeitadverbien, dann handelt es sich in der Regel um eine zeitliche oder sequentielle Bindung.
5.2 Methoden des Top-down Entwurfs
67
(3)
Bezieht sich das Akkusativobjekt auf mehrere Arbeitsobjekte („alle" Daten), dann liegt eine logische Bindung vor.
(4)
Verben wie „initialisieren" und„löschen" weisen auf eine zeitliche Bindung hin.
Es drängt sich die Frage auf, ob die sieben Kohäsionsstufen von Constantine überhaupt vollständig sind. So scheint uns mindestens eine Bindungsform zu fehlen, nämlich die kollaterale Bindung. Wir wollen darunter die Form der Kohäsion verstehen, die dann zustande kommt, wenn Anweisungen kollateral abgearbeitet werden, wie es die problemorientierte Programmiersprache ALGOL 68 vorsieht [32, S. 46]. In PL/1 kann der Programmierer diese Spracheigenschaft auf der Sprachebene der Anweisungen 2 mit Hilfe der Pseudovariablen COMPLETION simulieren [44, S. 136 f.]. Es liegt die Vermutung nahe,daß sich die Klassifizierung der Bindungsarten, wie sie Constantine gegeben hat, mehr auf Intuition gründet als auf systematische Untersuchungen. Trotzdem erkennt der Programmdesigner aus seinem Vorschlag, welche Faktoren er beim Entwurf von Programmodulen beachten muß. So ist als Ziel jeder Modularisierung die funktionelle Bindung, die auf einer verselbständigten Teilaufgabe beruht, anzustreben. Wenn ein funktionell definierter Programmodul sehr groß ist, wird der Programmdesigner nicht umhin können, ihn im Top-down Design weiter in Unterfunktionen aufzulösen. Welchen Umfang sollen sie annehmen? Sowohl in der materiellen als auch der immateriellen Güterproduktion gilt das Gesetz optimaler Produktions- und Fertigungsgrößen. So gibt Andler [1, S. 98 ff. und S. 131 ff.] für die Werkstattfertigung eine optimale Losgröße an, die dadurch gegeben ist, daß die auflagefixen Kosten, die auf das Stück bezogen mit wachsender Losgröße abnehmen, gleich den auflageabhängigen Kosten der Fertiglagerung sind. Sie nehmen linear mit der Losgröße zu. Wir haben gezeigt, daß für die Informationsverarbeitung ein analoger Ansatz gilt [41, S. 233 ff.]. Man kann erwarten, daß er auch für die Frage der optimalen Größe eines Moduls anwendbar ist. Wird eine informationelle Aufgabe durch einen einzigen großen Modul gelöst (z. B. bestehend aus 500 Anweisungen), dann ist die Kohäsion in diesem Modul groß. Weil ein solches Programm nur über einen Modul verfügt, hat die Kopplung den Wert Null. Löst nun der Programmdesigner diesen Modul in kleinere Module auf, dann steigen die Kopplungseffekte in erster Näherung linear mit der Anzahl der Module an und die Kohäsion nimmt umgekehrt proportional zur Anzahl der Module ab. Je kleiner ein Modul ausgelegt wird, desto geringer ist auch seine Komplexität. Umgekehrt verhält sich die Modulkopplung; denn je mehr interdependente Beziehungen ein Modul eingeht, desto komplexer ist auch seine Einbettung in ein Softwaresystem. Wir können deshalb einen „Komplexitätsgrad" als Summe aus Kohäsion und Kopplung einführen (s. Abbildung 8). Er hat an der Stelle ein Minimum, an der die Kopplung die Kohäsion schneidet. Da 2
Bekanntlich ist das Simultanisierungsniveau in PL/1 die Prozedurebene.
5. Strukturierter Softwareentwurf
68
die Entwicklungs- und Wartungskosten eines Moduls proportional zu seiner Komplexität angesetzt werden können, hat die Kostenkurve als Funktion der Modulgröße denselben Verlauf wie der Komplexitätsgrad.
MODULE
Abb. 8
JE
AUFGABE
MDPULE/AUFGABE
Ableitung einer optimalen Modulgröße.
Diese Betrachtungen haben zunächst rein qualitativen Charakter. Constantine hat nicht angegeben, wie man die Kohäsion und die Modulkopplung messend erfassen kann. Als optimale Modulgröße nennt er deshalb einen Erfahrungswert, der zwischen 10 und 100 Anweisungen je Modul liegt [53, S. 222]. Mit der Modulgröße hängt auch die Frage zusammen, auf wie viele Untermodule sich ein Modul stützen soll. Um sie zu beantworten, greift Constantine einen Begriff der Organisationslehre auf, nämlich die „Kontrollspanne". Sie gibt an, wie viele Untergebene ein Vorgesetzter führen kann. Diesen Begriff übernimmt Constantine und empfiehlt, daß ein Modul fünf bis neun Untermodule haben soll.
5.2.4.2 Das Entwurfsverfahren Wie Jackson so unterscheidet auch Constantine zwischen einer Entwurfsmethodik und der Darstellung von Entwurfsergebnissen in formalisierter Form als „Strukturdiagramm". Das Ziel des strukturierten Entwurfs steckt er sehr weit, indem er postuliert: „Structured design is the art of designing the components of a system and
69
5 . 2 M e t h o d e n des T o p - d o w n E n t w u r f s
t h e interrelationship b e t w e e n t h o s e c o m p o n e n t s in t h e best possible w a y " [ 5 3 . S. 7], Diese B e g r i f f s b e s t i m m u n g sagt n i c h t s S p e z i f i s c h e s zur C o n s t a n t i n e - M e t h o d e aus. J e d e r gute P r o g r a m m i e r e r hat a u c h bisher versucht, möglichst kurze und schnelllaufende P r o g r a m m e zu s c h r e i b e n . Was ihm aber bei seiner A r b e i t f e h l t e , war eine L e h r e , wie er ein solches Ziel sicher erreichen k a n n . C o n s t a n t i n e führt allerdings zusätzlich z u m , . s t r u k t u r i e r t e n E n t w u r f " a u c h n o c h den T e r m i n u s „ T o p down D e s i g n " ein. S e i n e B e g r i f f s b e s t i m m u n g e n t s p r i c h t unserer Auffassung v o m s t r u k t u r i e r t e n E n t w u r f , i n d e m er d e f i n i e r t : , , T o p - d o w n design requires t h e designer to design a program in t e r m s o f its m a j o r f u n c t i o n s " [ 5 3 . S . 3 3 3 ] .
D E F I N I T I O N
S Y M B O L
1.
MODUL
2.
VORDEFINIERTEP MoDUL
MODUL A R U F T
MODUL B A U F
UND
(IBERGIBT
PARAMETER
UND Y
AN ß , Z
EINGABE 1
H.
X.Y
DIE
MODUL B G I B T
DEN
X
PARAMETER
ZUR'TCK,
AUSGABE
nm
3GE
MODUL
A RUFT
UND ZWAR
MODUL E
I N DER
MODUL B B E Z I E H T DES
MODULS
A
EINE
SICH
(DER
VON A NACH B ) ,
UND
C
AUF,
REIHENFOLGE
AUF
VERZWEIGUNG
DATEN
DATENFLUSS
MODUL A
B.C.
GEHT
ENTHÄLT
NACH MODUL
C.
Abb. 9 Darstellungsmethode hierarchisch strukturierter Programmsysteme nach Constantine [47, S. 126].
70
5. Strukturierter Softwareentwurf
Die Constantine-Methode übernimmt von der HIPO-Technik die Differenzierung in Grobentwurf und Feinentwurf [47, S. 127], Im Grobentwurf leitet der Programmdesigner aus der zu lösenden Aufgabe Funktionen und Unterfunktionen zusammen mit ihren hierarchischen Beziehungen ab. Module, die Unterfunktionen repräsentieren, gehen in der Regel eine sequentielle oder kollaterale Bindung ein. Der Feinentwurf unterscheidet sich vom Grobentwurf darin, daß man nicht mehr fragt, welche Funktionen soll ein Programmsystem enthalten, sondern wie werden sie implementiert? Wie schon im letzten Kapitel entwickelt, ist der Angelpunkt der Constantine-Methode der Modulbegriff. Deshalb liegt in diesem Entwurfsverfahren bereits das Ergebnis des Grobentwurfs in modularisierter Form vor. Constantine nennt diese Darstellung „Strukturdiagramm". Abbildung 9 zeigt die Symbolik, die er für diesen Zweck einführt. Ein einfaches Rechteck symbolisiert den Programmodul als die organisatorische Grundeinheit, die top down in Unterkomponenten zu strukturieren ist. In der Terminologie der strukturierten Programmierung entspricht er dem Strukturblock [53, S. 548 ff.]. Vordefinierte Module, wie Funktionsunterprogramme in PL/1, kennzeichnet Constantine zusätzlich durch senkrechte Striche. Obwohl seine Entwurfsmethodik auf seinem eigenen Funktionsbegriff aufbaut, stellt das Strukturdiagramm aber nicht funktionelle Beziehungen dar, sondern bereits auch schon im Grobentwurf Modulaufrufe zusammen mit den Parametern, die in diese Prozesse eingehen. Ein Strukturbaum gibt deshalb an, welche Untermodule ein Modul in der Programmausführung aufruft, wobei die Aufrufrichtung von links nach rechts verläuft (s. Abbildung 9). Eine Besonderheit dieser Darstellungsmethode ist der Datenfluß und der Steuerfluß. Falls es notwendig ist, beim Aufruf eines Moduls anzugeben, wie sie sich auf den Programmablauf auswirken, wird der Datenfluß durch einen Kreis und der Steuerfluß durch einen Punkt markiert. Das unterste Bild in Abbildung 9 stellt deshalb dar, daß beim Aufruf der beiden Module B und C durch A, an B Daten übergeben werden und der Steuerfluß anschließend von A nach C verzweigt, ohne daß hierbei Daten fließen. Für eine detaillierte Darstellung der intermodularen Kommunikation stellt die Constantine-Methode den Datenflußgraphen zur Verfügung, auf den wir hier nicht näher eingehen wollen [53, S. 54 ff.]. Seine Kanten, dargestellt durch Pfeile, repräsentieren Datenflüsse, die Knoten, als Kreise gezeichnet, Module. Es wird sogar vorgeschlagen, bei komplexen Aufgaben zunächst den Datenflußgraphen zu entwerfen, um anschließend das Strukturdiagramm aus diesem Graphen abzuleiten [53, S. 254 ff.]. Diese Entwurfsstrategie hat zur Folge, daß ähnlich der HIPO-Technik Strukturdiagramme entstehen, die sich an Eingabe- und Ausgabeprozessen orientieren, obwohl Datenstrukturen nicht das entwurfsbestimmende Element sind. Ein Beispiel für diese Beobachtung findet man in Abbildung 10. Es wurde dort analog zu unserer Fallstudie mit der Constantine-Methode die Aufgabe strukturiert, eine Stammdatei auf den neuesten Stand zu bringen. Die
5.2 Methoden des Top-down Entwurfs
71
Aufgabenstellung unterscheidet sich von unserer Fallstudie allerdings in einigen wesentlichen Faktoren. So kommen keine Bewegungen ohne Stammsätze vor. Weiterhin bestehen die Stammsätze aus mehreren Feldern, die voneinander getrennt auf den neuesten Stand zu bringen sind, wobei natürlich nicht alle Felder angesprochen werden müssen. Auch kann ein Bewegungssatz aus mehreren Lochkarten bestehen, die dann zusammen einen funktionellen Bewegungssatz bilden.
Abb. 10
Strukturdiagramm für das Updaten einer Stammdatei [53, S. 315].
72
5. Strukturierter Softwareentwurf
Vergleicht man Abbildung 10 mit Abbildung 9, dann fällt zunächst auf, daß in diesem Strukturdiagramm zwei neue Symbole auftreten, nämlich ein Schleifensymbol und ein Symbol für die Selektion. Offensichtlich kommt man beim strukturierten Detailentwurf nicht ohne diese beiden Strukturelemente aus. Außerdem wurde in Abbildung 10 aus Platzgründen nur die Teilfunktion „Lesen Bewegungsdatei" tiefer gegliedert und von dieser wieder nur die Unterfunktion ,,Lesen Datenfeld" dargestellt. Trotzdem lassen sich aus dieser Darstellung die typischen Merkmale der Constantine-Methode ablesen. So sind Eingabe- und Ausgabeprozesse auf demselben hierarchischen Niveau wie die Verarbeitungsprozesse zu finden. Im Grunde genommen widerspricht diese hierarchische Struktur aber den Regeln des Top-down Entwurfes; denn dort wird postuliert, daß die Funktionen, die einer Realisierung näher liegen, tieferen Abstraktionsstufen zuzuordnen sind, als die mehr aufgabenorientierten Prozesse. Wenn man davon ausgeht, daß die Aufgabe eines Datenverarbeitungssystems darin besteht, durch Verknüpfung von Informationen neue Informationen zu erzeugen [41, S. 157], dann sind a posteriori Verarbeitungsfunktionen und Eingabe-Ausgabefunktionen nicht gleichberechtigt und damit im strukturierten Entwurf verschiedenen Abstraktionsniveaus zuzuweisen. Trotzdem ist festzuhalten, daß in einer Klassifizierung der Softwareentwurfsverfahren die Constantine-Methode im Gegensatz zur Jackson-Methode als aufgabenorientiertes Verfahren eingestuft wird. Abschließend ist noch zu bemerken, daß diese Entwurfsmethode das am weitesten entwickelte Verfahren ist, was man auch schon am Umfang unserer Ausführungen in diesem Kapitel ablesen kann.
5.2.5 Die SADT-Methode Die im letzten Kapitel aufgezeigten Schwächen der Constantine-Methode versucht die von der Firma SofTech Inc. in den USA entwickelte SADT-Methode zu beseitigen. Sie geht auf den „Structured Analysis" Ansatz (SA) von D. T. Ross zurück [40, S. 16 ff.]. Diese Entwurfsmethode zwingt den Softwaredesigner, durch ein spezielles Darstellungsschema, das er bereits beim Grobentwurf benutzen muß, von Anfang an in seiner Arbeit top-down und aufgabenorientiert vorzugehen. Dieses Ziel wird auch im Namen dieser Methode deutlich, die die Abkürzung für Structured Analysis Design 7echnique ist. Diese Strukturanalyse einer zu lösenden Aufgabe trägt die Attribute top down, hierarchisch und raodular [46, S. 2—1], In dieser Methode entwirft der Programmdesigner zunächst in einer Analysephase ein rein funktionell orientiertes Ablaufmodell, um daraus anschließend in einer synthetischen Phase einen Programmentwurf abzuleiten, der angibt, wie das Softwaresystem zu implementieren ist, damit es die geforderten Funktionen ausführt. Das Ergebnis wird wieder wie bei Constantine in einem speziellen Strukturdiagramm niedergelegt. Die beiden Aspekte jedes Softwareentwurfs, nämlich die Strukturierung der Daten und der Aufgaben, werden in einem untersucht. Hierin unterscheidet sich SADT von der Constantine-
5.2 Methoden des Top-down Entwurfs
73
Methode, in der man der Datenstrukturierung keine besondere Aufmerksamkeit widmet. Das Strukturdiagramm, das im Mittelpunkt dieser Entwurfsmethode steht, wird mit Hilfe der in Abbildung 11 dargestellten Symbole gezeichnet. Die SADT-Darstellungsmethode verlangt vom Programmdesigner, daß er jede Funktionseinheit top down in drei bis sechs Untereinheiten dekomponiert. So zeigt beispielsweise Abbildung 11, wie die Funktionseinheit 2 der höchsten hierarchischen Stufe in drei weitere Einheiten top down, funktionell und vom Datenfluß her gesehen, aufgelöst wird. Hierbei repräsentieren Pfeile Datenflüsse. Ihre Anordnung ist nicht beliebig, sondern ein wesentlicher Bestandteil der SADT-Entwurfsmethode und deshalb standardisiert (s. unterer Teil in Abbildung 11). Der von links in eine Funktionseinheit eintretende Pfeil stellt die Eingabedaten dar, der nach rechts austretende die abgehenden Datenflüsse. In un-
F U N K T I O N S E I N H E I T :
INITIIERENDER R
(Z. B,
EINGABE (Z,
B.
AUSGABE (z.
STAMMDATEI)
|
Abb. 11
SADT-Daistellungsmethode
DATENFLUSS
BEWEGUNGSDATEN)
B.
MECHANISMUS
NEUE
STAMMDATEI)
74
5. Strukturierter Softwareentwurf
serer Fallstudie, der Dateiverarbeitung, werden die Eingabedaten durch die Stammdatei, die auf den neuesten Stand zu bringen ist, und die Bewegungsdatei, die diese Updatefunktion auslöst, repräsentiert. Die SADT-Methode teilt außerdem die Eingabedaten in zwei Klassen ein, nämlich Daten, die zu verarbeiten sind, aber zusätzlich Steuerungscharakter haben, indem sie Arbeitsprozesse auslösen, und reine Eingabedaten (z. B. eine Stammdatei). Der Pfeil, der von oben in eine Funktionseinheit eintritt, markiert Eingabedaten mit steuernden Funktionen, der von links herangeführte reine Eingabedaten. Von unten tritt fallweise noch ein vierter Pfeil in eine Funktionseinheit ein, in Abbildung 11 als „Mechanismus" bezeichnet. Er spezifiziert das Arbeitsmittel, das eine Funktion ausführt. Es fällt auf, daß in dieser Darstellungsmethode die Iteration und die Selektion nicht darstellbar sind, was darauf schließen läßt, daß der SADT-Entwurf ein Softwaresystem rein funktionell als aufbauorganisatorisches Gebilde gliedert, ohne den Programmablauf zu berücksichtigen. Deshalb gibt es in dieser Methode kein eigenes Symbol für die Darstellung des Steuerflusses. Auch der Pfeil, der von oben in eine Funktionseinheit eintritt, symbolisiert nur einen Datenfluß, aller-
Abb. 12 Anwendung der SADT-Methode auf den Gemüseanbau[46, S. 4 - 2 ] .
5.3 Zusammenfassung
75
dings mit initiierendem Charakter, aber nicht den eigentlichen Steuerfluß. Es ist offensichtlich die Absicht dieser Methode, nur funktionelle, aber nicht ablauforganisatorische Beziehungen zu entwerfen. Diese Beobachtung hängt auch damit zusammen, daß die SADT-Methode den Anspruch erhebt, nicht nur auf Softwaresysteme beschränkt zu sein, sondern ein allgemeines Werkzeug für den Systementwurf zur Verfügung zu stellen. In diesem allgemeinen Kontext sind Funktionseinheiten Systemkomponenten jeglicher Art, die Pfeile ihre interdependenten Beziehungen. Ross gibt an, daß sich diese Methode für den Entwurf von Telefonsystemen, Fabrikorganisationen bis hin zu Finanzsystemen bewährt hat [40, S. 17]. Abbildung 12 bringt dafür ein instruktives Beispiel aus dem Alltagsleben, dem Gemüseanbau im Hausgarten. Aus dieser Darstellung läßt sich erkennen, daß der Vorteil der SADT-Methode darin liegt, eine präzise Analysetechnik anzubieten, um Aktivitäten einer beliebigen Organisation und ihre interdependenten Beziehungen zu gestalten. Sie eignet sich aber nicht so gut für den Detailentwurf eines strukturierten Programms, mit dem die Steuerflußkonstruktion inhärent verbunden ist.
5.3 Zusammenfassung Alle im letzten Kapitel dargestellten Entwurfsmethoden gehen letztlich auf ein und denselben Ansatz zurück, nämlich daß jedes System und damit auch ein Softwareprodukt eine Struktur hat. Sie manifestiert sich in den wechselseitigen Beziehungen einer Menge von Elementen, die einer gemeinsamen Aufgabe dienen [41, S. 9]. Diese Struktur ist das Ergebnis eines von Menschen vollzogenen Gestaltungsprozesses, der, ausgehend von einer Aufgabenstellung, zunächst Teilaufgaben definiert, um sie anschließend in einem System der Aufgabenerfüllung zusammenzufassen. Die verschiedenen Entwurfsverfahren unterscheiden sich allerdings bereits in der Art der Aufgabenanalyse und damit auch in der Gliederung einer Aufgabe in Teilaufgaben. Wir haben diese unterschiedlichen Ausgangspunkte in der nachfolgenden Übersicht (Tabelle 2) stichwortartig zusammengefaßt. Sie lassen sich in zwei Gruppen klassifizieren, nämlich die datenorientierten Verfahren (HIPO, Jackson, Warnier) und die aufgabenorientierten Methoden (Constantine, SADT). Der prozeßorientierte Ansatz von Parnas (s. Kap. 5.1) hat sich in der Praxis bis jetzt nicht durchsetzen können.
76
5. Strukturierter Softwareentwurf
Tab. 2
Charakteristische Merkmale der Softwareentwurfsmethoden.
Entwurfsmethode
Aufgabenanalyse
HIPO
Basierend auf dem Ansatz 0 = P (I) sind die Arbeitsprozesse (P) zu definieren, die aus den Eingabedaten (I) die Ausgabedaten ( 0 ) erzeugen.
Jackson
Datenstrukturen sollen 1:1 auf die Programmstruktur abgebildet werden.
Warnier
Die hierarchische Programmstruktur wird nur aus den Eingabedaten abgeleitet.
Constantine
Abbildung der zu lösenden Aufgabe auf ein System von unabhängigen und einfachen Programmodulen.
SADT
Welche Teilfunktionen soll ein Programmsystem enthalten und nicht wie werden sie implementiert.
In Fachdiskussionen über den strukturierten Entwurf und die strukturierte Programmierung behaupten Praktiker häufig, daß sie schon seit langem mit einer solchen Methode ihre Programme entwerfen. Sie verweisen dabei auf die normierte Programmierung. Die Frage, ob die normierte Programmierung die Merkmale einer strukturierten Programmierung besitzt, soll deshalb jetzt noch kurz untersucht werden. Unter normierter Programmierung wird in der Fachliteratur eine Methode zur einheitlichen Gestaltung jener Teile eines Programms verstanden, die immer dann vorkommen, wenn sequentiell organisierte Eingabedateien zu verarbeiten sind [8, S. 43 f.]. In diesem Verfahren wird die Ablaufsteuerung von den individuellen Arbeitsprozessen separiert. Sie besteht aus vier, sich zyklisch wiederholenden, Phasen: (1) (2) (3) (4)
dem sequentiellen Lesen der Eingabedateien der Auswahl des nächsten Satzes, der zu verarbeiten ist, auf Grund eines vereinheitlichten Ordnungsmerkmals der Gruppenwechselerkennung und der Gruppenwechselverarbeitung, wobei Gruppenwechsel verschiedener Rangfolge auftreten können der Verarbeitung eines in der 2. Phase ausgewählten Satzes zusammen mit der Ausgabe von Ergebnissen.
Es liegt nahe, diese Ablaufsteuerung zusammen mit den Ordnungsmerkmalen der hierbei verwendeten Dateien zu normen. In der BRD versucht das Normblatt DIN 66220, eine solche Vereinheitlichung, unabhängig von schon bestehenden Firmennormen, in die Wege zu leiten [17]. Österle hat daraufhingewiesen, daß vom Programmablauf her gesehen eine Ähnlichkeit zwischen der normierten Programmierung und datenorientierten Entwurfsverfahren, wie der Jackson-
77
5.3 Zusammenfassung
Methode, besteht. Das Normblatt DIN 66220 zeigt aber, daß die Regeln der normierten Programmierung nicht von vornherein identisch sind mit dem Ansatz, datenorientiert Softwareprodukte zu entwerfen [35, S. 253]. So enthalten die im Normblatt DIN 66220 dargestellten Programmablaufpläne viele GOTO-Vei'zweigungen. Auch wird die Schleifenstruktur nicht beachtet. Wie Kapitel 5.2 gezeigt hat, ist in der Praxis mit jedem Entwurfsverfahren auch eine eigene Darstellungsmethode verbunden. Wir haben deshalb in Anlehnung an McGowan die Merkmale der verschiedenen Darstellungs- und Dokumentationsmethoden in einer eigenen Tabelle (Tabelle 3) zusammengefaßt [18, S. 127]. Tab. 3 Darstellungsmittel der Softwareentwuifsmethoden.
Darstellungsmittel der Entwurfsmethode
Programmhierarchie
Darstellbar sind 4 DatenSchnittSteuerhierarchie stellen fluß
Datenfluß
J
N
J
J
J
J
J
N
J
N
Warnier
N
N
N
J
J
Constantine
J
N
J
J
J
SADT
J
J
J
N
J
HIPO Jackson 3
Die Vorteile und Nachteile der einzelnen Softwaredesignmethoden schlagen sich auch in ihren Darstellungsmitteln nieder. Man kann sie deshalb aus der Tabelle 3 noch einmal schlaglichtartig ablesen. Da man in der HIPO-Technik gezwungen ist, den Zusammenhang zwischen Eingabedaten. Arbeitsprozessen und Ausgabedaten explizit zu beschreiben, eignet sie sich vor allem für die Dokumentation der Schnittstellen von Programmsystemen und Programmkomponenten. Hingegen ist die Datenhierarchie nicht darstellbar. Diesen Nachteil vermeidet Jackson, der aber die Schnittstellenproblematik nicht beherrscht. Als einziger der hier aufgeführten Autoren von Softwareentwurfsverfahren hat Warnier versäumt, eine eigene Darstellungsmethode zu entwickeln, so daß man beim Einsatz seines Verfahrens gezwungen ist, auf die Symbolik der Programmablaufpläne zurückzugreifen. Da sie nicht für den strukturierten Softwareentwurf gedacht war, läßt sich sowohl die hierarchische Struktur eines Programms als auch die Datenhierarchie nicht darstellen. Dieses Beispiel zeigt, daß eine Softwareentwurfsmethode ohne eine eigene Darstellungsmethode nicht lebensfähig ist. 3 4
Es wurde als Darstellungsmittel die Symbolik der Programmablaufpläne unterstellt. J: ja, N: nein
78
5. Strukturierter Softwareentwurf
Obwohl Constantine seine Methode im Vergleich zu den anderen hier dargestellten Verfahren am weitesten entwickelt hat, bleibt als schwacher Punkt bei ihm die hierarchische Strukturierung der Daten, die er nicht beherrscht. Diese Beobachtung schmälert aber nicht den Wert seiner Theorie, die auf der sorgfältigen Untersuchung der Eigenschaften von Programmodulen beruht. Ihre Stärke liegt in der breit angelegten Entwurfsmethodik. Schließlich resultiert für die SADT-Methode aus Tabelle 3, daß sie durch ihr spezielles Darstellungsschema den Programmdesigner zwingt, die aufbauorganisatorische Gestaltung eines Programmsystems konsequent top down, hierarchisch und modular durchzufuhren. Hingegen liegt ihre Schwäche in der Gestaltung der Ablauforganisation dieses Programmsystems. In der Tabelle 3 fehlt das Struktogramm nach Nassi-Shneiderman [34, S. 12 ff.], das auch zur Darstellung der Ergebnisse eines strukturierten Entwurfs herangezogen wird [45, S. 63]. Es gibt die Schleifenstruktur von Programmen wieder, wofür bisher im Symbolsatz der Programmablaufpläne ein eigenes Sinnbild fehlte 5 (s. z. B. [15]). Damit ist aber das Struktogramm auf der Ebene der Programmablaufpläne angesiedelt. Es gehört nicht unmittelbar zum Instrumentarium des strukturierten Entwurfs. So schreiben auch Nassi-Shneiderman in ihrer Arbeit: ,,We propose a flowchart language whose control structure is closer to that of languages amenable to structured programming" [34, S. 15]. Alle bisher diskutierten Darstellungs- und Dokumentationsmethoden stellen die Entwurfsergebnisse auf graphischem Wege dar. Das ist anschaulich, aber auch platzaufwendig. Van Leer hat als erster zum Programmentwurf und zu seiner Dokumentation eine ,.Programmentwurfssprache" vorgeschlagen, die mit sprachlichen Ausdrücken Programmstrukturen beschreibt. Ihre Syntax besteht im großen und ganzen aus Sprachelementen höherer Programmiersprachen [48, S. 155 ff.]. Diese Sprache ist allerdings nicht formalisiert und damit auch nicht kompilierbar. Van Leer nennt sie ,.Pseudocode". Sie ist nicht an eine bestimmte Entwurfsmethode gebunden. Beispiel für die Beschreibung der höchsten hierarchischen Stufe eines Programms: Dateieröffnung Setze Endsumme aller Verkaufsbezirke auf Null DO WHILE noch Verkaufsdaten vorliegen Bilde Endsumme für einen Verkaufsbezirk Addiere diese Endsumme zur Endsumme aller Verkaufsbezirke ENDDO Drucke Endsumme Dateischließen 5
Dieses Symbol wurde erst in der Neuausgabe vom September 1977 definiert und eingeführt [16).
5.3 Zusammenfassung
79
Die Syntax dieser Programmentwurfssprache besteht aus den drei Steuerstrukturen der strukturierten Programmierung, wobei Van Leer zwei Versionen anbietet, einen PL/1-orientierten Pseudocode und einen für COBOL-Benutzer. Es soll hier nur der PL/1-Dialekt wiedergegeben werden. Wir bedienen uns zu seiner Notation der genormten Syntaxdarstellung [44, S. 12 ff.], die Van Leer allerdings nicht benutzt. (1)
IF THEN ELSE-Struktur: IF ] [ELSE < arbeitsprozeß >] ENDIF Das Schlüsselwort THEN oder das Schlüsselwort ELSE dürfen unterdrückt werden, beide zusammen nicht. Aus Ubersichtlichkeitsgründen sollen auch alle Syntaxkomponenten, die von PL/1 übernommen wurden, untereinander notiert werden.
(2)
DO WHILE-Struktur: DOWHILE < bedingung > < arbeitsprozeß > . . . ENDDO
(3)
CASE-Struktur: CASE < variable > O F wert ^ : : : = ERÖFFNUNG > < VERARBEITUNG > < SCHLIESSEN > VERARBEITUNG > : : = BEW.STAMMSÄTZE > | < NICHTBEW.STAMMSÄTZE > | BEW.OHNE STAMMS. >
Der Vorteil dieser Notation gegenüber dem Pseudocode wäre darin zu sehen, daß die Methoden generativer Grammatiken für den Softwareentwurf angewendet werden könnten.
6. LITOS (Linzer Technique of Softwaredesign), eine neue Methode des Softwareentwurfs und der Softwaresimulation
6.1 Zielsetzung Der Wert der normierten Programmierung beruht auf der Unterscheidung zwischen statischer und dynamischer Programmstrukturierung. Der statische Aspekt ist durch die Notation eines Programms gegeben, die dem Zweck dient, die zur Lösung einer Aufgabe erforderlichen Anweisungen und Vereinbarungen zusammenzufassen 1 . Die Ausführung eines Programms weicht in der Regel von der sequentiellen Folge der Anweisungen und Vereinbarungen der Programmnotation ab, so daß die Ausfuhrungsfolge eine dynamische Programmstruktur erzeugt, die nicht mehr identisch ist mit der statischen Niederschrift. In dieser Weise unterscheidet sich auch die statische Programmlänge von der dynamischen. Wie die normierte Programmierung gezeigt hat, ist es sinnvoll, diese Eigenschaft schon beim Entwurf eines Programms zu berücksichtigen und zwischen dynamischer Programmstruktur, die durch den Steuerfluß gegeben ist, und Prozeßstrukturierung zu unterscheiden. Wir gehen aber in der LITOS-Methode noch einen Schritt weiter, indem wir die Steuerflußlogik auf ihre Korrektheit überprüfen wollen, bevor der Programmierer mit der Notation der Verarbeitungsanweisungen beginnt. So ist es auch in der Hardwareentwicklung seit langem üblich, im automatisierten Entwurf eine eigene Simulationsphase vorzusehen, in der ein logischer Entwurf durch Simulation auf Fehlerfreiheit überprüft wird. Für diesen Zweck sind in den letzten Jahren eigene Simulationssprachen entwickelt worden. Wir verweisen beispielsweise auf die „Computer Design Language" (CDL) von Dr. Y. Chu, die logische Entwürfe auf der Registertransferebene simuliert [9]. Wir haben uns gefragt, warum bei der Entwicklung von Anwendungssoftware nicht ein ähnlicher Weg eingeschlagen wird, um dem Softwaredesigner schon vor der Implementierung eines Programmproduktes Informationen über seine Funktionsfähigkeit und das Laufzeitverhalten zu liefern. Keine der bisher bekanntgewordenen Entwurfsmethoden enthält einen Teil, der in der Lage ist, Steuerflußstrukturen zu simulieren und maschinell zu überprüfen. Im Grunde genommen zeigt Van Leer mit seinem Pseudocode aber die Richtung auf, in die sich Programmentwurfssprachen weiterentwickeln müssen. Es ist eines der Ziele von LITOS, hierfür einen Beitrag zu liefern und zu zeigen, wie man zu kompilierbaren Programmentwurfssprachen kommen kann. s. Begriffsbestimmung „Programm", nach DIN 44300 „eine zur Lösung einer Aufgabe vollständige Anweisung zusammen mit allen erforderlichen Vereinbarungen" [ 13, S. 5)
6.2 Die vier Entwurfsphasen in LITOS
81
Alle im Kapitel 5.2 beschriebenen Entwurfsmethoden sagen wenig über die Kriterien aus, nach denen top down Teilaufgaben aus übergeordneten Aufgaben abgeleitet werden sollen. Wir versuchen im Rahmen von LITOS, auch für dieses Problem eine Lösung anzubieten. Natürlich werden wir die Erfahrungen, die mit den anderen Methoden bereits gesammelt wurden, synthetisch in LITOS einbringen.
6.2 Die vier Entwurfsphasen in LITOS 6.2.1 Der Grobentwurf Wir verfolgen mit der LITOS-Methode das Ziel, den Softwareentwurf als organisatorische Tätigkeit auszuüben, ohne Kenntnisse einer speziellen problemorientierten Programmiersprache zu besitzen. Dafür sind Organisationsmittel und Organisationsmethoden bereitzustellen. Organisation, als Gestaltungsprozeß verstanden, hat zwei Erscheinungsformen. Die Gliederung einer Aufgabe in Teilaufgaben konstituiert die A «/ZwMorganisation als den Gebilde- und Beziehungszusammenhang der Elemente eines realen Systems. Die Ablauforganisation verkörpert hingegen die raumzeitliche Strukturierung desselben Systems, um Arbeitsprozesse auszuführen [29, S. 59 u. S. 79]. Wir übernehmen diese Differenzierung und erweitern sie im Blick auf den Softwareentwurfsprozeß, für den sowohl die HIPO-Technik als auch die Constantine-Methode ein Zwei-Phasen-Schema in Gestalt des Grobentwurfs und des Feinentwurfs postuliert haben. Wir unterscheiden deshalb in der LITOS-Methode zwischen dem Grobentwurf eines Softwaresystems als der integrativen Strukturierung des Programmaufbaus und dem Feinentwurf als der zeitlichen Abfolge informationeller Teilaufgaben. Im Grobentwurf gestaltet der Systemdesigner die Architektur eines Programmsystems, indem er aus einer vorgegebenen Aufgabe t o p down Funktionseinheiten, die den Charakter von Teilaufgaben haben, nach dem Objektprinzip dekomponiert. Der Datenfluß und der Steuerfluß bleiben in dieser Entwurfphase noch unberücksichtigt. Hierbei soll der Softwaredesigner anstreben, möglichst unabhängige und, soweit es die Aufgabenstellung zuläßt, universelle Funktionen zu definieren. Das Ergebnis dieser gestaltenden Tätigkeit stellt er als Organisationsschaubild informell dar. Im Feinentwurf führt er den Datenfluß und den Steuerfluß ein, so daß sich ein Softwaresystem ergibt, das die Aufgabenstruktur durch eine Prozeßstruktur ergänzt [29, S. 79], Damit diese Ausführungen nicht zu abstrakt bleiben, wollen wir an dieser Stelle bereits mit dem Grobentwurf unseres modellhaften Dateiverarbeitungssystems beginnen. Abbildung 13 zeigt die hierarchisch strukturierte Aufbauorganisation für diese Aufgabe, die sich als Gesamtheit der Teilfunktionen der Dateiverarbeitung darbietet.
82
6. LITOS, eine neue M e t h o d e des S o f t w a r e e n t w u r f s
HIERARCHISCHE STUFE
Abb. 13
Grobentwurf strukturiertes Magnetband-Dateiverarbeitungssystem.
Die hierarchische Stufe ,,0" stellt eine Kurzfassung der Aufgabenstellung dar. Auf der nächsten hierarchischen Stufe wird im Sinne der klassischen Organisationstheorie das Gliederungsmerkmal Objekt zur Strukturierung herangezogen, indem wir nach den beiden Hauptklassen von Programmobjekten differenzieren, nämlich den Vereinbarungen (Vereinbarungssystem) und den Anweisungen (operationales System). Diese Form der Aufgabenanalyse bedeutet aber nicht, daß im operationalen System keine Vereinbarungen zugelassen sind. Der Unterschied ist darin zu sehen, daß im Vereinbarungssystem Arbeitsobjekte globalen Charakters definiert werden, im operationalen System nur noch Daten von lokaler Bedeutung, wie Variable zur Programmsteuerung. Diese Form der Grobstrukturierung des Dateiverarbeitungssystems hat allerdings in der Implementierungsphase zur Folge, daß ein großer Teil der Daten zu globalen Variablen werden, die interne Prozeduren gemeinsam benutzen. Die Fachliteratur lehnt sie teilweise wie GOTO-Anweisungen ab; denn auf diese Weise entsteht eine starre Datenflußkopplung von Programmodulen (s. Kap. 5.2.4.1). Man
6.2 Die vier Entwurfsphasen in LITOS
83
sollte jedoch auch aus dieser Regel kein Dogma machen; denn in stark modularisierten Programmsystemen mit vielen Kommunikationsprozessen ist eine explizite Datenübergabe durch Parameter umständlich zu programmieren und ineffizient. Es wiederholen sich in einzelnen Modulen dieselben Datendeklarationen, was die Fehleranfälligkeit bei der Programmnotation erhöht. Allerdings ist in unserer Strukturierungsstrategie eine genaue „Buchführung" über die einzelnen Arbeitsobjekte und ihre Attribute notwendig, wofür ein eigenes Vereinbarungssystem, wie von uns vorgeschlagen, prädestiniert ist. Auf der hierarchischen Stufe „ 2 " zerlegen wir das operationale System weiter in die drei Hauptfunktionen jeder Dateiverarbeitung, die Arbeiten, die mit dem Eröffnen der Dateien zusammenhängen, die eigentliche Dateiverarbeitung und das Schließen der Dateien bei Programmende [43, S. 130], Auf der nächst niederen hierarchischen Stufe ist nun insbesondere der Programmbaustein „Dateiverarbeitung" weiter in Teilfunktionen aufzulösen. Es ist naheliegend, für diesen Entwurfsprozeß das Mischkriterium der Dateien heranzuziehen, das sequentiell organisierte Dateien in Sätze gruppiert. Aus dem Vergleich der Ordnungsmerkmale der Dateien resultieren somit von selbst die drei Teilaufgaben: (1)
Der Normalfall ist darin zu sehen, daß zu einem Stammsatz in der Bewegungsdatei ein oder mehrere Bewegungssätze vorliegen (bewegter Stammsatz), die den Stammsatz auf den neuesten Stand bringen.
(2)
Zu einem Satz der Stammdatei existiert kein Bewegungssatz (nichtbewegter Stammsatz). Er ist unverändert auf die Ausgabedatei zu übernehmen 1 .
(3)
Dann gibt es schließlich noch den Fall, daß zu einem Ordnungsmerkmal der Bewegungsdatei kein Stammsatz in der Stammdatei vorkommt. In dieser Teilfunktion ist ein neuer Stammsatz anzulegen (Bewegungen ohne
Stammsatz). Bei diesem Grobentwurf eines hierarchisch strukturierten Funktionsplans sind wir objektorientiert vorgegangen, indem wir die Aufgabenstrukturierung auf die Arbeitsobjekte der Dateiverarbeitung (Bewegungssätze und Stammsätze) hin ausrichteten. In dieser Weise wurden aus der zu lösenden Aufgabe Teilaufgaben abgeleitet, ohne die zur Lösung dieser Aufgabe einzusetzenden Arbeitsmittel beim Entwurf zu berücksichtigen. Parnas hat dagegen eine prozeßorientierte Dekomposition der Aufgabenstellung vorgeschlagen [36, S. 1053 ff.], die sich an informationellen Verrichtungen eines Arbeitssubjektes an einem bestimmten Objekt orientiert. Um beide Methoden sinnvoll vergleichen zu können, soll deshalb derselbe Grobentwurf noch einmal prozeßorientiert vorgenommen werden (s. Abbildung 14).
1
Den Fall, daß Stammsätze gelöscht werden, wollen wir hier nicht berücksichtigen.
84
6. L I T O S , e i n e n e u e M e t h o d e d e s S o f t w a r e e n t w u r f s
AUFGABE :
INFORMATIONELLE
SEQUENTIELL
INDEXSEQUENTIELL
DIREKT
Abb. 14 Beispiel eines prozeßorientierten hierarchischen Funktionsplans in der Dateiverarbeitung.
Da prozeßorientierte Funktionspläne von der individuellen Aufgabenstellung abstrahieren, sind sie universeller, was in der Implementierungsphase zu aufwendigeren Programmprodukten mit vielen Modulen und damit einem starken Datenaustausch führt. Dafür sind sie aber auch leichter zu ändern als objektorientierte Funktionspläne, bei denen eine enge Datenflußkopplung zwischen den Funktionseinheiten besteht (s. Kap. 5.1). Aus diesem Vergleich von objektorientierter und prozeßorientierter Funktionalisierung schließen wir, daß es zweckmäßig ist, die höchsten hierarchischen Stufen eines Programmsystems objektorientiert zu definieren und diese Strategie so lange anzuwenden, wie sich ablauforganisatorische Teilaufgaben bilden lassen. Anschließend bestimmt der Programmdesigner prozeßorientiert informationelle Teilaufgaben. Dieser Gestaltungsvorgang endet dort, wo der Programmdesigner einen Arbeitsprozeß nur noch durch ein einziges Verb beschreiben kann, wie im Fall der Dateiverarbeitung die Tätigkeiten Updaten eines Stammsatzes, Druckaufbereitung einer Zeile oder Drucken.
85
6 . 2 D i e v i e r E n t w u r f s p h a s e n in L I T O S
6.2.2 Der Feinentwurf
Steuerfluß
*
Funktionseinheit bzw. Prozedur, die mehrfach iterativ durchlaufen wird (Schleife)
Simultanverarbeitung:
ü Tir
Aufspaltung Abb. 15
Datenfluß
JLJL JLJL I "FHT Sammlung
Synchronisation
LITOS Darstellungsmethode für hierarchisch strukturierte Programmsysteme.
Ausgangspunkt für die zweite Entwurfsphase ist die funktionsorientierte Grobstruktur der Problemlösung. Damit sind die Hauptmodule eines Programmsystems bereits bekannt. Es fallen jetzt zwei Entwurfsaufgaben an. Der Programmdesigner hat den Datenfluß und den Steuerfluß in die Funktionalisierung einzuführen. Außerdem muß er die hierarchische Modularisierung top down weitertreiben, wobei dann die letzten hierarchischen Stufen, wie in Kapitel 6.2.1 dargestellt, prozeßorientiert auszulegen sind. Um eine einheitliche und eindeutige Darstellung der Ergebnisse dieser organisatorischen Tätigkeit zu erreichen, müssen sie in einer vorgegebenen Symbolik dokumentiert werden. Ihre Darstellungsmittel haben wir in Abbildung 15 zusammengestellt.
86
6. LITOS, eine neue Methode des Softwareentwurfs
Die Darstellungstechnik der LITOS-Methode geht vom Begriff der Funktionseinheit aus. DIN 44 300 gibt für sie als begriffsbestimmendes Merkmal sowohl die Teilaufgabe als auch den Teilprozeß an, so daß sie sich als Strukturelement für alle hierarchischen Entwurfsstufen eignet [13, S. 10], Bei der Definition einer Teilaufgabe legen wir noch nicht fest, ob sie eine eigene Prozedur bildet oder nur durch eine Anweisungsfolge innerhalb einer Prozedur realisiert wird. Auch ist es möglich, erst später bei Systemänderungen aus einer Funktionseinheit eine Prozedur abzuleiten. Wie in Organisationsschaubildern, so repräsentieren in der LITOS-Darstellungstechnik einfache Linien funktionelle Abhängigkeiten. Für die Darstellung des Steuerflusses und des Datenflusses übernehmen wir die Pfeile aus der HIPO-Technik, für Schleifen, die mit jedem strukturierten Entwurf inhärent verbunden sind, das Sternsymbol aus der Jackson-Methode. Schließlich verwenden wir noch für simultan ablaufende Funktionen und Prozesse die Darstellungsmittel nach DIN 66001 [15].
(keine Datenweitergabe,z.B. Druckprozeß) Eingabedaten
Ausgabedaten Eingabedaten F12 = Ausgabedaten F11
Eingabedaten F 12 * Ausgabedaten F11 Abb. 16 Schematisches Beispiel füi den Feinentwurf eines hierarchisch strukturierten Funktionsplans.
6.2 Die vier E n t w u r f s p h a s e n in L I T O S
87
D e r F e i n e n t w u r f liefert f o l g e n d e I n f o r m a t i o n e n : Welche F u n k t i o n s e i n h e i t e n h a b e n s t e u e r n d e n C h a r a k t e r ? Wo w e r d e n D a t e n e r z e u g t ? Welche D a t e n b e n ö t i g t eine T e i l a u f g a b e ? Welche D a t e n v e r a r b e i t e t eine F u n k t i o n s e i n h e i t ? A b b i l d u n g 16 d e m o n s t r i e r t diese Aussagen a n e i n e m Beispiel. Die F u n k t i o n s e i n heit F 1 wird d o r t iterativ d u r c h l a u f e n . Sie r u f t n a c h e i n a n d e r F 11 u n d F 12 a u f .
HIERARCHISCHE
STUFE
STAMMSATZ BEWEGUNGSSATZ
GLIEDERUNGSMERKMAL
KONTONUMMER-BEWEGUNG
=
KONTONUMMER-STAMMSATZ ABLAUFORGANISATOR, AUFGABEN STAMM-
ZE1LE,K0NT0NUMMER_STAMMSATZ,
SATZ
SALDOJTAMMSATZ
ik
UPDATEN STAMMSATZ
INFORMATIONELLE
B»
>
DRUCKEN
LESEN
BEWEGUNGSSATZ
BEWEGUNGSSATZ
STAMMSATZ
AUFGABEN D A T E I S C H L I E S S E N (ENDFILE-BEDINGUNG)
GRUPPENWECHSEL (DURCH FLUSS
STEUERGEGEBEN)
DO W H I L E
-> E O F
STAMMDATEN:
CALL
NICHTBEWEGTE STAMMSÄTZE
Abb. 17 Feinentwurf der Teilfunktion „bewegte Stammsätze" der Fallstudie.
88
6. LITOS, eine neue Methode des Softwareentwurfs
Nach j e d e m Schleifendurchlauf geht der Steuerfluß, n a c h d e m F 12 ausgeführt wurde, an F 1 zurück, was nicht explizit dargestellt werden m u ß , da sich dieser Ablauf von selbst aus d e m Schleifencharakter ergibt. Die Ausgabedaten von F 11 gehen als Ganzes in F 12 ein, so daß es sich bei diesen beiden Prozeduren u m sequentiell gebundene Programmodule im Sinne von Constantine handelt. Im unteren Teil dieser Abbildung wird die Alternative dargestellt, daß die Eingabedaten von F 12 nicht identisch sind mit den Ausgabedaten von F 11. Diese Ausführungen zur zweiten Entwurfsphase sollen wiederum an H a n d unserer Fallstudie weiter vertieft werden. Abbildung 17 zeigt den Feinentwurf der T e i l f u n k t i o n „bewegte S t a m m s ä t z e " , wie er sich mit Hilfe der LITOS-Methode ergibt. Diese T e i l f u n k t i o n stellt vom Steuerfluß her gesehen eine Prozedur dar, die einen Stammsatz mit den dazugehörenden Bewegungssätzen verarbeitet. Dieser Programmbaustein besteht auf der nächsten hierarchischen Stufe (,,4") aus vier Funktionseinheiten, die als Ergebnis unserer organisatorischen Tätigkeit selbst Ganzheiten darstellen, zugleich aber durch die gemeinsam zu erfüllende T e i l f u n k t i o n „bewegte S t a m m s ä t z e " ganzheitlich verbunden sind, nämlich: — — — —
der Druckaufbereitung der S t a m m d a t e n d e m Verarbeiten aller Bewegungsdaten eines Stammsatzes d e m Gruppenwechselprozeß bei Änderung des Ordnungsmerkmals in der Bewegungsdatei dem Lesen eines neuen Stammsatzes aus der Stammdatei, n a c h d e m ein Gruppenwechsel s t a t t g e f u n d e n hat.
S t a m m d a t e n sind auch in den beiden Teilfunktionen „Bewegungen ohne Stammsätze" u n d „nichtbewegte S t a m m s ä t z e " auszudrucken. U m die Programmlänge zu verkürzen, wird deshalb die Funktionseinheit „Druckaufbereitung Stammdat e n " als interne Prozedur e n t w o r f e n , auf die d a n n auch diese beiden Teilfunktionen zurückgreifen. Eine ähnliche Feststellung gilt für das Verarbeiten der Bewegungsdaten u n d den Gruppenwechselprozeß, die beide in die Teilfunktion „Bewegungen o h n e S t a m m s ä t z e " eingehen. Da in der Regel mehrere Bewegungssätze je Stammsatz vorliegen, wird die Bewegungsdatenverarbeitung in eine D O WHILE-Schleife inkorporiert. Die K o m p o n e n t e n dieser Schleife sind dann bereits drei prozeßorientierte informationelle Teilaufgaben: — — —
Updaten des Stammsatzes d u r c h einen Bewegungssatz Drucken eines Bewegungssatzes Lesen des nächsten Satzes der Bewegungsdatei.
S t ö ß t die Programmsteuerung in dieser Schleife auf einen Bewegungssatz, dessen K o n t o n u m m e r größer ist als die K o n t o n u m m e r des Stammsatzes, dann verläßt das Programm die DO WHILE-Schleife u n d der Steuerfluß geht automatisch z u m Gruppenwechselprozeß weiter. Z u den Gruppenwechselarbeiten gehören die Aufbereitung der Summenzeile jeder Gruppe, das Drucken dieser Zeile u n d das
6.2 Die vier Entwurfsphasen in LITOS
89
Speichern des auf den neuesten Stand gebrachten Stammsatzes in der Stammdatei. An der Funktionseinheit „bewegte Stammsätze" tritt deutlich der Vorteil des streng an Teilaufgaben orientierten Top-down Entwurfs zutage. Er vermeidet von selbst eine Verquickung der Funktion ..nichtbewegte Stammsätze" mit den Gruppenwechselarbeiten, obwohl beide auf demselben Vergleichsergebnis beruhen (Ordnungsmerkmal der Bewegungsdatei größer als Ordnungsmerkmal der Stammdatei); denn diese zwei Funktionen sind jetzt auf ganz verschiedenen Stellen im Programm und auf verschiedenen hierarchischen Niveaus des funktionellen Entwurfs angesiedelt. Mit Hilfe der Beschriftung an den Datenflußpfeilen kann sich der Leser eines solchen Funktionsplans leicht vorstellen, welche Arbeitsprozesse in den einzelnen Funktionseinheiten ablaufen. So gehen beispielsweise in die „Druckaufbereitung Stammdaten" als Daten eine Zeile, die Kontonummer des Stammsatzes und sein Saldo ein. Da diese Funktionseinheit eine Zeile ausgibt, ist aus dieser Datenkonstellation sofort abzulesen, daß sie eine Zeile zum Druck aufbereitet, indem sie die Kontonummer des Stammsatzes und den Saldo im betreffenden Druckfeld speichert und dabei gegebenenfalls führende Nullen unterdrückt und Beträge durch Radixpunkte ergänzt.
BEWEGUNGSSATZ
HIERARCHISCHE STUFE
KONTONUMMER-BEWEGUNG
ZEILE
ANLEGEN
NEUEN
VERARBEITEN
•
BEWEGUNGSDATEN
STAMMSATZ
EINES
STAMMSATZES
PUFFER_
PUFFER.
STAIWSATZ
STAMHSATZ. ZEILE
Abb. 18
Feinentwuif Dateifunktion „Bewegung ohne Stammsätze" der Fallstudie.
90
6. LITOS, eine neue Methode des Softwareentwurfs
Im Funktionsplan der Abbildung 17 haben wir aus Übersichtlichkeitsgründen auch noch die Endfile-Maßnahmen angedeutet, die am Ende der Bewegungsdatei zu ergreifen sind, obwohl wir sie außerhalb der Prozedur ,,BEWEGTE_ STAMMSAETZE" in einer eigenen Prozedur „DATE1SCHLIESSEN" zusammenfassen. Es sind in diesem Fall noch die Gruppenwechselarbeiten des letzten Stammsatzes durchzuführen, und anschließend ist der Rest der Stammdatei als nichtbewegte Stammsätze zu verarbeiten. In gleicher Weise gehen wir beim Feinentwurf der Teilfunktionen „Bewegungen ohne Stammsätze" (Abbildung 18) und „nichtbewegte Stammsätze" (Abbildung 19) vor. Auch hierbei sind zunächst wieder auf der hierarchischen Stufe .,4" die Stammdaten zum Druck aufzubereiten. Der Unterschied zwischen diesen beiden Teilfunktionen besteht aber darin, daß im Fall der Bewegungen ohne Stammsätze das Programm einen neuen Stammsatz anlegen muß, so daß in die Funktionseinheit „Druckaufbereitung Stammdaten" die Kontonummer des Bewegungssatzes anstelle der Kontonummer des Stammsatzes sowie der Anfangssaldo Null eingeht. Weil der nächste zu verarbeitende Stammsatz aber schon im Hauptspeicher steht (z. B. durch „Lesen Stammsatz" in „bewegte Stammsätze"), ist der Hauptspeicherbereich, in dem ein Stammsatz während der Verarbeitung einer Gruppe zwischengespeichert wird, bereits belegt, so daß das Programm den neuen Stammsatz in einem eigenen Pufferspeicherbereich aufbauen muß. Von hier aus ist die Verarbeitung der Bewegungen ohne Stammsätze funktionell gesehen identisch mit der Teilaufgabe „bewegte Stammsätze", wobei allerdings die Dateneingabe in die Funktionseinheit „Verarbeiten Bewegungsdaten" auch wieder aus dem Pufferspeicher heraus erfolgt. Da der nächste zu verarbeitende Stammsatz bereits aus der Stammdatei gelesen wurde, entfällt außerdem in diesem Fall die Funktionseinheit „Lesen Stammsatz". Wir unterstellen, daß nichtbewegte Stammsätze relativ selten sind. Die Teilfunktion „nichtbewegte Stammsätze" verarbeitet deshalb im Gegensatz zu den beiden anderen Teilfunktionen dieses Programmsystems nur einen Datensatz (s. Abbildung 19). Die Stammdaten sind, so wie sie im Stammsatz stehen, auszudrucken. Dabei ist das Datum aus dem Stammsatz in die Druckzeile zu übertragen. Hingegen stammt dieses Datum, wenn Bewegungssätze vorliegen, aus dem zuerst eingelesenen Bewegungssatz. Die Funktionseinheit „Drucken Summenzeile", die jetzt auf der hierarchischen Stufe „ 4 " aufscheint, ist in den anderen beiden Teilfunktionen Bestandteil des Gruppenwechselprozesses. Auch in diesem Funktionsplan wurden wieder Endfile-Arbeiten dargestellt, und zwar jetzt für das Ende der Stammdatei. Sie sind invers zur Bewegungsdatei auszulegen, indem der Rest der Bewegungsdatei als Bewegungen ohne Stammsätze zu verarbeiten ist. Zusammenfassend läßt sich jetzt im Blick auf die Struktur der Endfile-Arbeiten sagen, daß im Gegensatz zum ersten Programmierversuch, die Dateiverarbeitung strukturiert zu programmieren (Kapitel 4.2), die LITOS-Methode zu einer über-
6.2 Die vier Entwurfsphasen in LITOS
Abb. 19
91
Feinentwurf Dateifunktion „nichtbewegte Stammsätze" der Fallstudie.
sichtlichen und klaren Struktur der Dateiverarbeitungsfunktionen führt, so daß wir schon in der Entwurfsphase die Einwände von Knuth gegen eine GOTO-lose Programmierung entkräftet haben. Programmiertricks, wie eine künstliche Kontonummer .999999', sind überflüssig geworden. Um die Aussagekraft und den Informationsgehalt der LITOS-Darstellungs-Methode beurteilen zu können, wurde in Abbildung 20 die Teilfunktion „bewegte Stammsätze" zusätzlich in der HIPO-Technik dargestellt. Ein Vergleich mit der Abbildung 17 zeigt, daß sie platzaufwendiger ist, insbesondere wegen der INPUTund OUTPUT-Section. Auch nimmt ein HIPO-Diagramm nur Teilprozesse derselben hierarchischen Stufe auf, so daß in Abbildung 20 nur die hierarchische Stufe „4" der Teilfunktion „bewegte Stammsätze" dargestellt werden konnte. Hingegen lassen sich in der LITOS-Methode auf einem DIN A 4-Blatt größere strukturelle Zusammenhänge, die mehrere hierarchische Entwurfsebenen umfassen, darstellen als in der HIPO-Technik, so daß diese Dokumentationsmethode dem Leser einen besseren Einblick in die Gesamtstruktur eines Softwaresystems
92
6. LITOS, eine neue Methode des Softwareentwurfs . . . INPUT
Abb. 20
PROCESS
OUTPUT
Feinentwurf der Dateifunktion „bewegte Stammsätze" als HIPO-Diagramm.
bietet. Auch tritt der Steuerfluß klarer hervor als in der HIPO-Technik. Für die HIPO-Diagramme spricht der Zwang, alle Eingabe- und Ausgabedaten eines Prozesses spezifizieren zu müssen, eine Methodik, die aus dem Aufbau des HIPODiagramms resultiert. Deshalb ist dort die Wahrscheinlichkeit, daß der Programmdesigner vergißt, Daten anzugeben, kleiner als in der LITOS-Methode. Zum Abschluß dieses Kapitels wollen wir festhalten, daß der Feinentwurf eines Softwareproduktes nicht nur die Grundlage seiner Programmierung ist, sondern daß er auch bereits die Programmdokumentation verkörpert, die der Anwender für die Wartung dieses Programmproduktes benötigt. Wenn in der Praxis darüber geklagt wird, daß Programme schwer wartbar sind, dann hängt es auch damit zusammen, daß häufig erst am Ende eines Programmierprojektes Programme dokumentiert werden. Zu diesem Zeitpunkt fehlt aber häufig den Programmierern die Zeit, um die Programmdokumentation sorgfältig durchführen zu können; denn
6.2 Die vier Entwurfsphasen in LITOS
93
sie sind bereits mit neuen Programmierprojekten befaßt. Es ist deshalb eine Form der Dokumentation anzustreben, die parallel zur Entwurfs- und Programmierphase abläuft und quasi von selbst alle Ergebnisse dokumentiert. 6.2.3 Die Ableitung der Programmsteuerung aus dem Feinentwurf In der Programmierung war es bisher üblich, unmittelbar nach der Systemanalyse mit der Programmierung selbst zu beginnen. Dieser Arbeitsstil hat den Nachteil, daß der Programmierer die Korrektheit der Systemanalyse nicht überprüfen kann. Deshalb wird auch in der Hardwareentwicklung seit langem im Anschluß an den logischen Entwurf eines Computers vor seiner Implementierung eine Simulationsphase zwischengeschaltet, um in dieser Weise Entwurfsfehler aufzudecken, bevor sie in Hardwarekomponenten Eingang gefunden haben. Demselben Zweck dienen die dritte und vierte LITOS-Phase, indem dort der Programmdesigner aus dem Feinentwurf eines Programmsystems seine Steuerungslogik ableitet, um sie anschließend simulieren zu können. Unter Steuerungslogik verstehen wir dabei die Folge aller Steuerungsanweisungen eines Programms zusammen mit denjenigen Prozessen, die Steuerungsvariable erzeugen und verarbeiten. In unserer Fallstudie ist die Steuerungslogik vor allem auf das operationale System beschränkt, wobei für die Simulation nur die beiden Hauptfunktionen ,.Dateiverarbeitung" und „Dateischließen" relevant sind. Diese Steuerungslogik basiert auf den Ordnungsmerkmalen der Dateien, aus deren Vergleich die drei Teilfunktionen bewegte Stammsätze, nichtbewegte Stammsätze und Bewegungen ohne Stammsätze resultieren. Den Steuerfluß konstituieren deshalb vor allem die Prozesse, die diese Ordnungsmerkmale erzeugen, nämlich das Lesen der Bewegungsdatei und der Stammdatei. Da in die Schleifensteuerung außerdem aber auch die Endfile-Bedingungen eingehen, sind sie ebenfalls Teil der Steuerungslogik. Abbildung 21 und 22 zeigen die Struktur der Steuerungslogik für unsere Fallstudie, wobei steuerungsrelevante Daten mit Großbuchstaben gekennzeichnet wurden. Um zu erreichen, daß am Ende der Bewegungsdatei oder der Stammdatei die Programmausführung aus der Dateiverarbeitung automatisch zur Hauptfunktion „Dateischließen" weitergeht, werden in die Schleifensteuerung der Prozedur „Dateiverarbeitung" jetzt im Gegensatz zur Lösung im Kapitel 4.2 beide Endfile-Bedingungen aufgenommen und konjunktiv verbunden. Abbildung 22 zeigt anschaulich die Dualität der Endfile-Maßnahmen je nachdem, ob die Bewegungsdatei oder die Stammdatei früher ihr Ende erreicht hat. Wir behaupten, daß eine solche klare Lösung nur aus einem systematischen Entwurf resultiert, wie er durch die LITOS-Methode erzwungen wird. Was für die Simulation des Programmsystems als Ganzes konstatiert wurde, gilt natürlich auch für die einzelnen Funktionseinheiten des Feinentwurfs. Das bedeutet, daß wir in den Simulationsprozeß nur die Funktionseinheiten aufnehmen,
94
6. LITOS, eine neue Methode des S o f t w a r e e n t w u r f s
KOiffO.WfflERJEWEGU.'iG DATEIVERARBEITUNG
(ß)
*
(ENDFILEJEDINGUNGJEWEGUNGSDATEM.
KOrlTONU MMER_ST AMMSATZ
EINGABE = 'O'B & ENDFILE_BEDINGUNG_ STAHMDATEi'LE INGABE = 'O'B) KONTONUMMER_BEWEGUNG KOiJTONUMMER_STAMMSATZ
BEHEGTE
STAMMSÄTZE
*
(KOiTüNUMMERJiEWEGUriG =
INICHTBEWEGTE
STAMMSÄTZE
BEWEGUNGEN OHNE
DRUCKPROZESS
STAMMSATZ
(KONTONUMMERJEWEGUNG J . =
KOrtTOKMER_STAIflSATZ & L E S E N S T A M M S A T Z e;wfile_bedi;jgung_ (KGNTOiJUMMER_STAff1SATZ)
STAMMSÄTZE*
KONTONUMMERJEWEGUNG &
BEWE6UrtGSDATEiM_
ENDFILEJEDINGUNG_ BEWEGUNGSDATEN_
EINGABE = 'O'B)
EINGABE = 'O'B)
PROZESS LESEN
BEWEGUNGSSATZ
(KOiiTüiWMMERJEWEGUNG) EIJD
SCHLEIFE
GRUPPENWECHSELPROZESS LESEN
PROZESS LESEN
BEWEGUNGSSATZ
(KONTONUMMERJEWEGUNG) END
SCHLEIFE
GRUPPENWECHSELPROZESS
STAMMSATZ
(KU,JTOilUflMEK_STAmSATZ) Abb. 21 Steuerungslogik Funktion „Dateiverarbeitung" der Fallstudie.
Abb. 22 Steuerungslogik Funktion „Dateischließen" der Fallstudie.
6 . 2 Die vier E n t w u r f s p h a s e n in LITOS
95
die Bewegungs- oder Stammdaten lesen und auf der Grundlage dieser Ordnungsmerkmale Programmschleifen steuern. Alle anderen Funktionen deuten wir nur durch textliche Hinweise an, wie beispielsweise „Druckprozeß Stammsatz" oder ..Gruppenwechselprozeß". 6.2.4 Simulation und Test der Programmsteuerung In der vierten Entwurfsphase ist die Steuerungslogik in ein Simulationsprogramm umzusetzen, um sie maschinell austesten zu können. Wir haben schon darauf hingewiesen, daß, im Gegensatz zur Hardwaresimulation, bisher für die Simulation der Steuerungslogik von Programmsystemen keine eigenen Simulationssprachen entwickelt wurden. In dieser Situation gibt es drei Möglichkeiten, die Korrektheit eines Programms durch Simulation zu überprüfen: (1)
Es wird eine eigene kompilierbare Entwurfssprache definiert, etwa in Anlehnung an den Pseudocode. Dazu wäre es allerdings notwendig, entweder einen eigenen Kompilierer zu konstruieren oder vorhandene Kompilierer abzuändern.
(2)
Der Programmdesigner greift auf einen vorhandenen Kompilierer zurück, indem er ein Simulationsprogramm in einer universellen höheren Programmiersprache mit Simulationsfähigkeiten schreibt. Für unsere Fallstudie, die Dateiverarbeitung, ist dafür der Listenverarbeitungsteil von PL/1 besonders geeignet [44, S. 85 ff.]; denn jede Datei läßt sich als lineare Liste interpretieren. Da in diesem Fall Simulationssprache und Sprache des Quellprogramms identisch sind, kann das Simulationsprogramm mit weniger Programmieraufwand in das Quellprogramm umgesetzt werden als im Fall einer eigenen Entwurfssprache.
(3)
Der Programmdesigner entwirft den Steuerungsteil eines Programms, indem er die von einer höheren Programmiersprache zugelassenen Steuerungsanweisungen dafür benutzt. Es existiert ein Preprozessor für diese Programmiersprache, der die Simulationsanweisungen in Sprachelemente der höheren Programmiersprache, d. h. im Fall von PL/1 der Listenverarbeitung, übersetzt.
Die dritte Variante soll noch mit einem kleinen Beispiel belegt werden. Wir gehen davon aus, daß ein Programmierer folgende READ-Anweisung notiert hat, um das Lesen einer Bewegungsdatei zu simulieren: READ FILE (LEINGAB) INTO (BEWEGUNGSSATZ); Der Preprozessor übersetzt sie in die beiden Listenverarbeitungsanweisungen: P = P -> Z E I G E R B E W E G U N G ; BEWEGUNGSSATZ = P BEWEGUNG; Diese beiden Anweisungen haben folgende Bedeutung. In der Listenverarbeitung
96
6. LITOS, eine neue Methode des Softwareentwurfs
speichern wir die Bewegungsdatei und die Stammdatei als Liste. Jedes Listenelement besteht dabei aus Nutzdaten, nämlich einem Ordnungsmerkmal, und einem Zeiger auf das nächste Listenelement. Um ein Element der Bewegungsdatei lesen zu können, ist es deshalb notwendig, zunächst den gespeicherten Zeiger, der auf dieses Listenelement zeigt, zu lesen. Diese Funktion übernimmt die erste Anweisung, indem in ihr einem Hilfszeiger ,,P" der Wert des Zeigers „ZEIGER BEWEGUNG", der auf das angeforderte Listenelement zeigt, zugewiesen wird. Anschließend überträgt die zweite Anweisung mit Hilfe des Zeigers „P", zeigend auf das Nutzdatenelement „BEWEGUNG", den Wert aus diesem Listenelement in eine Variable „BEWEGUNGSSATZ". Die nachfolgende Skizze stellt diese Situation schematisch dar.
BEWEGUNG (Nutzdaten)
ZEIGERBEWEGUNG-
BEWEGUNG Z E I G E R BEWEGUNG-
P (Stellung von " P " in der ersten Anweisung)
Das Programm „MAGNETBAND_ DATEIVERARBEITUNG VERSION_2_ STEUERUNGSLOGIK" zeigt nun die vollständige Simulation des strukturierten Entwurfs der Magnetbanddateiverarbeitung. In den beiden Dateien, der Bewegungsdatei und der Stammdatei, werden nur die Ordnungsmerkmale von Datensätzen simuliert, repräsentiert durch ihre Kontonummer, nicht aber Nutzdaten, beispielsweise verkörpert durch Beträge. Die Ordnungsmerkmale wurden so gewählt, daß sämtliche drei Teilfunktionen vorkommen. So werden „bewegte Stammsätze" durch die Kontonummern 1, 2 und 9 simuliert, „nichtbewegte Stammsätze" durch 4 und 11 und „Bewegungen ohne Stammsätze" durch die Ordnungsmerkmale 3 , 1 2 und 15. Nachdem die Stammdatei das Ordnungsmerkmal 11 gelesen hat, tritt dort die Endfile-Bedingung auf, so daß die Prozedur „DATEISCHLIESSEN" die Bewegungssätze 12 und 15 als „Bewegungen ohne Stammsätze" verarbeitet. Wegen der Endfile-Bedingung und der Gruppenwechselarbeiten muß der Programmdesigner beim Test der Steuerungslogik selbstverständlich verschiedene Datenkonstellationen simulieren. Wir haben in unserem Beispiel nur eine Variante angegeben. Zu diesem Zweck kann man den Programmteil, der die Dateien simuliert, als Unterprogramm entwerfen. Der Programmdesigner kann nun anhand der Ordnungsmerkmale leicht feststellen, ob die Reihenfolge der verarbeiteten Datensätze der Programmlogik entspricht.
6.2 Die vier Entwurfsphasen in LITOS
97
MAGNETBAND_DATEIVERARBEITUNG_VERSION_2_STEUERUNGSLOGIK
:
PROC O P T I O N S ( M A I N ) ; y********************************************************/ /********* /*
SIMULATION DATEIEN DURCH L I S T E N SIMULATION BEWEGUNGSDATEI
DCL 1 BEWEGUNGSSATZ BASED
*/
(ZEIGER_1),
2 KONTONUMMER_BEWEG CHAR 2 ZEIGER_BEWEGUNG /*
**************/
(6)f
POINTER,
SIMULATION VON ORDNUNGSMERKMALEN WORT
(10)
CHAR
INIT
*/
(6)
( ' 1 ' , ' 2 ' , ' 2 ' , ' 3 ' , ' 3 ' , ' 3 ' , ' 9 ' , •12','15','15'),
( HEAD_1,P,Q /*
)
POINTER;
AUFBAU BEWEGUNGSDATEI ALLOCATE
*/
BEWEGUNGSSATZ;
P,HEAD_1
=
ZEIGER_1;
KONTONUMMER_BEWEG = WORT DO I
= 2 TO
(1); 10;
ALLOCATE BEWEGUNGSSATZ ; KONTONUMMER_BEWEG = WORT P ->
(I);
ZEIGER_BEWEGUNG, P =
ZEIGER_1;
END; P -> /*
ZEIGER_BEWEGUNG = NULL
SIMULATION STAMMDATEI
DCL 1 STAMMSATZ BASED
/*
(6),
POINTER,
SIMULATION VON ORDNUNGSMERKMALEN W0RT_1
(5)
INIT HEAD_2 /*
*/
(ZEIGER_2),
2 KONTONUMMER CHAR 2 ZEIGER_STAMM
();
CHAR ,
(6)
( 1 ' , 2 , , , 4 , , , 9 , , , 1 1 , )
,
POINTER;
AUFBAU STAMMDATEI ALLOCATE
*/
*/
STAMMSATZ;
/
98
6. LITOS, eine neue M e t h o d e des S o f t w a r e e n t w u r f s . . . P,HEAD_2
=
ZEIGER_2;
KONTONUMMER = WORT_1 DO I ALLOCATE
=
2 TO
(1 ) ;
5;
STAMMSATZ;
KONTONUMMER = WORT_1 P
->
ZEIGER_STAMM,P
=
(I);
ZEIGER_2;
END; P -> /*********
Z E I G E R _ S T A M M = NULL
SIMULATION
DCL
();
DATEIEROEFFNUNG
******************/
(KONTONUMMER_BEWEGUNG,KONTONUMMER_STAMMSATZ) CHAR P =
(6);
HEAD_1;
KONTONUMME R_BEWE GUN G = P - > Q =
KONTONUMMER_BEWEG;
HEAD_2;
KONTONUMMER_STAMMSATZ = Q - > KONTONUMMER; /*******************************************************/ CALL
DATEIVERARBEITUNG;
CALL D A T E I S C H L I E S S E N ; /*******************************************************/ DATEIVERARBEITUNG
:
PROC; DO W H I L E
/*
(P
- , = NULL
()
& Q -i= NULL
P = NULL S I M U L I E R T
EOF
Q = NULL S I M U L I E R T
E O F STAMMDATEI
IF
BEWEGUNGSDATEI, */
KONTONUMMER_BEWEGUNG
=
KONTONUMME R_STAMMSAT Z THEN CALL IF
BEWEGTE_STAMMSAETZE;
KONTON UMME R_BEWE GUNG > KONTONUMME R _ S TAMMSATZ
THEN CALL IF
NICHTBEWEGTE_STAMMSAETZE;
(KONTONUMMER_BEWEGUNG KONTONUMMER_STAMMSATZ)
& (P
n = NULL
())
ZEIGER_BEWEGUNG; /* SIMULATION EOF_BEDINGUNG */ IF P = NULL () THEN GOTO END_ BEWEGUNGSSCHLEIFE; KONTONUMMER_BEWEGUNG = P -> KONTONUMMER_BEWEG;
END_BEWEGUNGS SCHLEIFE :
END;
PUT SKIP (2) LIST ('GRUPPENWECHSEL') /* SIMULATION LESEN STAMMSATZ */ Q = Q -> ZEIGER_STAMM; /* SIMULATION EOF_BEDINGUNG */ IF Q = NULL () THEN GOTO END_BEWEGTE_ STAMMSAETZE; KONTONUMMER_STAMMSATZ = Q -> KONTONUMMER; END_BEWEGTE_STAMMSAETZE : END BEWEGTE_STAMMSAETZE; /********************************************************/ NICHTBEWEGTE_STAMMSAETZE : PROC; PUT SKIP (3) LIST ('NICHTBEWEGTER_STAMMSATZ = ',KONTONUMMER_STAMMSATZ); /* SIMULATION LESEN STAMMSATZ */ Q = Q -> ZEIGER_STAMM; /* SIMULATION EOF_BEDINGUNG */ IF Q = NULL () THEN GOTO END_NICHTBEWEGTE_STAMMSAETZE; KONTONUMMER_STAMMSATZ = Q -> KONTONUMMER; END_NICHTBEWEGTE_STAMMSAETZE : END NICHTBEWEGTE_STAMMSAETZE; /********************************************************/ BEWEGUNGEN_OHNE_STAMMSAETZE : PROC; DCL PUFFER KONTONUMMER STAMMSATZ CHAR (6);
6.2 Die vier Entwurfsphasen in LITOS
101
P UFFE R_KONTONUMME R_STAMMSAT Z =
KONTONUMMER_ BEWEGUNG;
/*
SIMULATION DO WHILE
VERARBEITEN
BEWEGUNGSDATEN
(KONTONUMMER_BEWEGUNG KONTONUMMER_STAMMSATZ PUT
=
*/
PUFFER_ & P
-i= NULL
());
SKIP LIST
( 1 BEWEGUNGEN_OHNE_STAMMSAETZE 1
=
,KONTONUMMER_BEWEGUNG,
•PROZESS'); /*
SIMULATION
LESEN P
/*
SIMULATION IF
= P
BEWEGUNGSSATZ ->
ZEIGER_BEWEGUNG;
EOF_BEDINGUNG P = NULL
*/
()
*/
THEN GOTO
KONTONUMMER_BEWEGUNG
= P
END_SCHLEIFE; ->
KONTONUMMER_BEWEG; END_SCHLEIFE
:
END; PUT S K I P
(2)
LIST
('GRUPPENWECHSEL');
END BEWEGUNGEN_OHNE_STAMMSAETZE; /********************************************************/ END
MAGNETBAND_DATEIVERARBEITUNG
VERSION_2_STEUERUNGSLOGIK;
Auf einige Besonderheiten dieses Simulationsprogramms ist noch hinzuweisen. Die Eröffnung der Dateien simulieren wir in der Weise, daß das Programm den beiden Hilfszeigern „P" und „Q" die Adressen der beiden gespeicherten Listen „BEWEGUNGSSATZ" und „STAMMSATZ" („HEAD 1" und „HEAD 2") zuweist. Dieselben Hilfszeiger benutzen wir für das Lesen der BewegungsJatei und der Stammdatei. Sobald ein Hilfszeiger dabei den Wert ,,Null" annimmt, hat die Programmausführung das Dateiende erreicht, so daß sie automatisch durch die Anweisung „CALL DATEISCHL1ESSEN" zu den Dateiendearbeiten weitergeht. Dabei ist zu berücksichtigen, daß die nächste Zuordnungsanweisung „KONTONUMMER BEWEGUNG = P KONTONUMMER BEWEG" bzw. „KONTONUMMER STAMMSATZ = Q ->• KONTONUMMER" nicht mehr ausführbar ist. Die Programmausführung muß diese Anweisung deshalb im Fall des Listenendes überspringen. Diesen Ablauf haben wir mit einer GOTO-Anweisung 2 programmiert (z. B. „IF P = NULL( ) THEN GOTO END BEWEGUNGS SCHLEIFE"). Hierbei liejt die Marke, zu der die Programmausführung durch die 2
nur aus didaktischen Gründen
102
6. LITOS, eine neue Methode des Softwareentwurfs
GOTO-Anweisung verzweigt, in unmittelbarer Nähe der Programmverzweigung, so daß die Einwände von Dijkstra gegen die GOTO-Anweisung nicht gelten. Auch dieses Beispiel zeigt noch einmal, wie falsch es wäre, aus der Vermeidung von GOTO-Anweisungen ein Dogma zu machen. Abbildung 23 zeigt schließlich das Ergebnis des Simulationslaufs. Die Folge der Ordnungsmerkmale zusammen mit den Arbeitsprozessen, insbesondere dem Gruppenwechselprozeß, ist aus diesem Druckbild leicht zu erkennen und damit auch leicht zu überprüfen. Dieses Verfahren eignet sich grundsätzlich auch für den Programmentwurf im Dialogverkehr über Bildschirme und wird dort besonders interessant, weil es die Testphase abkürzt.
6.2 Die vier Entwurfsphasen in LITOS
10.-5
¡75 C/J w N O Oí 0-
CO w N O Oí cu
Ii N H
= NULL
()
& Q -i= NULL
())
/ * P = NULL SIMULIERT EOF BEWEGUNGSDATEI, Q = NULL SIMULIERT EOF STAMMDATEI CALL
*/
STEUERUNGSTEIL;
CALL
DRUCKAUFBEREITUNG_STAMMDATEN;
CALL
BEWEGUNGSDATENVERARBEITUNG;
CALL
SUMMENTEIL; END DRUCKGRUPPE;
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * • » ! * * * * * * * * * * * * * * * * * *
EOF_ARBEITEN
: PROC;
PUT SKIP
(5)
I F P = NULL
LIST ()
('EOF_ARBEITEN');
THEN DO;
KONTONUMMER_BEWEGUNG = DO WHILE
(Q -.= NULL
'999999';
());
CALL
STEUERUNGSTEIL;
CALL
DRUCKAUFBEREITUNG_STAMMDATEN;
CALL
BEWEGUNGSDATENVERARBEITUNG;
CALL SUMMENTEIL; END; END; I F Q = NULL
()
THEN DO;
KONTONUMMER_STAMMSATZ = DO WHILE
(P -i= NULL
' 999999 ' ;
());
CALL
STEUERUNGSTEIL;
CALL
DRUCKAUFBEREITUNG_STAMMDATEN;
CALL
BEWEGUNGSDATENVERARBEITUNG;
124
6. LITOS, eine neue Methode des Softwareentwurfs . . . CALL SUMMENTEIL; END; END; END EOF_ARBEITEN;
/*******************************************************/
STEUERUNGSTEIL :
PROC;
IF KONTONUMMER_BEWEGUNG >= KONTONUMMER_STAMMSATZ THEN DO; STAMMSATZ_VORHANDEN = 11'B; IF KONTONUMMER_BEWEGUNG
= KONTONUMMER_STAMMSATZ
THEN BEWEGTE_STAMMDATEN = '11B; ELSE BEWEGTE_STAMMDATEN = 'O'B; END; ELSE STAMMSATZ_VORHANDEN = 'O'B; END STEUERUNGSTEIL; /*******************************************************/ DRUCKAUF BEREITUNG_STAMMDATEN :
PROC;
PUT SKIP (2) LIST ('DRUCKAUFBEREITUNG_ STAMMDATEN'); /* FUNKTIONSEINHEIT KONTO_NR */ IF STAMMSATZ_VORHANDEN THEN DO; Z EILE_KONTONUMMER = KONTONUMMER_STAMMSATZ; PUT SKIP (2) LIST ('ZEILE_KONTONUMMER = Z EILE_KONTONUMMER) ; END; ELSE DO; ZWISCHENSPEICHER_KONTONUMMER,ZEILE_KONTONUMMER, PUFFER_STAMMSATZ_KONTONUMMER = KONTONUMMER_BEWEGUNG; PUT SKIP (2) LIST ('ZEILE_KONTONUMMER = ZEILE_KONTONUMMER); END; /* FUNKTIONSEINHEIT ALTER SALDO */ IF STAMMSATZ_VORHANDEN THEN PUT LIST ('ALTER SALDO KONTONUMMER =
KONTONUMMER STAMMSATZ);
6.4 Ein Vergleich der LITOS-Methode mit der Jackson-Methode ELSE PUT LIST (1ALTER_SALDO = 0') ; /* FUNKTIONSEINHEIT DATUM */ PUT LIST ('DATUM'); END DRUCKAUFBEREITUNG_STAMMDATEN; /****************************************************** BEWEGUNGSDATENVERARBEITUNG : PUT SKIP (2) LIST
PROC;
('BEWEGUNGSDATENVERARBEITUNG1);
IF BEWEGTE_STAMMDATEN = '11B | STAMMSATZ_VORHANDEN = 'O'B THEN DO; IF STAMMSATZ_VORHANDEN = 'O'B THEN DO; DO UNTIL (ZWISCHENSPEICHER_KONTONUMMER -.= KONTONUMMER_BEWEGUNG | P = NULL ()) PUT SKIP LIST ('BEWEGUNGEN OHNE STAMMSAETZE', KONTONUMMER_BEWEGUNG); PUT SKIP LIST ('UPDATEN STAMMSATZ', 'DRUCKAUFBEREITUNG','DRUCKEN') ZWISCHENSPEICHER_KONTONUMMER = KONTONUMMER_ BEWEGUNG; /* LESEN BEWEGUNGSSATZ */ P = P -> ZEIGER_ BEWEGUNG; IF P = NULL () THEN GOTO END_EINS_SATZ; KONTONUMMER_BEWEGUNG = P -> KONTO NUMMER_BEWEG; END_EINS_SATZ :
END; END; ELSE DO;
DO UNTIL (ZWISCHENSPEICHER_KONTONUMMER -.= KONTONUMMER_BEWEGUNG
| P = NULL ());
PUT SKIP LIST ('BEWEGTE STAMMSAETZE', KONTONUMMER BEWEGUNG);
126
6. LITOS, eine neue Methode des Softwareentwurfs . . . PUT SKIP LIST ('UPDATEN STAMMSATZ', 'DRUCKAUFBEREITUNG 1 , 'DRUCKEN') ; ZWISCHENSPEICHER_KONTONUMMER = KONTONUMMER_ BEWEGUNG; /* LESEN BEWEGUNGSSATZ */ P = P -> ZEIGER_ BEWEGUNG; IF P = NULL () THEN GOTO END; KONTONUMMER_BEWEGUNG = P -> KONTO NUMMER_BEWEG; END;
END :
END; END; ELSE PUT SKIP LIST ('BEWEGUNGSDATENZEILE LEER'); END BEWEGUNGSDATENVER ARBEITUNG; /it******************************************************/ SUMMENTEIL :
PROC;
PUT SKIP (2) LIST ('SUMMENTEIL1); IF BEWEGTE_STAMMDATEN = " T B | STAMMSATZ_VORHANDEN = 'O'B THEN DO; IF STAMMSATZ_VORHANDEN = 'O'B THEN PUT SKIP LIST ('DRUCKEN UND SCHREIBEN STAMMSATZ AUS PUFFER SPEICHER',ZWISCHENSPEICHER_KONTONUMMER); ELSE PUT SKIP LIST ('DRUCKEN UND SCHREIBEN STAMMSATZ AUS STAMMSATZ', KONTONUMMER_STAMMSATZ); END; ELSE PUT SKIP LIST ( 'NICHTBEWEGTER
STAMMSATZ',KONTONUMMER_STAMMSATZ) ;
IF STAMMSATZ_VORHANDEN = '1'B THEN DO; /* LESEN STAMMSATZ */
6.4 Ein Vergleich der LITOS-Methode mit der Jackson-Methode Q = Q -> ZEIGER_STAMM; IF Q = NULL () THEN GOTO END_1; KONTONUMMER_STAMMSATZ = Q -> KONTONUMMER; END; END_1 :
END SUMMENTEIL;
/******************************************************
END MAGNETBAND_DATEIVERARBEITUNG_JACKSON_METHODE_ STEUERUNGSLOGIK; MAGNETBAND_DATEIVERARBEITUNG_JACKSON_METHODE : PROC OPTIONS (MAIN); /* DATEIVEREINBARUNGEN, VEREINBARUNG BEWEGUNGSSATZ, STAMMSATZ, UEBERSCHRIFT, ZEILE,ZEILE_1 IDENTISCH MIT PROGRAMM MAGNETBAND_DATEIVERARBEITUNG_STRUKTURIERT VERSION_2 */ DCL
ZWISCHENSPEICHER_KONTONUMMER CHAR (6);
/**** VEREINBARUNGEN ZUR PROGRAMMSTEUERUNG ****/ DCL
(STAMMSATZ_VORHANDEN,BEWEGTE_STAMMDATEN) BIT (1) INIT ('01B);
DCL
(ENDFILE_BEDINGUNG_STAMMDATEN_EINGABE, ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE) BIT (1) INIT ('O1B);
/*************** ENDE VEREINBARUNGSSYSTEM ************* ON ENDFILE (LEINGAB) ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE = '1'B; ON ENDFILE (MEINGAB) ENDFILE_BEDINGUNG_STAMMDATEN_EINGABE = '1'B; /*************** OPERATIONALES SYSTEM *****************
1
128
6. LITOS, eine neue Methode des Softwareentwurfs . . . CALL UEBERSCHRIFTSZEILE;
CALL DRUCKGRUPPE; CALL EOF_ARBEITEN; /*******************************************************/ UEBERSCHRIFTSZEILE :
PROC; OPEN FILE (LEINGAB), FILE (MEINGAB), FILE (MAUSGAB), FILE (LISTE);
/* DRUCKEN UEBERSCHRIFTSZEILE,1 LEERZEILE */ WRITE FILE (LISTE) FROM (UEBERSCHRIFT); ZEILE_1 = '0'; WRITE FILE (LISTE) FROM (ZEILE); READ
FILE (LEINGAB) INTO
READ
FILE (MEINGAB) INTO
(BEWEGUNGSSATZ); (STAMMSATZ); END UEBERSCHRIFTSZEILE; /*******************************************************/ DRUCKGRUPPE : DO WHILE
PROC; (ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_ EINGABE =
1
O1B &
ENDFILE_BEDINGUNG_STAMMDATEN_ EINGABE = '0'B); CALL STEUERUNGSTEIL ; CALL D RUCKAUF BE REITUNG_STAMMDATEN ; CALL BEWEGUNGSDATENVERARBEITUNG ; CALL SUMMENTEIL ; END; END DRUCKGRUPPE; /*******************************************************/ EOF_ARBEITEN :
PROC; IF ENDFILE BEDINGUNG
6.4 Ein Vergleich der LITOS-Methode mit der Jackson-Methode
129
BEWEGUNGSDATEN_ EINGABE = '1'B THEN DO; KONTONUMMER_BEWEGUNG = ' 999999 ' ; DO WHILE
(ENDFILE_BEDINGUNG_
STAMMDATEN_ EINGABE = '01B); CALL STEUERUNGSTEIL ; CALL DRUCKAUFBEREITUNG_ STAMMDATEN ; CALL BEWEGUNGSDATEN VERARBEITUNG ; CALL SUMMENTEIL ; END; END; IF ENDFILE_BEDINGUNG_STAMMDATEN_ EINGABE =
1
1'B THEN DO;
STAMMSATZ.KONTONUMMER = '999999' ; DO WHILE (ENDFILE_BEDINGUNG_ BEWEGUNGSDATEN_ EINGABE = 10'B); CALL STEUERUNGSTEIL ; CALL DRUCKAUFBEREITUNG_ STAMMDATEN ; CALL BEWEGUNGSDATEN VERARBEITUNG ; CALL SUMMENTEIL ; END; END; END EOF_ARBEITEN; /+*****************************+************************/ STEUERUNGSTEIL :
PROC;
IF KONTONUMMER BEWEGUNG >= STAMMSATZ.KONTONUMMER
130
6. LITOS, eine neue M e t h o d e des S o f t w a r e e n t w u r f s . . .
THEN DO; STAMMSATZ_VORHANDEN = '1'B; IF KONTONUMMER_BEWEGUNG
= STAMMSATZ.KONTONUMMER
THEN BEWEGTE_STAMMDATEN = ' 1 ' B ; ELSE BEWEGTE_STAMMDATEN = 'O'B; END; ELSE STAMMSATZ_VORHANDEN = 'O'B; END STEUERUNGSTEIL; /****************************************************** DRUCKAUFBEREITUNG_STAMMDATEN :
/
PROC ; ZEILE_1 = '0';
/* FUNKTIONSEINHEIT KONTO_NR */ IF STAMMSATZ_VORHANDEN THEN ZEILE.KONTONUMMER = STAMMSATZ. KONTONUMMER; ELSE ZWISCHENSPEICHER_KONTONUMMER, ZEILE.KONTONUMMER, PUFFER_STAMMSATZ. KONTONUMMER = KONTONUMMER_ BEWEGUNG; /* FUNKTIONSEINHEIT ALTER SALDO */ IF STAMMSATZ_VORHANDEN THEN CALL DRUCKAUFBEREITUNG_SALDO (STAMMSATZ.SALDO); ELSE DO; PUFFER_STAMMSATZ.SALDO = 0; CALL DRUCKAUFBEREITUNG_SALDO (PUFFER_STAMMSATZ.SALDO); END; /* FUNKTIONSEINHEIT DATUM */ IF STAMMSATZ_VORHANDEN THEN ZEILE.DATUM = DATUM_BEWEGUNG; ELSE ZEILE.DATUM = STAMMSATZ.DATUM;
6.4 Ein Vergleich der LITOS-Methode mit der Jackson-Methode
131
END DRUCKAUFBEREITUNG_ STAMMDATEN; / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
BEWEGUNGSDATENVERARBEITUNG
:
PROC;
I F BEWBGTE_STAMMDATEN = ' 1 ' B | STAMMSATZ_VORHANDEN = ' O ' B THEN DO; I F STAMMSATZ_VORHANDEN = ' O ' B THEN DO; DO UNTIL
(ZWISCHENSPEICHER_KONTONUMMER ->= KONTONUMMER_BEWEGUNG
|
ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_ EINGABE =
'1'B);
CALL VERARBEITEN_BEWEGUNGSDATEN
(PUFFER_
STAMMSATZ); END; END; ELSE DO UNTIL
DO;
(ZWISCHENSPEICHER_KONTONUMMER - , = KONTONUMMER_BEWEGUNG
|
ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_ EINGABE =
'1'B);
CALL VERARBEITEN_BEWEGUNGSDATEN
(STAMMSATZ);
END; END; END; /*
F U N K T I O N S E I N H E I T NICHTBEWEGTE STAMMDATEN
*/
ELSE; END BEWEGUNGSDATENVER ARBEITUNG; / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ? . - * * * * * * * * * * * * /
SUMMENTEIL IF IF
:
PROC; BEWEGTE_STAMMDATEN =
'1'B
|
STAMMSATZ_VORHANDEN =
' O ' B THEN DO;
STAMMSATZ_VORHANDEN =
'O'B
CALL DRUCKEN SUMMENZEILE
THEN
(PUFFER
STAMMSATZ);
132
6. LITOS, eine neue Methode des Softwareentwurfs . . . ELSE CALL DRUCKEN_SUMMENZEILE
(STAMMSATZ); END; ELSE DO;
WRITE FILE (LISTE)
FROM (ZEILE);
WRITE FILE (MAUSGAB) FROM (STAMMSATZ); END; IF STAMMSATZ_VORHANDEN = '1'B THEN READ
FILE (MEINGAB) INTO (STAMMSATZ);
END SUMMENTEIL; /*******************************************************/ DRUCKAUFBEREITUNG_SALDO :
PROC (SALDO); DCL SALDO DEC FIXED (9,2); ZEILE.ALTER_SALDO = SALDO; IF SALDO > O THEN AN Z EIGE_SOLL_HABEN = 'H1; ELSE IF SALDO < 0 THEN AN Z EIGE_SOLL_HABEN = * S'; ELSE AN Z EIGE_SOLL_HABEN = '
1
;
END DRUCKAUFBEREITUNG_SALDO; /*******************************************************/ DRUCKEN_SUMMEN Z EILE :
PROC (STAMMSATZ) ; DCL 1 STAMMSATZ CONNECTED, 2 SATZART
CHAR (1),
2 KONTONUMMER CHAR (6), 2 DATUM
CHAR (6),
2 SALDO
DEC FIXED (9,2);
IF SALDO > O THEN ANZEIGE_SOLL_HABEN_NEU = -H'; ELSE
6.4 Ein Vergleich der LITOS-Methode mit der Jackson-Methode
133
IF SALDO < 0 THEN AN Z EIGE_SOLL_HABEN_NEU = 'S'; ELSE AN Z EIGE_SOLL_HABEN_NEU _ i it . ZEILE.NEUER_SALDO = SALDO; WRITE FILE (LISTE)
FROM (ZEILE);
WRITE FILE (MAUSGAB) FROM (STAMMSATZ); END DRUCKEN_SUMMENZEILE; /*******************************************************/ VERARBEITEN_BEWEGUNGSDATEN :
PROC (STAMMSATZ);
DCL 1 STAMMSATZ, 2 SATZART
CHAR (1),
2 KONTONUMMER CHAR (6), 2 DATUM
CHAR (6),
2 SALDO
DEC FIXED (9,2);
ZEILE.BELEG_NUMMER = BELEG NUMMER_BEWEGUNG; /* UPDATEN STAMMSATZ */ STAMMSATZ.SATZART = KARTENART; STAMMSATZ.DATUM
= DATUM_BEWEGUNG;
STAMMSATZ.SALDO
= STAMMSATZ.SALDO + BETRAG_BEWEGUNG;
/* DRUCKAUFBEREITUNG */ ZEILE.BELEG_NUMMER = BELEGNUMMER_ BEWEGUNG; IF BETRAG_BEWEGUNG > 0 THEN ZEILE.HABEN = BETRAG_BEWEGUNG; ELSE ZEILE.SOLL
= BETRAG_BEWEGUNG;
/* DRUCKEN UND LESEN */
134
6. LITOS, eine neue Methode des Softwareentwurfs . . . WRITE FILE
(LISTE) FROM
(ZEILE);
ZEILE_1
=
'
1
;
ZWISCHENSPEICHER_KONTONUMMER = K O N T O N U M M E R _ BEWEGUNG; READ
FILE
(LEINGAB) INTO
(BEWEGUNGSSATZ);
END VERARBEITEN_BEWEGUNGSDATEN; /*******************************************************/ END M A G N E T B A N D _ D A T E I V E R A R B E I T U N G J A C K S O N _ M E T H O D E ;
6.5 Zur Änderungsfreundlichkeit von LITOS-Produkten Im Kapitel 1. wurde gezeigt, daß in der herkömmlichen Programmierung im Mittel 70 % der Softwarekosten auf die Programmwartung entfallen. Dieser hohe Kostenanteil hängt ursächlich damit zusammen, daß die Programmierer bisher den Umstand, daß Programme häufig geändert werden müssen, in der Programmentwicklung nicht berücksichtigt haben. Zwei Faktoren sind für die Änderungsfreundlichkeit relevant: — eine übersichtliche Programmdokumentation — eine Programmstruktur, die einen leichten Austausch von Programmkomponenten gestattet. Beide Bedingungen erfüllt die strukturierte Programmierung. Wir wollen diese Eigenschaft an Hand unserer Fallstudie verifizieren. Es sei die Aufgabe gegeben, in das Programm ,JV1AGNETBAND DATEIVER ARBEITUNG_STRUKTURIERT_VERSION_2" eine Sortierkontrolle für Bewegungsdaten einzubauen, die überprüft, ob in der Bewegungsdatei die vorgegebene Folge der Ordnungsmerkmale (in unserem Fall eine aufsteigende Folge) eingehalten wird; denn die Programmlogik interpretiert jeden falsch sortierten Bewegungssatz als Bewegung ohne Stammsatz und legt damit für dieses Ordnungsmerkmal zusätzlich einen zweiten Stammsatz an. Beispiel:
Merkmalsmenge Stammdatei: Merkmalsmenge Bewegungsdatei:
{11,13,15,16,20} { 1 1 , 1 5 , 1 3 , 1 6 , 20}
Das falsch sortierte Ordnungsmerkmal „13" der Bewegungsdatei führt zum Abschluß der Gruppe „15". Das Programm liest nach den Gruppenwechselarbeiten den Stammsatz „16", so daß der sich anschließende Sortiervergleich mit dem Ergebnis Ordnungsmerkmal der Bewegungsdatei kleiner als Ordnungsmerkmal der Stammdatei abschließt. Die Prozedur DATEIVERARBEITUNG ruft deshalb die Teilfunktion Bewegungen ohne Stammsätze auf. Wenn auf den Bewegungssatz
6.5 Zur Ä n d e r u n g s f r e u n d l i c h k e i t von L I T O S - P r o d u k t e n
135
„ 1 3 " weitere Bewegungssätze mit dem Ordnungsmerkmal ,,1 5 " folgen, wirkt sich dieser Sortierfehler noch gravierender aus. Weil die Stammdatei bereits zum Ordnungsmerkmal „ 1 6 " zugreift, würden diese im Grunde genommen richtig sortierten Datensätze ebenfalls als Bewegungen ohne Stammsätze verarbeitet werden. Dieses Beispiel zeigt, daß der Programmdesigner in der Praxis Aufgaben, deren Programmablaufsteuerung auf dem Vergleich von Ordnungsmerkmalen beruht, in der Regel mit Sortierkontrollen entwerfen muß. Als nächstes ist die Frage zu diskutieren, an welcher Stelle im Feinentwurf die Sortierkontrolle einzubauen ist (s. Abbildung 17). Es bietet sich an, im Zuge der Gruppenwechselarbeiten abzufragen, ob der Gruppenwechsel die geforderte Folge der Ordnungsmerkmale eingehalten hat. Ist dies nicht der Fall, dann können aber die Auswirkungen eines Sortierfehlers nicht mehr vermieden werden. Die Sortierkontrolle m u ß deshalb vor dem Gruppenwechselprozeß und zwar nach dem Lesen des Bewegungssatzes ansprechen. Zu diesem Zweck nehmen wir in die Funktionseinheit „Lesen Bewegungssatz" eine zusätzliche Anweisung auf, die ein Unterprogramm „Sortierkontrolle" aufruft (s. Abbildung 28). Es druckt eine Fehlermeldung aus und liest den nächsten Bewegungssatz, so daß der Pro-
Abb. 28 Feinentwurf des Moduls „Verarbeiten Bewegungsdaten" mit Sortierkontrolle der Bewegungsdatei.
6. LITOS, eine neue Methode des Softwareentwurfs
136
grammablauf falsch sortierte Bewegungssätze nicht berücksichtigt und nur den Maschinenbediener durch Hinweise auf dieses Faktum aufmerksam macht. In der Programmdokumentation haben wir durch eine entsprechende äußere Gestaltung die Stellen, die von dieser Änderung betroffen sind, markiert, so daß sie ein Außenstehender leicht finden kann. Der nachfolgende Programmausschnitt weist die geänderten Programmteile aus. MAGNETBAND_DATEIVERARBEITUNG_STRUKTURIERT_VERSION_2: PROC OPTIONS
(MAIN);
/**************
EINBAU SORTIERKONTROLLE
/**************
VEREINBARUNGSSYSTEM
****************/
********************/
/ * DATEIVEREINBARUNGEN,
SATZVEREINBARUNGEN,
VEREINBARUNGEN ZUR PROGRAMMSTEUERUNG WIE URSPRUENGLICHE FASSUNG /**************
NEUE VEREINBARUNGEN
*/
********************/
DCL ALTE_KONTONUMMER
CHAR(6) ,
1 MELDUNG, 2 VORSCHUBSTEUERUNG CHAR (1 ) I N I T
CO'),
2 LEERZEICHEN^
CHAR(3)
INIT
('
'),
2 NEUE_KONTONUMMER
CHAR(6),
2 LEERZEICHEN_2
CHAR(25)INIT
('
'),
2 BELEGNUMMER
CHAR(4),
2 TEXT
CHAR(16)INIT ('
2 LEERZEICHEN_3 /**************
FALSCH SORTIERT'),
CHAR(33)INIT
ENDE VEREINBARUNGSSYSTEM
/ * ENDFILE_BEDINGUNGEN,
');
***************/
OPERATIONALES_SYSTEM
WIE URSPRUENGLICHE FASSUNG DATEIEROEFFNUNG:
('
*/
PROC; OPEN F I L E
(LEINGAB),
FILE
(MEINGAB),
FILE
(MAUSGAB),
FILE
(LISTE);
/ * DRUCKEN UEBERSCHRIFTZEILE, WRITE F I L E
(LISTE)
FROM
1 LEERZEILE
(UEBERSCHRIFT);
*/
6.5 Zur Änderungsfreundlichkeit von LITOS-Produkten
137
ZEILE_1 = '0'; WRITE FILE (LISTE) FROM (ZEILE); READ
FILE (LEINGAB) INTO (BEWEGUNGSSATZ);
READ
FILE (MEINGAB) INTO (STAMMSATZ);
/************** AENDERUNG 1 ****************************/ ALTE_KONTONUMMER = KONTONUMMER_BEWEGUNG; /************** ENDE AENDERUNG 1 ***********************/ END DATEIEROEFFNUNG; /*******************************************************/ /* DATEIVERARBEITUNG, DATEISCHLIESSEN, PROZEDUREN HIERARCH. STUFE 3, DRUCKAUFBEREITUNG_STAMMDATEN WIE URSPRUENGLICHE FASSUNG */ /a******************************************************/ VERARBEITEN_BEWEGUNGSDATEN: PROC (STAMMSATZ); DCL 1 STAMMSATZ, 2 SATZART
CHAR
(1),
2 KONTONUMMER CHAR
(6),
2 DATUM
CHAR
(6),
2 SALDO
DEC FIXED (9,2);
DO WHILE (KONTONUMMER_BEWEGUNG = KONTONUMMER & ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_ EINGABE =
1
0'B);
/* UPDATEN STAMMSATZ */ STAMMSATZ.SATZART
= KARTENART;
STAMMSATZ.DATUM
= DATUM_ BEWEGUNG;
STAMMSATZ.SALDO
= STAMMSATZ. SALDO + BETRAG_ BEWEGUNG;
/* DRUCKEN BEWEGUNGSSATZ */ ZEILE.BELEG_NUMMER
= BELEGNUMMER_ BEWEGUNG;
138
6. LITOS, eine neue Methode des Softwareentwurfs . . .
ZEILE.DATUM
= DATUM_ BEWEGUNG;
IF BETRAG_BEWEGUNG > 0 THEN ZEILE.HABEN = BETRAG_BEWEGUNG; ELSE ZEILE.SALDO = BETRAG_BEWEGUNG; WRITE FILE (LISTE) FROM(ZEILE); ZEILE_1
=
1
';
/* LESEN BEWEGUNGSSATZ */ READ
FILE (LEINGAB) INTO (BEWEGUNGSSATZ);
/************** AENDERUNG 2 *************************** CALL SORTIERKONTROLLE; /************** ENDE AENDERUNG 2 ********************** END; END VERARBEITEN_BEWEGUNGSDATEN; /****************************************************** /* GRUPPENWECHSEL, DRUCKEN_SUMMENZEILE WIE URSPRUENGLICHE FASSUNG */ SORTIERKONTROLLE:
PROC; NEUE_KONTONUMMER = KONTONUMME R_ BEWEGUNG; DO WHILE (NEUE_KONTONUMMER
O THEN ZEILE.HABEN = BETRAG_BEWEGUNG; ELSE ZEILE.SALDO = BETRAG_BEWEGUNG; WRITE FILE
(LISTE) FROM(ZEILE);
ZEILE_1
= ' ';
/* LESEN BEWEGUNGSSATZ */ READ
FILE
(LEINGAB) INTO
(BEWEGUNGSSATZ);
END; END VERARBEITEN BEWEGUNGSDATEN;
Die lokale Bedeutung der in dieser Prozedur zusätzlich auftretenden Schleife ist daran zu erkennen, daß sie durch eine einzige Anweisung aktiviert wird, nämlich durch das Bedingungspräfix „SIZE" in der arithmetischen Anweisung, die den Saldo des Stammsatzes durch die Bewegungsdaten auf den neuesten Stand bringt. In dieser Schleife wird durch eine Art Komplementbildung der Ausgangswert des Saldos, den dieses Summenfeld vor dem Überlauf hatte, in der Variablen „ S I Z E V A R I A B L E " rekonstruiert. Anschließend druckt das Programm diesen Wert aus und legt einen neuen Stammsatz mit dieser Zwischensumme an. Nachdem das Programm diese lokale Schleife verlassen hat, geht es bei der Funktionseinheit „Drucken Bewegungssatz" (s. Abbildung 17) weiter. Dieses Kapitel wollen wir abschließen, indem wir die im Kapitel 6.3 gegebene Struktur eines top down entworfenen Programms verallgemeinern und dabei lokale Schleifen mit aufnehmen. Wir kommen damit zu folgender Gliederung:
7 . 4 Strukturierter Entwurf simultaner Prozesse
159
7.4 Strukturierter Entwurf simultaner Prozesse In diesem Kapitel soll zum Abschluß unserer gesamten Untersuchungen gezeigt werden, daß sich die LITOS-Methode nicht nur für die serielle Programmverarbeitung, die wir bisher ausschließlich unterstellt haben, eignet, sondern auch beim Entwurf simultan ablaufender Programmteile (in PL/1 die MultitaskingFunktion) vorteilhaft einsetzbar ist. Die klassische Arbeit zur Frage der strukturierten Programmierung simultaner Prozesse geht auf Brinch Hansen zurück, der aber von einer anderen Aufgabenstellung ausgeht, als sie unseren Untersuchungen zugrunde liegt, nämlich dem Entwurf von Betriebssystemen für den Mehrprogrammbetrieb [19, S. 574 ff.]. Die Hauptaufgabe eines solchen Betriebssystems besteht darin, Betriebsmittel verschiedenen Aufgaben simultan zur Verfügung zu stellen, ohne daß schädliche gegenseitige Beeinflussungen entstehen. Die Zeitpunkte, zu denen Betriebsmittel angefordert werden, sind unbekannt.
160
7. Weiterführende Untersuchungen zur Software-Konstruktionslehre
Ein Lösungsansatz für dieses Problem sind die „kritischen Regionen" in einem Programm oder einem Programmblock, in die mehrfachbenutzte Betriebsmittel eingebettet sind. Es darf sich in einer solchen Region nur ein Prozeß aufhalten, der auf dieses Betriebsmittel zurückgreift. In der Anwendungsprogrammierung können ähnliche Situationen dann entstehen, wenn verschiedene Tasks gleichzeitig auf ein und dieselbe Datei zugreifen. Wir haben dieses Problem allerdings schon unter 5.2.3 untersucht. An dieser Stelle beschränken wir uns auf die Frage, wie mit dem Werkzeug der LITOS-Entwurfsmethode eine Aufgabe top down in simultan ablaufende Teilaufgaben dekomponiert werden kann, wobei im Gegensatz zur Situation in einem Betriebssystem determiniert ist, um welche Prozesse es sich handelt. Für diesen Entwurf setzen wir zusätzlich zu den Strukturelementen der seriellen Programmverarbeitung einen minimalen Satz von Programmbausteinen ein, bestehend aus der Aufspaltung, der Sammlung und dem Synchronisationsschnitt [44, S. 170 ff.]. Die Symbole zur bildhaften Darstellung dieser drei Programmbausteine wurden schon in Abbildung 15 angegeben. Der strukturierte Entwurf eines simultan arbeitenden Anwendungssystems vollzieht sich in zwei Stufen: (1)
LITOS-Entwurf einer seriell arbeitenden Lösung
(2)
Simultanisierung von Funktionseinheiten der seriellen Lösung.
Für die zweite Entwurfsphase ist die Frage relevant, gibt es allgemeine Entwurfsregeln für die Simultanisierung oder muß der Programmdesigner von Fall zu Fall untersuchen, welche Funktionen eines Programmsystems simultan ablaufen können. Diese Frage wollen wir pragmatisch untersuchen, indem wir wieder unsere Fallstudie aufgreifen. Wir gehen davon aus, daß die hierarchischen Stufen 4 und 5 der Teilfunktion „bewegte Stammsätze" (s. Abbildung 17) in einen simultanen Ablauf überführt werden sollen. Bei der Lösung dieser Aufgabenstellung sind offenbar solche Funktionseinheiten kritisch, die auf dieselben Daten zurückgreifen. Hansen hat vorgeschlagen, den simultanen Zugriff zu Daten durch einen ,.Monitor" überwachen zu lassen [20, S. 2 ff.]. Er serialisiert alle Zugriffswünsche und stellt Daten zu einem Zeitpunkt nur einem der simultanen Prozesse zur Verfügung, so daß die anderen warten müssen. Diese Monitor-Funktion ist aber nur dann von Vorteil, wenn der simultane Datenzugriff zu falschen Ergebnissen in den damit verbundenen Arbeitsprozessen führen kann. Sie ist dann von Nachteil, wenn die simultan benutzten Daten nicht verändert werden. Um die Frage zu beantworten, unter welchen Bedingungen Daten simultan benutzt werden können, gehen wir einen anderen Weg. Wir legen dieser Untersuchung die zu simultanisierenden Prozesse zugrunde. Beispielsweise sind die Ausgangsdaten der Funktionseinheit „Verarbeiten Bewegungsdaten" auch Eingangsdaten des Gruppenwechselprozesses. Zwischen beiden besteht eine sequentielle Bindung im Sinne von Constantine (s. S. 65). Deshalb ist eine Simultanisierung dieser beiden
7.4 Strukturierter Entwurf simultaner Prozesse
161
Funktionen nicht möglich, weil sonst die Gruppenwechselarbeiten von falschen Daten ausgehen. Damit kommen wir zur ersten Regel einer Simultanisierbarkeit: (1)
Zwei Funktionseinheiten A und B, die im seriellen Programmentwurf nacheinander ausgeführt werden, sind nicht simultanisierbar, wenn die Ausgangsdaten von A Eingangsdaten von B sind, es sei denn, daß die Funktionseinheit B an der Stelle, wo sie die Ergebnisse von A benötigt, so lange wartet, bis A diese Daten nicht mehr verändert.
Der zweite Teil dieser Regel ist am Beispiel der beiden Funktionseinheiten „Druckaufbereitung Stammdaten" und „Drucken Bewegungssatz" zu erkennen. Es liegt nahe zu versuchen, die Druckaufbereitung der Stammdaten simultan zum Verarbeiten der Bewegungsdaten eines Stammsatzes ablaufen zu lassen. Kritisch ist dabei der Datensatz „ZEILE". Da zusammen mit dem ersten Bewegungssatz einer Gruppe auch seine Stammdaten ausgedruckt werden, darf dieser Druckprozeß erst dann beginnen, wenn die Druckaufbereitung der Stammdaten abgeschlossen ist. Man kann deshalb diese beiden Funktionseinheiten nur unter der einschränkenden Bedingung simultanisieren, daß eine zusätzliche Synchronisation in den Programmablauf eingebaut wird. Sie hat dafür zu sorgen, daß vor dem Drucken einer Zeile die Druckaufbereitung der Stammdaten in dieser Zeile abgeschlossen ist (s. Abbildung 30). Eine Simultanisierung ist auch dann kritisch, wenn Ausgangsdaten einer Funktionseinheit B Eingangsdaten einer vorgeschalteten Funktionseinheit A sind; denn in diesem Fall verändert „B" Daten, auf die „A" zugreift. Beispiel: Auf der Eingangsseite der Funktionseinheit „Verarbeiten Bewegungsdaten" tritt in Gestalt des Stammsatzes derselbe Datensatz auf wie am Ausgang von „Lesen Stammsatz", so daß ein simultanes Lesen eines neuen Stammsatzes zu Fehlern im Verarbeitungsprozeß fuhren kann, wenn dieser Lesevorgang Stammdaten im Hauptspeicher überschreibt, die der Verarbeitungsprozeß noch benötigt. Nun haben wir die Funktionseinheit „Verarbeiten Bewegungsdaten" in der Weise programmiert, daß der Datensatz „STAMMSATZ" ein explizit übergebener Parameter ist (s. Programm „MAGNETBANDDATEIVERARBEITUNGSTRUKTU RIERT VERSION 2"). Man könnte argumentieren, daß in dieser Weise beide Funktionseinheiten entkoppelt sind, so daß der Leseprozeß eines neuen Stammsatzes keinen Einfluß auf den Verarbeitungsprozeß der Bewegungsdaten haben kann. Dabei ist aber zu beachten, daß es in problemorientierten Programmiersprachen drei Methoden der Parameterübergabe gibt, die nicht einheitlich verwendet werden, nämlich das Call by value, das Call by name und das Call by reference [39, S. 21]. PL/1 arbeitet im Normalfall mit der dritten Kategorie, dem Call by reference. Hierbei übergibt das aufrufende Programm dem aufgerufenen die Bezeichner von Arbeitsobjekten und damit implizit die Adressen ihrer Speicherplätze im aufrufenden Programm [43, S. 157]. Jede Änderung eines Wertes
162
7. Weiterführende Untersuchungen zur Software-Konstruktionslehre
in einem Unterprogramm wirkt sich deshalb auch im Oberprogramm aus. Für die serielle Programmverarbeitung ergeben sich durch diese Unterprogrammtechnik keine Schwierigkeiten, im Gegenteil, man erhält in dieser Weise automatisch die Rückgabe eines aktuellen Parameters an das Oberprogramm. In der Simultanverarbeitung unterstellt jedoch der Programmdesigner, daß simultan aufgerufene Prozeduren nicht nur zeitlich unabhängig sind, sondern daß auch hinsichtlich ihrer Arbeitsobjekte keinerlei Bindungen bestehen. Diese Forderung kann nur durch ein Call by value erreicht werden, wobei das Oberprogramm dem Unterprogramm den aktuellen Wert eines Parameters direkt übergibt und im Unterprogramm für diesen Wert ein Speicherplatz angelegt wird [39, S. 21]. PL/1-Kompilierer simulieren diese Form der Parameterübergabe durch „dummy" Argumente [24, S. 113]. In bestimmten Fällen werden sie automatisch erzeugt. Beispiele sind Parameter in Form von Konstanten oder arithmetischen Ausdrücken. Man kann sie aber auch künstlich erzeugen, nämlich durch doppelte Klammerung. So ist die Notation „CALL A ((B))" ein Call by value [S. 24, S. 115]. Die zweite Regel einer Simultanisierbarkeit lautet deshalb: (2)
Zwei Funktionseinheiten A und B, die im seriellen Entwurf nacheinander ausgeführt werden, sind nicht simultanisierbar, wenn die Eingangsdaten von A auch Ausgangsdaten von B sind, es sei denn, daß eine der beiden Funktionseinheiten mit einer expliziten Parameterübergabe in Form des Call by value arbeitet oder daß eine Synchronisation der beiden Funktionseinheiten eine schädliche gegenseitige Beeinflussung verhindert.
Beispiel-, DCL A (5) DEC FIXED (3) INIT (1, 2, 3,4, 5); CALL PUT (A) EVENT (DRUCKER); A = A + A**3; CALL PUT l(A) EVENT (DRUCKEN); WAIT (DRUCKEN, DRUCKER); In dieser Anweisungsfolge soll die erste ,,PUT"-Prozedur die Ausgangswerte des Bereichs „A" ausdrucken und die zweite das Ergebnis der arithmetischen Anweisung. Infolge des Call by reference druckt aber auch die erste CALL-Anweisung das Ergebnis des Rechenprozesses aus. Um dies zu verhindern, muß der Programmierer an dieser Stelle im Programm ein Call by value notieren, d. h. die Anweisung: CALL PUT ((A)) EVENT (DRUCKER); Als nächstes ist zu untersuchen, unter welchen Bedingungen Funktionseinheiten, die mit denselben Eingabe daten arbeiten, simultanisierbar sind. Ein Beispiel für diesen Fall sind in unserer Fallstudie die beiden Funktionen „Updaten Stammsatz" und „Drucken Bewegungssatz", die beide denselben Bewegungssatz verarbeiten. Da in diesem Fall aber weder im Updateprozeß noch im. Druckprozeß der
163
7.4 Strukturierter Entwurf simultaner Prozesse
Bewegungssatz verändert wird, ist eine Simultanisierung ohne weiteres möglich. Zu einem ganz anderen Schluß käme man aber, wenn beispielsweise die Funktionseinheit „Updaten Stammsatz" auch Bewegungsdaten verändert. Aus diesem Beispiel wird die dritte Regel der Simultanisierbarkeit abgeleitet: (3)
Zwei Funktionseinheiten A und B mit denselben Eingangsdaten sind nicht simultanisierbar, es sei denn, die gemeinsamen Daten werden in den beiden Funktionseinheiten nicht verändert, oder sie stellen explizit durch ein Call by value übergebene Parameter dar, oder es erfolgt eine Synchronisation, die eine schädliche gegenseitige Beeinflussung unterbindet.
Ü
STAMMSATZ BEWEGUNGSSATZ
BEWEGTE
ZEILE, WMOIWER_STAN"tSATZ, SAUXLST/WtSATZ
DRUCKAUFBEREITUNG ST/WIDATEN
>
I tii
STAMMSÄTZE
A
BEWEGUNGSSATZ STAMMSATZ =
VERARBEITEN BEWEGUNGSDATEN
STAMMSATZ
LESEN STAMMSATZ
BEWEGUNGSDATEN SCHLEIFE
Abb. 31
Simultanisierung der Teilfunktion „Bewegte Stammsätze" der Fallstudie.
164
7. Weiterführende Untersuchungen zur Software-Konstruktionslehre
Unter Anwendung dieser drei Simultanisierungsregeln haben wir den Feinentwurf der Abbildung 17 in eine simultanisierte Lösung überführt (Abbildung 31). Dabei ist zu beachten, daß in PL/1 im Nonnalfall das Simultanisierungsniveau die Prozedurebene ist, so daß alle simultanisierten Funktionseinheiten zu Prozeduren werden, sofern es sich nicht um satzweise Eingabe- und Ausgabeprozesse handelt, die der Sprachzusatz „EVENT" simultanisiert. Der folgende Programmausschnitt zeigt die simultanisierte Lösung der Prozedur „BEWEGTESTAMMSAETZE". In diesem Beispiel wurden sämtliche Simultanisierungsmöglichkeiten ausgenutzt.
BEWEGTE_STAMMSAETZE: /*** SIMULTANE PROGRÄMMVERARBEITUNG ***/ PROC; CALL DRUCKAUFBEREITUNG_STAMMDATEN ((STAMMSATZ.KONTONUMMER) , (STAMMSATZ. SALDO) ) EVENT (DRUCKAUFBEREITONG) ; CALL VERARBEITEN_BEWEGUNGSDATEN
((STAMMSATZ))
EVENT (BEWEGUNGSSCHLEIFE) ; READ FILE (MEINGAB) INTO (STAMMSATZ) EVENT (LESEN_STAMMSATZ) ; WAIT
(LESEN_STAMMSATZ,BEWEGUNGSSCHLEIFE,
DRUCKAUFBEREITUNG); END BEWEGTE_STAMMSAETZE; /*******************************************************/ VERARBEITEN_BEWEGUNGSDATEN: PROC (STAMMSATZ); DCL 1 STAMMSATZ CONNECTED, 2 SATZART_STAMMSATZ
CHAR
(1),
2 KONTONUMMER_STAMMSATZ CHAR
(6),
2 DATUM STAMMSATZ
CHAR
(6),
2 SALDO_STAMMSATZ
DEC FIXED (9,2);
DO WHILE (KONTONUMMER_BEWEGUNG = KONTONUMMER_ STAMMSATZ & ENDFILE_BEDINGUNG_ BEWEGUNGSDATEN_EINGABE = 'O'B); CALL UPDATEN_STAMMSATZ EVENT (UPDATEN);
((BEWEGUNGSSATZ))
I
7.4 Strukturierter Entwurf simultaner Prozesse
165
CALL DRUCKEN_BEWEGUNGSSATZ EVENT
(DRUCKEN);
READ F I L E EVENT WAIT
((BEWEGUNGSSATZ))
(LEINGAB)
INTO (BEWEGUNGSSATZ)
(LESEN_BEWEGUNGSSATZ); (LESEN_BEWEGUNGSSATZ,UPDATEN, DRUCKEN);
END; CALL GRUPPENWECHSEL
(STAMMSATZ);
/ i t * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
/**************
NEUE PROZEDUREN
************************/
UPDATEN_STAMMSATZ: PROC
(BEWEGUNGSSATZ);
DCL 1 BEWEGUNGSSATZ, 2 KONTONUMME R_BEWEGUNG
CHAR
(6),
2 BELEGNUMMER_BEWEGUNG
CHAR
(4),
2 DATUM_BEWEGUNG
CHAR
(6), 1
S999999V991,
2 BETRAG_BEWEGUNG
PIC
2 TEXT
CHAR(54),
2 KARTENART
CHAR
SATZART_STAMMSATZ =
(1);
KARTENART;
DATUM_STAMMSAT Z
= DATUM_BEWEGUNG;
SALDO_S TAMMSAT Z
= SALDO_STAMMSATZ
+
BETRAG_BEWEGUNG; END UPDATEN_STAMMSATZ; / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * /
DRUCKEN_BEWEGUNGSSATZ: PROC DCL 1
(BEWEGUNGSSATZ); BEWEGUNGSSATZ, 2 KONTONUMMER_BEWEGUNG
CHAR
(6),
2 BELEGNUMMER_BEWEGUNG
CHAR
(4),
2 DATUM_BEWEGUNG
CHAR
(6),
2 BETRAG_BEWEGUNG
PIC
'S999999V991,
166
7. Weiterführende Untersuchungen zur Software-Konstruktionslehre
2 TEXT
CHAR(54),
2 KARTENART
CHAR (1);
ZEILE.BELEG_NUMMER = BELEGNUMMER_ BEWEGUNG; ZEILE.DATUM
= DATUM_BEWEGUNG;
IF BETRAG_BEWEGUNG > 0 THEN ZEILE.HABEN = BETRAG_BEWEGUNG; ELSE ZEILE.SOLL
=
BETRAG_BEVJEGUNG ; WAIT (DRUCKAUFBEREITUNG); WRITE FILE (LISTE) FROM (ZEILE); ZEILE_1
= ' ';
END DRUCKEN_BEWEGUNGSSATZ; END VERARBEITEN_BEWEGUNGSDATEN; /*******************************************************/ GRUPPENWECHSEL: PROC (STAMMSATZ); /* DRUCKAUFBEREITUNG SUMMENZEILE */ DCL 1 STAMMSATZ, 2 SATZART
CHAR
(1),
2 KONTONUMMER
CHAR
(6),
2 DATUM
CHAR
(6),
2 SALDO
DEC FIXED (9,2); SELECT;
WHEN (SIGN (SALDO)
= 1)
ANZEIGE_SOLL_HABEN_NEU = 'H1; WHEN (SIGN (SALDO)
= -1)
AN Z EIGE_SOLL_HABEN_NEU = 'S'; OTHERWISE ANZEIGE_SOLL_HABEN_NEU = ' '; ZEILE.NEUER_SALDO
= SALDO;
END; CALL DRUCKEN_SUMMENZEILE (STAMMSATZ) ; END GRUPPENWECHSEL; /*******************************************************/
7.4 Strukturierter Entwurf simultaner Prozesse
167
DRUCKAUFBEREITUNG_STAMMDATEN: PROC
(KONTONUMMER_STAMMSATZ,SALDO_STAMMSATZ);
DCL KONTONUMMER_STAMMSATZ CHAR SALDO_STAMMSATZ ZEILE_1
(6),
DEC FIXED (9,2); = 'O';
ZEILE.KONTONUMMER = KONTONUMMER_STAMMSATZ; ZEILE.ALTER_SALDO = SALDO_STAMMSATZ; SELECT; WHEN (SIGN (SALDO_STAMMSATZ) = 1) ANZEIGE_SOLL_HABEN =
1
H' ;
WHEN (SIGN (SALDO_STAMMSATZ) = -1) ANZEIGE_SOLL_HABEN = 'S'; OTHERWISE ANZEIGE_SOLL_HABEN = '
1
;
END; END DRUCKAUFBEREITUNG_STAMMDATEN; /*******************************************************/ DRUCKEN_SUMMENZEILE: PROC (STAMMSATZ); DCL 1 STAMMSATZ CONNECTED, 2 SATZART
CHAR
(1),
2 KONTONUMMER CHAR
(6),
2 DATUM
CHAR
(6)
2 SALDO
DEC FIXED (9,2);
WRITE FILE (LISTE) FROM (ZEILE); WRITE FILE (MAUSGAB) FROM (STAMMSATZ); END DRUCKEN_SUMMEN Z EILE; /*******************************************************/
8. Ausblick
Die Diskussion in der Fachwelt über die Methoden des strukturierten Softwareentwurfs und der strukturierten Programmierung ist noch in vollem Gange. Zwei Entwicklungsrichtungen zeichnen sich ab: — —
der Programmentwurf über Bildschirmterminals die Ausdehnung der Methoden des strukturierten Entwurfs auf die betriebliche Aufbau- und Ablauforganisation.
Um Probleme schrittweise in Form von Versuch und Irrtum zu lösen, wird seit längerem propagiert, Programme interaktiv zu erstellen und zu testen. Es liegt nahe zu versuchen, in diesen Dialogverkehr auch den Programmentwurf und die Simulation der Programmlogik einzubeziehen. Dadurch könnte die Produktivität der Programmierung gesteigert werden, indem der Schreib- und Zeichenaufwand des Programmdesigners reduziert wird und er unmittelbaren Zugriff zu einer maschinell gespeicherten Programmdokumentation erhält. Um einen solchen computergestützten Entwurfsstil zu realisieren, sind erst noch eine Reihe von Voraussetzungen zu erfüllen, wie die Definition und Implementierung einer interpretierbaren Programmentwurfssprache und die Verbesserung der Listenverarbeitungsfunktionen (z. B. Generierung einer Liste durch eine einzige Anweisung), so daß noch einige Zeit vergehen wird, bis den Anwendern diese Form des Mensch-Maschine-Dialogs zur Verfügung stehen wird. Betriebe, die in den letzten Jahren versucht haben, Programmsysteme strukturiert und top down zu entwerfen, haben manchmal die Erfahrung gemacht, daß die Schnittstelle zwischen herkömmlicher Aufbau- und Ablauforganisation und der DV-Organisation Schwierigkeiten bereitet, weil beide Organisationsformen nicht kompatibel waren. Aus solchen Erfahrungen resultiert die Erkenntnis, daß die moderne Softwaretechnologie offenbar nur dann optimal einsetzbar ist, wenn auch die davorliegende Organisation nach denselben Prinzipien arbeitet. Es ist deshalb notwendig, die Methoden des strukturierten Entwurfs auch auf die betriebliche Aufbau- und Ablauforganisation anzuwenden. Der Autor hat versucht, mit diesem Buch sowohl den gegenwärtigen Stand der Softwaretechnologie zu analysieren als auch einen Beitrag zur Softwarekonstruktionslehre zu leisten. Wir hoffen, daß wir in dieser Weise helfen, die Softwarekrise zu überwinden.
169 Anhang
Syntax der neuen Anweisungen für das strukturierte Programmieren im Release 3.0 des PL/1 -Optimizing Compilers [24] Die Anweisungen werden mit der in [44, S. 9 ff.] beschriebenen Syntaxnotation dargestellt. AI DO-Anweisung : : = DO; ! DO {WHILE «elementausdruck^») [UNTIL «elementausdruck»] | UNTIL «elementausdruck» [WHILE «elementausdruck»]; | D O - ^ l a u f v a r i a b l e ^ = < Spezifikation > [ « S p e z i f i k a t i o n } ' ] . . . ; «^Spezifikation^ : : =
[TO] END []; Erläuterungen: Der Ausdruck in der SELECT-Anweisung wird nacheinander mit den Ausdrücken der WHEN-Anweisungen verglichen. Sobald beide denselben Wert haben, geht die Programmausführung zu derjenigen syntaktischen Komponente -^ausführbarer prozedurteil}- weiter, die mit dieser WHEN-Anweisung zusammen notiert wurde. Dieser Vergleichsprozeß läuft so lange ab, bis die OTHER WISE-An weisung erreicht ist. Sie wird ausgeführt, wenn kein Vergleich positiv beantwortet werden konnte. 1 2
s. [44, s. 19] s. [44, S. 18]
170
Anhang
Falls ein Programmierer die SELECT-Anweisung ohne den Zusatz < a u s d r u c k > notiert hat, werden die Ausdrücke der WHEN-Anweisungen wie Vergleichsausdrücke behandelt und in Bitketten konvertiert [43, S. 64 ff.]. Die OTHERWISEAnweisung kann weggelassen werden, aber nur dann, wenn sichergestellt ist, daß auf jeden Fall eine WHEN-Anweisung anspricht.
Literaturverzeichnis
[ 1] Andler, K.: Die wirtschaftliche Auftragsmenge für Fertigung und Lager. In: Das Industrieblatt 51 (1951). [ 2] Baker, F. T., H. D. Mills: Chief Programmer Teams. In: Datamation 19(1973). [ 3] Bell, T. E., D. C. Bixler, M. E. Dyer: An Extendable Approach to Computer-Aided Software Requirements Engineering. In: Structured Design, Infotech State of the Art Conference, London 1977. [ 4] Boehm, B. W.: Software Design and Structuring. In: E. Horowitz (Hrsg.), Practical Strategies for Developing Large Software Systems, Reading 1975. [ 5] Boehm, B. W.: Software and Its Impact: A Quantitative Assessment. In: Datamation 19 (1973). [ 6] Böhm, C., G. Jacopini: Flow Diagrams, turing machines and languages with only two formation rules. In: Communications of the ACM 9 (1966). [ 7] Brown, R. R.: The Techniques and Practice of Structured Design a la Constantine. In: Structured Design, Infotech State of the Art Conference, London 1977. [ 8] Chroust, G., N. Flödl, F. Klein: Für und wider eine ÖNORM für normierte Programmierung: In: ÖNORM 5 (1975). [ 9 ] Chu, Y.: Computer Organization and Microprogramming. Englewood Cliffs, N . J . 1972. [10] Dijkstra, E. W.: Go To Statement Considered Harmful. In: Communications of the ACM (Letters to the Editor) 11 (1968). [11] Dijkstra, E. W.: Structured Programming. In: P. Ercoli, F. L. Bauer (eds.), Software Engineering Techniques, Brüssel 1969. [12] Dijkstra, E. W.: The Humble Programmer. In: Communications of the ACM 15 (1972). [13] DNA, Deutscher Normenausschuß, Fachnormenausschuß Informationsverarbeitung (FNI): DIN 44300, Informationsverarbeitung, Begriffe, Berlin 1972. [14] DNA, Deutscher Normenausschuß, Fachnormenausschuß Informationsverarbeitung (FNI): DIN 44300, Informationsverarbeitung, Begriffe, Neuauflage (noch nicht veröffentlicht). [15] DNA, Deutscher Normenausschuß, Fachnormenausschuß Informationsver-
172
Literaturverzeichnis arbeitung(FNI): DIN 66001, Informationsverarbeitung, Sinnbilder für Datenfluß- und Programmablaufpläne, Berlin 1969.
[16] DNA, Deutscher Normenausschuß, Fachnormenausschuß Informationsverarbeitung (FNI): DIN 66001, Informationsverarbeitung, Sinnbilder für Datenfluß-und Programmablaufpläne, Berlin 1977. [17] DNA, Deutscher Normenausschuß, Fachnormenausschuß Informationsverarbeitung (FNI): DIN 66220, Informationsverarbeitung, Programmablauf für die Verarbeitung von Dateien nach Satzgruppen, Berlin 1977. [18] McGowan, C. L., J. R. Kelly: A Review of Decomposition and Design Methodologies. In: Structured Design, Infotech State of the Art Conference, London 1977. [19] Hansen, P. B.: Structured Multiprogramming. In: Communications of the ACM 15 (1972). [20] Hansen, P. B.: Concurrent PASCAL Introduction. Pasadena, 1975. [21] Heldmann, G.: Zielsetzung der Software-Technologie. In: Softlab-Seminar „Moderne Software-Technologie für EDV-Führungskräfte", Tagungsband, München 1974. [22] IBM: Chief Programmer Teams Principles and Procedures. IBM Federal Systems Division, FSC 7 1 - 5 1 0 8 . Gaithersburg 1971. [23] IBM: H I P O - A D e s i g n Aid and Documentation Technique. G C 2 0 - 1 8 5 1 11, White Plains, N. Y. 1975. [24] IBM: OS PL/1 Checkout and Optimizing Compilers. Language Reference Manual, GC 3 3 - 0 0 0 9 - 4 , 6 . Auflage, Hursley Park, Winchester UK 1976. [25] Jackson, M. A.: Principles of Program-Design. London, New York, San Francisco 1975. [26] Knuth, D. E.: Structured Programming with go to Statements. In: Computing Surveys 6 (1974). [27] Knuth, D. E., R. W. Floyd: Notes on avoiding ,GO TO' statements. In: Inf. Proc. Letters 1 (1971). [28] Kopetz, H.: Softwarezuverlässigkeit. München, Wien 1976. [29] Kosiol, E.: Die Unternehmung als wirtschaftliches Aktionszentrum. Hamburg 1966. [30] Kramer, R.: Information und Kommunikation. In: E. Kosiol und E. Grochla (Hrsg.): Betriebswirtschaftliche Forschungsergebnisse Band 23, Berlin 1965. [31 ] Langers, F.: Die Ordnung der Stamm- und Bewegungsdaten in der Aufbauund Ablauforganisation. In: elektronische datenverarbeitung 10 (1968).
Literaturverzeichnis
173
[32
van der Meulen, S. G., P. Kühling: Programmieren in ALGOL 68,1. Einführung in die Sprache. Berlin, New York 1974.
[33
Mills, H. D.: Mathematical Foundations for Structured Programming. IBM Federal Systems Division, FSC 7 2 - 6 0 1 2 . Gaithersburg 1972.
[34
Nassi, I., P. Shneidermann: Flowchart Techniques for Structured Programming. In: SIGPLAN Notices 8 (1973).
[35
Osterie, H.: Techniken zum Entwurf betrieblicher Anwendungsprogramme. In: Angewandte Informatik 8 (1977).
[36
Parnas, D. L.: On the Criteria To Be Used in Decomposing Systems into Moduls. In: Communications of the ACM 15 (1972).
[37
Peterson, W. W., T. Kasami, N. Tokura: On the Capabilities of While, Repeat, and Exit Statements. In: Communications of the ACM 16 (1973).
[38
Ratcliff, K.: Data Structure and Structured Design. In: Structured Design, Infotech State of the Art Conference, London 1977.
[39
Rechenberg, P.: Programmieren für Informatiker mit PL/1, Band 2. München, Wien 1974.
[40
Ross, D. T.: Structured Analysis (SA): A Language for Communicating Ideas. In: IEEE Transactions on Software Engineering SE—3 (1977).
[41
Schulz, A.: Strukturanalyse der maschinellen betrieblichen Informationsbearbeitung. Berlin 1970.
[42
Schulz, A.: Informatik für Anwender. Berlin, New York 1973.
[43
Schulz, A.: Einführung in das Programmieren in PL/1. Berlin, New York 1975.
[44
Schulz, A.: Höhere PL/1 -Programmierung. Berlin, New York 1976.
[45
Schnupp, P., C. H. Floyd: Software Programmentwicklung und Projektorganisation. Berlin, New York 1976.
[46
SofTech Inc.: An Introduction to SADT Structured Analysis and Design Technique. 9 0 2 2 - 7 8 R . Waltham MA 1976.
[47
Stevens, W. P., G. J. Myers, L. L. Constantine: Structured design. In: IBM Systems Journal 13 (1974).
[48
Van Leer, P.: Top-down development using a program design language. In: IBM Systems Journal 15 (1976).
[49
Warnier, J. D.: Logical Construction of Programs (L. C. P.). Leiden 1974.
[50
Wedekind, H.: Datenorganisation. Berlin 1970.
[51
Wirth, N.: Program Development by Stepwise Refinement. In: Communications of the ACM 14(1971).
174
Literaturverzeichnis
[52] Wulf, W., M. Shaw: Global Variable Considered Harmful. In: SIGPLAN Notices 8 (1973). [53] Yourdon, E., L. L. Constantine: Structured Design. 2. Auflage, New York 1976.
Sach- und Personenregister Ablauforganisation 78, 81, 168 Abschlußtest 9, 153 Abstraktionsstufen 14, 51, 57, 72 Adressierung direkte 139,140 Änderungsfreundlichkeit 11, 12, 13, 49, 52, 56, 113, 134 ff., 140 Andler 67 Anweisung bedingte 30, 38 Arbeitsmittel 50, 51, 74, 83 Arbeitsprozeß 51, 52, 54, 74, 76, 81 Architektur (Programm) 50, 81 Aufbauorganisation 78, 81, 85, 168 Aufgabe 12, 13, 52, 53, 63, 72, 75, 76, 80, 81, 83, 149, 154 Aufgabenanalyse 50, 51, 63, 75, 82 Aufgabendekomposition 52 Aufgabenfluß 149 Aufgabenmodularisierung 52 Aufgabenstellung 49, 50, 51, 52, 63, 75, 81, 82, 83, 84, 114, 140, 149, 150, 153, 159 Aufspaltung 85, 160 Auftrennung von Knoten 40 Ausnahmebedingung 31, 40, 41, 47, 113, 155 - , globale 113, 155 lokale 155, 156 Back-up-Programmiererfunktion 154 Baker 154 Bauelemente 13, 35 ff., 40 Benutzerfreundlichkeit 12, 27, 49, 112, 113 Benutzerschnittstelle 12 Betriebsmittel 11, 13, 52, 159, 160 Betriebssystem 159, 160 bewegte Stammsätze 16, 19, 20, 21, 41, 47, 79, 83, 87, 88, 89, 90, 91, 92,93, 94, 96, 98, 99, 104, 109, 116, 118, 140, 144, 160, 163, 164 Bewegungen ohne Stammsätze 16,17, 19, 20, 21, 41, 47, 48, 71, 79, 83, 88, 89, 90, 93, 94, 96, 99, 100, 104, 109, 116, 118, 134, 135, 139, 140, 145 Bewegungsdatei 15, 16, 19, 20, 21, 28, 41, 42, 48, 74, 83, 88, 89, 90, 93, 95, 96, 101, 115, 116, 122, 134, 135, 139, 140 Bildschirmterminal 102, 168 Bindung, funktionelle 64, 65, 66, 67 kollaterale 67, 70 - , kommunikative 64, 65, 66 - , logische 64, 65, 66, 67 - , prozedurale 64, 65
- , sequentielle 64, 65, 66, 70, 88, 160 - , zeitliche 64, 65, 66, 67 - , zufällige 64 Block 30, 31,41, 155, 156 Blockbegriff 30 BNF-Notation 79 Böhm 28 Bottom-up-Struktur 49 CALL-Anweisung 48, 63, 162 Call by name 161 Call by reference 161,162 Call by value 161,162,163 CASE-Anweisung 30, 57, 58, 169 CASE-Konstruktion 34, 37 CASE-Struktur (Pseudocode) 79 Chefprogrammiererfunktion 154 Chefprogrammiererteam 14, 153, 154 Chu 80 Codierung 112,153 Constantine 60, 62, 63, 64, 65, 66, 67, 68, 69, 70, 72, 78, 88, 120, 160 Constantine-Methode 62 ff., 72, 75, 76, 77, 81
Darstellung des Programmentwurfs 16,53, 57, 60, 68, 69, 70, 72, 73, 74, 77, 78, 85, 86 Dateiende 31, 40, 48, 83, 101, 140 Dateiendearbeiten 48, 101, 119, 120 Dateiendebedingung 16, 19, 21, 31, 48, 113 Dateieröffnung 83, 98, 101, 107, 122, 136, 142 Dateiorganisation, gestreut gespeicherte 140 - , index sequentielle 139 - , sequentielle 57, 76, 83 Dateischließen 16, 83, 90, 93, 94, 96, 98, 99, 107, 108, 140, 144 Dateiverarbeitung 19, 41, 47, 74, 79, 81 ff., 93, 94, 107, 108, 140, 143 - , sequentielle 15, 76, 139 Datenfluß 21, 54, 60, 70, 73, 77, 81, 85, 86, 89, 113, 153 Datenflußgraph 65, 70 Datenflußkopplung 63, 64, 82, 84 Datenstruktur 56, 57, 59, 63, 70, 72, 73, 76, 78, 115, 116, 117 Deadlockproblem 64 Dialogverkehr 102, 168 Dijkstra 28, 29, 30, 31, 50, 102 DO-Gruppe 34
176
Sach- u n d P e r s o n e n r e g i s t e r DO REPEAT-Anweisung 32, 169 DO-REPEAT-Schleife 31 DO UNTIL-Anweisung 3 2 , 1 6 9 DO UNTIL^ Schleife 31, 32, 36, 116 DO WHILE-Anweisung 28, 30, 31, 32, 169 DO WHILE-Schleife 28, 31, 32, 33, 36, 39, 40, 41, 42, 65, 88, 116 DO WHILE-Struktur (Pseudocode) 79 Dokumentation 11, 56, 77, 78, 93, 112, 154, 155 Dokumentationsverfahren 54, 77, 78, 91 Eingangsstelle, sekundäre 30 ELSE-Ausgang 38 ELSE-Zweig 39 Endfile-Arbeiten 90 Endfile-Bedingung 41, 42, 93, 96, 115, 140 Endfile-Maß nähme 90, 93 Endlosschleife 33, 37 Endkontonummer fiktive 16 Entwicklungskosten 9, 51, 68 Entwurfsfehler 9, 93 Entwurfsmethodik 9, 56, 60, 68, 70, 78 Entwurfsphase 49, 50, 54, 56, 60, 81, 85, 88, 91, 93, 95, 113, 115, 153, 154, 160 Entwurfsprozeß 10, 57, 60, 81, 83, 153 Entwurfssprache kompilierbare 95 Entwurfsverfahren 52, 54, 70, 77, 114 Fallunterscheidung 30 Fehlerdichte 28 Fehlerfreiheit 9, 12, 13,49, 80 Fehlerrate 28 Fehlersuche 51 Feinentwurf 54, 56, 70, 81, 85 ff., 93, 104, 114, 120, 135, 139, 150, 151, 153, 164 Festpunktaddition 149, 150, 151, 152 Festpunktmultiplikation 152 Floyd 32, 154 FORTRAN 29, 30 Funktion 12, 50, 51, 54, 65, 66, 70, 74, 81, 89, 117, 160, 161, 162 Funktionalisierung, objektorientierte 83, 84 - , prozeßorientierte 83, 84 Funktionseinheit 19, 73, 74, 75, 81, 84, 85, 86, 87, 88, 89, 90, 93, 135, 149, 151, 152, 153, 160, 161, 162, 163, 164 Funktionsplan 83, 86, 89, 90, 153 - , objektorientierter 83, 84 - , prozeßorientierter 84 globale Variable 64 GOTO-Anweisung 14, 16, 27, 28, 29, 30, 31, 34, 38, 40, 47, 48, 60, 64, 77, 82, 101, 102, 113 Graph, gerichteter 40
Grobentwurf 49, 54, 70, 72, 81 ff., 104, 114, 139, 153 Gruppenwechsel 16, 19, 20, 40, 76, 88, 104, 111, 135, 147, 160, 166 Gruppenwechselarbeiten 19, 20, 76, 88, 89, 90, 96, 111, 134, 135, 161 Gruppenwechselprozeß 19, 41, 88, 90, 102, 135, 160 Hansen 159, 160 Hauptsteuerleiste 113 hierarchische Gliederung 12, 77, 151 HIPO-Diagramm 54, 55, 56, 91, 92 HIPO-Technik 53 ff., 60, 63, 70, 75, 76, 77, 81, 86, 91, 92 IF THEN ELSE-Anweisung 28, 29, 30, 34, 35, 36, 38, 39, 41, 57, 60, 64, 122 IF THEN ELSE-Struktur (Pseudocode) 79 IF THEN GOTO-Konstruktion 29 Input-Section 54, 55, 91 IPT-Methode 153 Iteration 28, 57, 58, 60, 74, 85, 115 Iterationsbedingung 28, 31 Iterationsprozeß 32 Jackson 56, 57, 60, 68, 114 Jackson-Methode 56 ff., 63, 72, 75, 76, 77, 86, 114 ff. Jacopini 28 Knuth 30, 31, 32, 40, 47, 91 Kohäsion 64, 66, 67, 68 Kohäsionsstufen 64, 67 Komplexitätsgrad 67, 68 Kontrollspanne 68 Korrektheit 53, 7 9 , 8 0 , 9 3 , 9 5 Kosiol 52 Kosten 12, 13 Kramer 52 kritische Region 160 Langers 15, 139 LCP-Methode 59 ff. LEA VE-Anweisung 31, 32, 33, 37, 169 Lesbarkeit 1 2 , 1 1 3 Liste 30, 31, 32, 33, 95, 96, 101 Listenende 33, 101 Listenkopf 32, 33, 101 Listenverarbeitung 31, 95, 96, 168 LITOS-Darstellungsmethode 85 ff. LITOS-Methode 60, 80 ff., 93, 114 ff., 139, 141, 149 ff., 159, 160 Magnetband-Dateiverarbeitung 15 ff., 42 ff., 82 ff., 96, 97 ff., 103, 105 ff., 117, 122 ff., 134, 136 ff., 140, 156
Sach- u n d P e r s o n e n r e g i s t e r Magnetplatten-Dateiverarbeitung 15, 139, 140, 141 ff. Markenvariable 30, 34 McGowan 77 Mikroprogramm 149 ff. Mikroprogrammierung 149 ff. Mills 154 Modul 12, 13, 49, 51, 52, 58, 63, 64, 65, 66, 67, 68, 70, 84 Modularisierung 13, 20, 41, 67, 85 Modulbegriff 70, 120 Modulgröße 63, 68 - , optimale 63, 67, 68 Modulkopplung 63, 64, 67, 68 Monitor 160 Multitasking-Funktion 16, 159 Nachfolger, dynamische 41 Nassi-Shneiderman 78 nichtbewegte Stammsätze 16, 17, 19, 20, 21, 41, 42, 47, 48, 79, 83, 88, 89, 90, 91, 93, 94, 96, 98, 99, 100, 104, 109, 116, 118, 139, 140, 145 Node splitting 40 normierte Programmierung 15, 76, 77, 80, 114 Nullprozedur 48 Österle 76 ON-Anweisung 4 0 , 4 1 ON-Bedingung 41 Organisationslehre betriebswirtschaftliche 51, 82 Organisationsschaubild 21, 54, 81, 86 OTHERWISE-Anweisung 35, 170 Output-Section 54, 55, 91 Parnas 52, 75, 83 PL/1-Optimizing Compiler 10, 31, 169, 170 Hanungsphase 50 Platzhalter 104 Preprozessor 95 Process-Section 54, 55, 56 Produktivität (Programmierung) 51, 153, 155, 168 Programm, strukturiertes 29, 30, 40 Programmablaufplan 19, 21, 27, 28, 29, 36, 37, 40, 55, 60, 77, 78 Programmänderung 51, 139, 140, 141 Programmbibliothek 154, 155 Programmdokumentation 10, 11, 12, 52, 53, 56, 92, 93, 114, 134, 136, 141, 153, 168 Programmebene 51 Programmende 40, 83 Programmentwurf 9, 11, 16, 29, 53, 57, 58, 63, 78, 102, 104, 112, 115, 117, 168
177 Programmentwurfssprache 78, 79, 80, 168 Programmierfehler 9, 64 Programmierkosten 9 Programmierphase 9 3 Programmierprojekt 49, 53, 92, 93, 153 ff. Programmierstil 29, 49, 50, 154 Programmierteam 49, 153, 154 Programmiertechnologie 10 Programmierung 9, 12, 21, 29, 49, 63, 92, 93, 104, 112, 113, 114, 120, 153, 154, 155 klassische 17, 19, 27, 28 ff., 40 Programminversion 57, 59 Programmlänge 31, 88 - , dynamische 16, 80 statische 16, 80, 114, 122 Programmlogik 60, 9 3 ff., 120, 122 ff., 134, 139, 140, 141, 153, 168 Programmodul 13, 49, 52, 53, 58, 63, 67, 70, 76, 78, 82, 88, 113, 154, 155 Programmodularisierung 51, 52, 53 Programmnukleus 50 Programmschalter 21, 113 - , binärer 41, 4 8 Programmschleife 30, 31, 32, 38, 57, 155 ff. - , globale 155 ff. - , iterative 28, 32 - , lokale 155 ff. Programmspezifikation 12 Programmsteuerung 30, 41, 82, 93 ff., 116, 120, 140 Programmsteuerungsanweisung 28, 30 Programmstruktur 31, 40, 56, 57, 58, 59, 63, 76, 78, 114, 117, 134 - , hierarchische 50, 59 - , logische 60 Programmstrukturierung 12, 75, 104, 117, 122 dynamische 80 - , statische 80 Programmtest 9, 51, 154 Programmverlängerung 40, 59 Programmverzweigung 28, 30, 31, 102 - , bedingte 28 Programmzusammenführung 28, 36 Projektdokumentation 154 Projektleitung 51, 154 Projektsekretär 154, 155 Prozedur 41, 42, 65, 66, 85, 86, 88, 90, 104, 140, 156, 164 - , externe 35, 85, 161 - , interne 35, 66, 82, 85, 88 Prozeduraufruf, interner 64 - , simultaner 162 Prozeßfluß 16 Prüfverfahren 13, 14
Sach- und Personenregister Pseudocode 78, 79, 80, 95 Qualitätskontrolle 13, 14 REGIONAL (l)-Datei 139,140 Ross 72, 75 RSL (Requirements Statements Language) 13 SADT-Darstellungsmethode 73 SADT-Methode 72 ff., 75, 76, 77, 78, 149 Sammlung 85, 160 Schachtelungstiefe 35 Schaltalgebra 60, 61, 62 Schalter 19, 33, 117, 119 Scheinsätze 140 Schleife 29, 32, 38, 40, 77, 78, 85, 86, 88, 122 - , iterative 32, 88 Schleifenabbruch 32 Schleifenbedingung 31, 32, 39 Schleifensteuerung 31, 32, 93, 155 Schleifensymbol 72 Schleifenvariable 32, 42, 155 Schnittstelle 12, 49, 52, 77, 153, 154, 168 Schnupp 154 Sekretärfunktion 154 selbstdokumentierende Programme 12, 112, 114 SELECT-Anweisung 31, 34, 35, 37, 169, 170 Selektion 57, 60, 72, 74, 115 Sequenz 57, 115 Simulation 13, 16, 80, 93, 95 ff., 104, 153, 168 Simulationsphase 80, 93 Simulationsprogramm 95, 101, 104, 122 Simulationssprache 80, 95 Simultanisier barkeit 16, 161 ff. Simultanisierung 150, 160 ff. Simultanverarbeitung 85, 86, 150, 159 ff. Software 10, 11, 149 Software-Bauelementekunde 14, 28 ff., 48 Softwaredokumentation 11 Software-Engineering 13, 14, 66 Software-Entwurf, aufgabenorientierter 52, 53, 63, 72, 153 - , datenorientierter 56, 60, 63, 75, 76, 77 - , prozeßorientierter 52, 53, 75, 83, 84, 85 - , strukturierter 48, 49 ff., 152, 168 Software-Konstruktionslehre 13, 14, 27, 48, 49 ff., 60, 64, 168 Softwarekosten 9, 134 Softwaretechnologie 12, 13, 60, 155, 168
178 Sortieralgorithmus 32, 33 Sortierfehler 135 Sortierkontrolle 134, 135, 136, 138, 140 Sortierprogramm 32, 33, 34 Sortierprozeß 32, 33 Speicherplatzzuweisung, dynamische 30,65 Spezifikationen 13, 53, 153, 155 sprechende Bezeichner 13, 27, 112 Stammdatei 15, 16, 17, 19, 20, 21, 41, 42, 48, 70, 71, 74, 83, 88, 89, 90, 93, 96, 101, 115, 116, 134, 135, 139, 140, 156 Steuerfluß 16, 21, 30, 31, 54, 55, 60, 64, 70, 74, 75, 77, 80, 81, 85, 86, 88, 92, 93, 113, 153 Steuerflußkopplung 63, 64 Steuerflußlogik 80 Steuerungsanweisung 93, 95 Steuerungslogik 93 ff., 122 ff. Steuerungsteil (Jackson-Methode) 117, 123, 124, 129 Steuerungsvariable 41, 93 Strecke 30, 54 Struktogramm 78 Struktur, alternative 60 - , GOTO-ähnliche 48, 113 - , repetitive 60 Strukturblock 30, 31, 35, 54, 70 Strukturdiagramm 68, 70, 71, 72, 73 strukturierte Organisation 152 strukturierte Programmierung 14, 15, 16, 21, 27, 28, 30, 31, 33, 35, 37, 40, 41, 48, 53, 54, 57, 64, 70, 76, 79, 104 ff., 119, 122, 134, 168 strukturierter Entwurf 21, 53, 54, 63, 68, 69, 72, 76, 77, 78, 86, 96, 140, 155, 160, 168 - , simultaner Prozesse 159 ff. Strukturierung, hierarchische 54, 59, 78, 85 Strukturierungsstrategien 16, 83 Synchronisation 64, 85, 161, 162, 163 Synchronisationsschnitt 160 System, operationales 82, 83, 93, 107, 113, 127, 142 Systemanalyse 50, 93 Systembeschreibungen 11 Systementwurf 75, 149, 152 Technologie 11, 12 Teilaufgabe 49, 50, 51, 52, 54, 61, 63, 67, 75, 81, 83, 84, 86, 87, 88, 89, 149, 153, 154, 160 - , ablauforganisatorische 84 - , informationelle 52, 84, 88 Teilfunktion 19, 21, 41, 76, 81, 83, 87, 88, 90, 93, 96, 104, 116, 117, 134, 139, 149, 150, 151, 152, 160, 163
Sach- und Personenregister
179
Teilprozeß 86, 91 Teilsystem 49 Testen 9, 13, 53, 95 ff., 104, 153, 154 THEN-Zweig 39 Top-down Dekomposition 52, 160 Top-down Design 14, 67, 69 Top-down Entwurf 15, 16, 53 ff., 89, 104, 113, 149, 154, 158, 159, 160, 168 Top-down Programmierung 14, 104 Top-down Testen 104
Van Leer 78, 79, 80 Verarbeitungsanweisungen 30, 40 Vereinbarungssystem 82, 83, 112, 113 Verfeinerung schrittweise 50
Universalität 12 Unterprogramm 20, 40, 96, 120, 135, 162
Zählschleife 32 Zuverlässigkeit 12,13,49
Warnier 59, 60, 75, 76, 77 Wartbarkeit 12, 49, 51 Wartung 92, 113, 134, 154 Wartungskosten 9, 13, 68 WHEN-Anweisung 35, 169, 170 Wirkungsgrad 12 Wirth 50, 52
w DE
G A. Schulz
Walter de Gruyter Berlin-New York Informatik für Anwender Zum Gebrauch neben Vorlesungen und zum Selbststudium GroB-Oktav. 378 Seiten. Mit 67 Abbildungen und zahlreichen Tabellen. 1973. Gebunden DM 48,ISBN 311 002051 3 (de Gruyter Lehrbuch)
A. Schulz
Einführung in das Programmieren in PL/1 Groß-Oktav. 306 Seiten. 1975. Plastik flexibel DM 32,ISBN 311 003970 2 (de Gniyter Lehrbuch)
A. Schulz
Höhere PL/1-Programmierung Groß-Oktav. 214 Seiten. 1976. Plastik flexibel DM 32,ISBN 311 004862 0 (de Gruyter Lehrbuch)
P. Schnupp Ch. Floyd
Software
P. Schnupp
Systemprogrammierung
Programmentwicklung und Projektorganisation GroB-Oktav. 260 Seiten. Mit 74 Abbildungen und 6 Tabellen. 1976. Gebunden DM 58,ISBN 311 005953 3 (de Gruyter Lehrbuch)
GroB-Oktav. 245 Seiten. Mit 103 Abbildungen. 1975. Plastik flexibel DM 32,ISBN 311 004730 6 (de Gruyter Lehrbuch)
P. Schnupp
Rechnernetze Entwurf und Realisierung Groß-Oktav. 193 Seiten. 1978. Gebunden DM 58,ISBN 311 007290 4 (de Gniyter Lehrbuch)
H. Wedekind
Datenorganisation 3. Auflage. Groß-Oktav. 271 Seiten. Mit 152 Abbildungen. 1975. Gebunden DM 34,ISBN 311 005891 X (de Gniyter Lehrbuch)
D. S. Koreimann
Lexikon der angewandten Datenverarbeitung Oktav. 339 Seiten. 1977. Gebunden DM 34,ISBN 311 0069911 Preisänderungen vorbehalten