248 40 11MB
German Pages 181 [184] Year 1982
de Gruyter Lehrbuch Schulz • Methoden des Softwareentwurfs und Strukturierte Programmierung
Arno Schulz
Methoden des Softwareentwurfs und Strukturierte Programmierung 2., bearbeitete und erweiterte Auflage
W DE
G_ Walter de Gruyter • Berlin • New York 1982
CIP-Kurztitelaufnahme
der Deutschen
Bibliothek
Schulz, Arno: Methoden des Softwareentwurfs und strukturierte Programmierung./Arno Schulz. - 2., bearb. u. erw. Auflage. Berlin; New York: de Gruyter, 1982. (De-Gruyter-Lehrbuch) ISBN 3-11-008895-9
© Copyright 1982 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: Karl Gerike, Berlin. Bindearbeiten: Dieter Mikolai, Berlin. - Printed in Germany.
Vorwort zur 1. Auflage 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 darauf hin, 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 für 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.5 3-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
Vorwort zur 2. Auflage Die nach kurzer Zeit notwendige zweite Auflage dieses Buches zeigt, daß :n der Praxis ein großes Interesse an Methoden der Software-Konstruktionslehre - auch Software-Engineering genannt - besteht. Obwohl diese junge Ingenieurdisziplin gerade eine stürmische Entwicklungsphase durchläuft, schien es wegen der kurzen, seit dem Erscheinen der ersten Auflage verflossenen Frist nicht sinnvoll zu sein, eine völlige Neubearbeitung dieses Buches vorzunehmen. Gegenüber der ersten Auflage wurde der Text deshalb nur aktualisiert und ergänzt. In Diskussionen mit Praktikern wurde häufig angeregt, die im Kapitel 5 dargestellten wichtigsten Software-Entwurfsmethoden vertieft zu behandeln und sie einem Vergleich zu unterziehen. In dieses Kapitel wurde weiterhin das Geheimnisprinzip von Parnas, das sich jetzt auch immer mehr in der Anwendungsprogrammierung durchsetzt, aufgenommen. Dieser Entwicklung mußte unser eigener Ansatz, die LITOS-Methode, angepaßt werden. Diese 2. Auflage widme ich meinem hochverehrten Lehrer in „Computer Science", Herrn Prof. Dr. Karl Ganzhorn, dem ich meine „Lehrzeit" verdanke. im Juni 1982
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 Entwurfsstrategien 5.2 Methoden des Top-down Entwurfs 5.2.1 Funktionsbezogener Entwurf (HIPO-Technik) 5.2.2 Datenstrukturorientierter Entwurf 5.2.2.1 Die Jackson—Methode 5.2.2.2 Die Wamier-Methode , 5.2.3 Aufgabenorientierter Entwurf 5.2.3.1 Die Constantine-Methode 5.2.3.1.1 Grundlagen 5.2.3.1.2 Das Entwurfsverfahren 5.2.3.2 Die SADT-Methode 5.2.4 Das Geheimnisprinzip von Parnas 5.3 Methodenvergleich
49 49 56 56 67 67 72 78 78 78 84 89 92 94
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
28 28
100 100 101 101 104 112
8
Inhalt 6.2.4 Simulation und Test der Programmsteuerung 6.3 Strukturierte Programmierung der Fallstudie 6.4 Zur Änderungsfreundlichkeit von LITOS-Produkten
115 127 137
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
152 152 156 158 163
8. Ausblick
170
Anhang Literaturverzeichnis Sachregister
171 173 177
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 Prograi.imierkosten 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 riiaterieller 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-
2. Programmierung als Softwaretechnologie
12
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 für 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 Ausführungen 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 Magnetplattendateicn 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.4 wird eine Lösung für das Verfahren „B" angegeben.
16
3. Einführung in eine Fallstudie
Je nach dem Vergleichsergebnis: gleich (bewegte Starnmsä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 Verfügung standen. Sie resultierten aus der Syntax der verwendeten Programmiersprache. Außerdem sollte jede Programmiertätigkeit damit beginnen, daß der Program»
ALTER SALDO
000001
H
10188.50
BELEG-NR. 1005
DATUM
SOLL
711020
HABEN
NEUER SALDO
S T A M M S A T Z
H 000002
H
2905.00
000003
S
13713.91
000001
S
1170.80
000005
H
1103.11
000020
0.00
BEWEGTER
1005.50 11191,00
711011 711011
NICHT-
sr
B E W E 6 T E
711023
8533
711020
8531
711020
9000.01 8000.01
BEWEGUNGEN 0 H N E
„ H
Abb. 2 Druckliste der Dateiverarbeitung im „Verfahren A".
„„ STAMMSATZ 1000.00
18
3. Einführung in eine Fallstudie
C ANFANG 3 — Y I Ü W H - ^
FVERARBEITUNG f bEWEGUNGSV ^ SÄTZE
/NICHTBEWEGTE [ STAMMSÄTZE U VÊRUPPENWECHSE
Abb. 3
(Fortsetzung S. 1 9 )
: SYMBOL F. ZUSAMMENFÜHRUNG (NICHT DIN 66 001)
ON E N D F I L E - T E I L
l
(BEWEGUNGSDATEI)
EMDE
B 999999
THEN
ELSE
GRUPPENWECHSEL
NICHTBEWEGTE
BEWEGUNGEN OHNE
UND GRUPPENWECHSEL
STAMMSÄTZE
STAMMSÄTZE
CJÜL) Abb. 3 Piogiammablaufplan 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 „ E O F " (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. Gruppenwechselarbeiten (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 durchzufuhren, 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 „PUFFERSTAMMSATZ" 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 l " 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 Programmausführung 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: „Ende 1"). 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
PROC OPTIONS(MAIN);
*/
DCL LEINGAB F I L E RECORD I N P U T ,
/ * LOCHKARTENEINGABE
*/
MEINGAB F I L E RECORD I N P U T ,
/ * MAGNETBANDEINGABE
*/
MAUSGAB F I L E RECORD OUTPUT,
/ * MAGNETBANDAUSGABE
*/
LISTE
F I L E RECORD OUTPUT ENV ( F ( 8 8 ) /*
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
PIC'S9999V99',
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 .
STAMMSATZ
*/
DCL 1 PUFFER_STAMMSATZ LIKE STAMMSATZ; / * VEREINBARUNG UEBERSCHRIFTZEILE
*/
DCL 1 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(10)
INIT
('HABEN'),
SALDO'),
3.2 Eine klassische Lösung
23
2 NEUER_SALDO
CHAR(12) INIT
('NEUER SALDO');
/* VEREINBARUNG DRUCKLISTE */ DCL 1 ZEILE, 2 VORSCHUBSTEUERUNG
CHAR(1)
2 LEERZEICHEN^
CHAR(3)
2 KONTONUMMER
CHAR(6)
2 LEE RZ EICHEN_2
CHAR(5)
2 A N Z E I G E _ S O L L J HABEN
CHAR(1)
2 ALTER_SALDO
PIC' (6) Z 9 V . 9 9 '
2 LEERZEICHEN_3
CHAR(9)
2 BELEG_NUMMER
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'
2 LEERZEICHEN_7
CHAR(2)
2 ANZ EIGE_SOLL_HABEN_NEU
CHAR(1)
2 NEUER_SALDO
PIC'(6)Z9V.99'
2 REST
CHAR(4)
/* BLANKS NACH ZEILE */ DCL ZEILE_1 CHAR (88) DEFINED ZEILE; DCL (SCHALTER,SCHALTER_1) OPEN FILE
(LEINGAB),
FILE
(MEINGAB),
FILE
(MAUSGAB),
FILE
(LISTE);
BIT(1) INIT
('O'B);
/* DRUCKEN UEBERSCHRIFTZEILE */ WRITE FILE (LISTE) FROM
(UEBERSCHRIFT);
/* DRUCKEN 1 LEERZEILE */ ZEILE_1 = *0'; WRITE FILE (LISTE) FROM (ZEILE); ON ENDFILE (LEINGAB) GOTO ENDE;
24
3. Einführung in eine Fallstudie
ON ENDFILE
(MEINGAB)
BEGIN;
I F KONTONUMMER_BEWEGUNG =
'999999'
THEN GOTO ENDE_1; ELSE DO; SCHALTER_1 =
* 1'B;
GOTO NEUE R_STAMM; END; END; ANFANG:
READ F I L E
(LEINGAB)
INTO
(LOCHKARTENSATZ);
READ F I L E
(MEINGAB)
INTO
(STAMMSATZ);
=
'0';
ANFANG_1:ZEILE_1
ZEILE.KONTONUMMER = STAMMSATZ.KONTONUMMER; ZEILE.DATUM
= STAMMSATZ.DATUM;
ZEILE.ALTER_SALDO = IF
STAMMSATZ.SALDO;
STAMMSATZ.SALDO > O THEN ANZEIGE_SOLL_HABEN =
'H';
ELSE IF
STAMMSATZ.SALDO < O THEN ANZEIGE_SOLL_HABEN = ELSE ANZEIGE_SOLL_HABEN =
'S'; '
';
VERARBEITUNG_BEWEGUNGSSAETZE: I F KONTONUMMER_BEWEGUNG = STAMMSATZ.KONTONUMMER THEN DO; / * VERARBEITEN BEWEGUNGSSATZ
*/
STAMMSATZ.SATZART = KARTENART; STAMMSATZ.DATUM
= DATUM_BEWEGUNG;
STAMMSATZ.SALDO
= STAMMSATZ.SALDO
+
BET RAG_BEWEGUNG; /*
RESTLICHE DRUCKZEILE ERSTELLEN UND DRUCKEN
*/
ZEILE.BELEG_NUMMER= ZEILE.DATUM IF
BELEGNUMMER_BEWEGUNG;
= DATUM_BEWEGUNG;
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 = ' 1'B; READ FILE (LEINGAB) INTO (LOCHKARTENSATZ); GOTO VERARBEITUNG_BEWEGUNGSSAETZE; END; NICHTBEWEGTE_STAMMSAETZE_UND_GRUPPENWECHSEL: IF KONTONUMMER_BEWEGUNG > STAMMSATZ.KONTONUMMER THEN DO; IF SCHALTER THEN DO; /* GRUPPENWECHSELARBEITEN */ 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 ANZEIGE_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; ZEILE.KONTONUMMER, PUFFER_STAMMSATZ.KONTONUMMER = KONTONUMME R_ BEWEGUNG; PUFFER STAMMSATZ.SALDO = O;
26
3. Einführung in eine Fallstudie
ZEILE.ALTER_SALDO - O; /* VERARBEITEN BEWEGUNGEN_OHNE_STAMMSAETZE */ BEWEGUNGEN_OHNE_STAMMSAETZE: PÜFFER_STAMMSATZ.DATUM = DATUM_BEWEGUNG; PUFFER_STAMMSATZ.SALDO = PÜFFER_STAMMSATZ. SALDO + BET RAG_BEWEGUNG; ZEILE.BELEG_NUMMER - BELEGNUMMER_ BEWEGUNG; - DATUM_BEWEGUNG;
ZEILE.DATUM IF BETRAG_BEWEGUNG > O THEN ZEILE.HABEN = BETRAG_BEWEGUNG; ELSE = BETRAG_BEWEGUNG; Z EILE.SOLL WRITE FILE (LISTE) FROM (ZEILE); ZEILE_1 - • '; READ FILE (LEINGAB) INTO (LOCHKARTENSATZ); IF KONTONUMMER_BEWEGUNG = PUFFER_STAMMSATZ. KONTONUMMER THEN GOTO BEWEGUNGEN_OHNE_STAMMSAETZE; GRUPPENWECHSEL_BEWEGUNGEN_OHNE_STAMMSAETZE: ZEILE.NEUERJSALDO = PUFFER_STAMMSATZ. SALDO; IF PUFFER_STAMMSATZ.SALDO > 0 THEN ANZEIGE_SOLL_HABEN_NEU « 'H'; ELSE IF PUFFER_STAMMSATZ.SALDO < 0 THEN ANZEIGE_SOLL_HABEN_NEU = 'S'; ELSE ANZEIGE_SOLL_HABEN_NEU = ' *; WRITE FILE (LISTE) FROM (ZEILE);
27
3.2 Eine klassische Lösung ZEILE_1 WRITE FILE
* ' '; (MAUSGAB) FROM
(PUFFER_STAMMSATZ);
IF SCHALTER_1 = '1'B THEN GOTO ANFANG; ELSE GOTO ANFANG_1 ; ENDE: KONTONUMMER_BEWEGUNG
=
'999999';
IF SCHALTER THEN GOTO NICHTBEWEGTE_STAMMSAETZE_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 daraufhinwies, 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 möglichen 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 Programmzusammenführung. 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 überfuhren, die von den beiden Bedingungen „B = S" und ,,-i EOF Bewegungsdatei" gesteuert wird. Trotzdem wäre es
4.1 Strukturierte Programmierung
29
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 T H E N 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 F O R T R A N . In diesem Fall kommt er nicht umhin, eine Schleife durch eine IF T H E N 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 " , die auch Datensätze sein können. 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 Ende wiederum eine Variable „ A " gelesen. Um diese Programmschleife zu schließen, muß die Programmausführung
IF.EINGAI THEN
ELSE
( J
PROCESS A, READ A; GOTO IF_ EINGANG;
ZUSAMMENFÜHRUNS
Abb. 4
IF T H E N GOTO-Konstruktion zur Schleifenbildung.
30
4. Kritik der herkömmlichen Programmierung
am Ende dieses Weges mit einer GOTO-Anweisung 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. Elshoff hat 120 konventionelle PL/1-Programme mit 34 Programmen verglichen, die nach den Regeln der strukturierten Programmierung erstellt wurden [17a, S. 364 ff.]. Er fand, daß die Häufigkeit der GOTO-Anweisung innerhalb des gesamten Anweisungsspektrums von 11,7 % auf 2,8 % zurückgeht, also auch moderne Programme nicht ganz ohne diese Anweisung auskommen. Es ist deshalb sinnvoll, zwischen schädlichen GOTO-Anweisungen im Sinne von Dijkstra und solchen, die konstruktiv bedingt und damit unvermeidbar sind („unschädliche" GOTOAnweisungen), 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 Speiche rplatzzu Weisung). 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, 1
zur Definition s. [44, S. 154]
4.1 Strukturierte Programmierung
31
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 Verzweigungen 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-An Weisungen 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 LEA VE-Anweisung2. An einigen kleinen Beispielen soll jetzt kurz ihre Funktion und ihre Bedeutung für die strukturierte Programmierung dargestellt werden. Die DO UNTIL-Schleife (nichtabweisende Schleife) unterscheidet sich von der „herkömmlichen" DO WHILE-Schleife (abweisende 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) ; 1 = 1+ 1000 ; A = 9876.54 ; B = 1234.56 ; A = A -I ; END; Verwendet ein Programmierer die DO WHILE-Schleife, dann muß er häufig, bevor die Programmausfuhrung 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 2
formale Definition s. Anhang
4. Kritik der herkömmlichen Programmierung
32
Programmierer im obigen Programmausschnitt, wenn er die D O WHILE-Anweisung verwendet, vor dem ersten Schleifendurchlauf den beiden Variablen A u n d B Anfangswerte in der Weise zuweisen, daß die Schleifenbedingung A > B im ersten Durchlauf gilt. Um zwei verschiedene Schleifenkriterien unabhängig voneinander anwenden zu k ö n n e n , läßt die Sprachsyntax jetzt auch zu, daß iterative Schleifen gemeinsam von einer DO WHILE- u n d einer D O UNTIL-Anweisung gesteuert werden (s. Anhang). Beispiel:
DO WHILE (A) UNTIL (B);
Die strukturierte Programmierung verfolgt das Ziel, klare Programmstrukturen zu erzielen. Es wäre deshalb besser, solche Mischformen zu vermeiden. Auf das obige Beispiel übertragen, resultiert aus dieser Regel folgende N o t a t i o n : DO WHILE (A & - B); In der DO WHILE-Schleife u n d der DO UNTIL-Schleife k a n n der Zustand des Iterationsprozesses nur am Anfang oder am Ende der Schleife abgefragt werden. K n u t h und Floyd haben d a r a u f h i n g e w i e s e n , daß es in der Praxis aber Fälle gibt, w o es notwendig ist, die Schleifenbedingung an jeder beliebigen Stelle innerhalb der Schleife überprüfen zu k ö n n e n , 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 z u m Verlassen der Schleife. In PL/1 bewirkt eine bedingungslose Schleifenwiederholung die N o t a t i o n D O WHILE ( V B ) oder D O U N T I L ('O'B); d e n n nach der PL/1-Sprachsyntax wird j e d e WHILE-Bedingung in eine Bitkette konvertiert und die Schleife so lange d u r c h l a u f e n , 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 R E P E AT 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 R E P E A T K + 1;
Natürlich m u ß 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 T H E N LEA VE;
Sobald die IF-Abfrage positiv b e a n t w o r t e t wird, führt der LEAVE-Teil z u m Schleifenabbruch.
33
4.1 Strukturierte Programmierung
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 ändert, 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 LISTENSORTIEREN:
PROC(ZEIGER); DCL
(P,Q,R,
/* H I L F S Z E I G E R F Ü E R S O R T I E R P R O Z E S S ZEIGER) 1 SATZ
POINTER,
BASED(ZEIGER_1),
2 WORT
CHAR(10),
2 ZEIGER_2
POINTER,
S C H A L T E R BIT(1)
INIT('O'B)
/* ZUR P R U E F U N G OB S O R T I E R P R O Z E S S ABGESCHLOSSEN ENDLOSSCHLEIFE:
*/;
DO K - 0 REPEAT K = 1; ZEIGER_1, Q = ZEIGER; R = Q -> Z E I G E R _ 2 ; SCHALTER =
'O'B;
*/
34 SORTIERSCHLEIFE:
4. Kritik der herkömmlichen Programmierung DO WHILE /*
(R 1 =
NULL());
AUFSTEIGENDE SORTIERFOLGE
DO WHILE
(R - > WORT > Q - > P -
Q;
Q -
R;
R = R ->
*/
WORT);
ZEIGER_2;
I F SCHALTER = R = NULLO
'O'B &
THEN
LEAVE ENDLOSSCHLEIFE; IF
R = NULLO THEN
LEAVE SORTIERSCHLEIFE; END; SCHALTER =
"TB;
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; vom Listenkopf bis zum Listenende vollständig durchsucht hat, ohne eine Vertauschung vorgenommen zu haben. Außerdem muß die Programmausführung die Sortierschleife auch dann verlassen, wenn sie das Listenende erreicht hat. Damit läßt sich ein Programm „LISTENSORTIEREN" mit zwei LEA VE-Anweisungen schreiben. Der „SCHALTER" in diesem Programm gibt an, ob beim Durchlaufen der innersten Schleife eine Vertauschung von Zeigern vorgenommen wurde.
4.1 Strukturierte Programmierung
35
Die Sprachsyntax läßt zu, nach der LEA VE-Anweisung eine „markenkonstante" zu notieren (s. Anlage). In dieser Weise wirkt sie als GOTO-Anweisung. Damit widerspricht aber die „markenkonstante" den Regeln der strukturierten Programmierung, indem die Programmausführung nach dem Verlassen einer Schleife irgendwohin springt. Um eine lineare Programmstruktur zu erreichen, sollte die Programmausführung unmittelbar nach der Endlosschleife fortsetzen. Dies bedeutet, daß man die LEAVE-Anweisung ohne eine „markenkonstante" verwenden sollte. 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 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 „MAGNETBANDDATEIVERARBEITUNGKLASSISCH"). SELECT; WHEN (SIGN(STAMMSATZ .SALDO) = 1) ANZEIGE SALDO = 'H'; WHEN (SIGN(STAMMS ATZ .SALDO) = - 1 ) A N Z E I G E S A L D O = ' S ' ; OTHERWISE ANZEIGE SALDO = ' '; END; IF-Lösung: IF STAMMSATZ .SALDO > 0 THEN ELSE IF STAMMSATZ .SALDO < 0 THEN ELSE
ANZEIGE SALDO = '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:
4. Kritik der herkömmlichen Programmierung
36 (1)
Strukturblock: Sonderform: BEGIN;
(2)
(3)
END;
Reihung von Strukturblöcken:
IF
THENELSE-Anweisung: Sonderform: IF THEN-Anweisung
(4)
3
DO WHILESchleife
(Abfrage am Schleifenbeginn):
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 (151 benutzt.
4.1 Strukturierte Programmierung (5)
DO JJNTIL-Schleife (Abfrage am Schleifenende):
(7)
Endlos-Schleife
mit Unterbrechung (LEAVE-Anweisung):
37
38
4. Kritik der herkömmlichen Programmierung
Die erste Verzweigung in diesem Ablauf, die nur einen Ausgang hat und deshalb gar keine echte Verzweigung ist, repräsentiert die bedingungslose Schleifenwiederholung, z.B. DO WHILE O ' B ) . Mit Hilfe dieses Repertoires an Symbolen könnte man versuchen, nichtstrukturierte Programmablaufpläne graphisch nach den Regeln der strukturierten Programmierung umzugestalten. Allerdings entspricht ein solches Vorgehen nicht den Intentionen der strukturierten Programmierung (s.S. 29). Beispiel:
Es sei folgender Programmablaufplan gegeben.
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 Programmausfiihrung, nachdem „E" durchlaufen wurde, wiederum mit einem be-
4.1 Strukturierte Programmierung
39
dingungslosen 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.
40
4. Kritik der herkömmlichen Programmierung
Der umstrukturierte Programmablauf beginnt mit der Abfrage, ob X den Wert 'O'B oder 'l'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 Programmablauf 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 die 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 muß 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. Dabei wird angenommen, daß der Programmierer, der dieses Programm schreibt, über keine großen Programmiererfahrungen verfügt, beispielsweise ein Student ist, dem nur gesagt wird, daß er keine GOTO-Anweisungen verwenden darf. An Hand dieses Beispiels 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 muß, wenn Ausnahmefälle
4.2 Ein erster Versuch
41
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-Anweisunß in PL/1: ON ENDFILE (LK) GOTO ENDE; In diesem Beispiel verzweigt die Programmausführung beim Ende der Lochkartendatei „LK" zur Marke "ENDE", wo die Dateiendeaktionen zu finden sind. ONAnweisungen haben im Gegensatz zu den üblichen Verarbeitungsanweisungen 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 aufruft, 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 „BEWEGTE STAMMSAETZE" (Ordnungsmerkmal Bewegungsdatei = Ordnungsmerkmal Stammdatei), „NICHTBEWEGTE STAMM SAETZE UND GRUPPENWECHSEL" (Ordnungsmerkmal Bewegungsdatei > Ordnungsmerkmal Stammdatei), „BEWEGUNGENOHNESTAMMSAETZE" (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. Abbüdung 2), führt die Prozedur „DRUCKAUFBEREITUNG_
42
4. Kritik der herkömmlichen Programmierung
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 „VERARBEITENBEWEGUNGSDATEN", 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 ,$EWE GUNGEN_OHNE_STAMMSAETZE" aus mit dem Unterschied, daß die Verarbeitung im Hauptspeicher in einem eigenen Speicherbereich erfolgt („PUFFER STAMMSATZ"). Schließlich liegt auch die Steuerung der Prozedur „NICHTBE WEGTESTAMMSAETZEUNDGRUPPENWECHSEL" 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 „MAGNETBAND_DATEIVERARBEITUNG _STRUKTURIERT_VERSION_l" verständlich. MAGNETBAND_DATEIVERARBEITUNG_S TRUKTURIERT_VERSION_1: PROC OPTIONS
(MAIN)
/* DATEIVEREINBARUNGEN, VEREINBARUNG LOCHKARTENSATZ, STAMMSATZ, UEBERSCHRIFT, ZEILE,ZEILE_1 IDENTISCH MIT PROGRAMM MAGNETBAND_DATEIVERARBEITUNG_KLASSISCH / * VEREINBARUNG SCHALTER DCL (SCHALTER / * GRUPPENWECHSEL SCHALTER_1 / * DATEIENDE * / ) DCL
*/
*/
*/, BIT(1)
INIT('O'B);
(ENDFILE_BEDINGUNG_MAGNETBAND_EINGABE, ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE)
BIT(1) I N I T ( ' O 1 B ) ; /******************************************************* OPEN FILE
(LEINGAB),
FILE
(MEINGAB),
FILE
(MAUSGAB),
FILE
(LISTE);
4.2 Ein erster Versuch
43
/* DRUCKEN UEBERSCHRIFTZEILE */ WRITE FILE (LISTE) FROM
(UEBERSCHRIFT);
/* DRUCKEN 1 LEERZEILE */ ZEILE_1 = 'O'; WRITE FILE (LISTE) FROM ON ENDFILE
(ZEILE);
(LEINGAB)
ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE = '1'B; ON ENDFILE (MEINGAB) ENDFILE_BEDINGUNG_MAGNETBAND_EINGABE READ FILE
(LEINGAB) INTO
= '1'B;
(LOCHKARTENSATZ);
READ FILE (MEINGAB) INTO (STAMMSATZ); /*******************************************************/ 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 KONTONUMMER_BEWEGUNG < STAMMSATZ. KONTONUMMER THEN CALL BEWEGUNGEN_OHNE_STAMMSAETZE; END DATEIVERARBEITUNG; BEWEGTE_STAMMSAETZE: PROC; CALL D RUC KAUF BE REITUNG_LINKS; CALL VERARBEITEN_BEWEGUNGSDATEN; END BEWEGTE_STAMMSAETZE; VERARBEITEN_BEWEGUNGSDATEN: PROC; DO WHILE (KONTONUMMER_BEWEGUNG « STAMMSATZ. KONTONUMMER & ENDFILE BEDINGUNG BEWEGUNGSDATEN EINGABE = 'O'B);
44
4. Kritik der herkömmlichen Programmierung STAMMSATZ.SATZART =
KARTENART;
STAMMSATZ.DATUM
«
DATUMJ3EWEGUNG;
STAMMSATZ.SALDO
= STAMMSATZ.SALDO
+
BET RAG_BEWEGUNG; CALL D RUCKAUFBE REITUNG_REC HTS_UND_DRUCKEN; SCHALTER
-
READ F I L E
(LEINGAB)
'1'B; INTO
(LOCHKARTENSATZ);
END BEWEGUNGSSCHLEIFE; I F ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE
«=
'1'B
THEN CALL F I N A L I S I E R U N G ; END VERARBEITEN_BEWEGUNGSDATEN; NICHTBEWEGTE_STAMMSAETZE_UND_GRUPPENWECHSEL: DO WHILE
(KONTONUMMER_BEWEGUNG >
PROC R E C U R S I V E ;
STAMMSATZ. KONTONUMMER &
ENDFILE_BEDINGUNG_MAGNETBAND_EINGABE IF /*
=
'O'B);
SCHALTER THEN DO;
SCHALTER = STAMMSAETZE
'1'B
GRUPPENWECHSEL,
'O'B
NICHTBEWEGTE
*/
I F STAMMSATZ.SALDO > O THEN ANZEIGE_SOLL_HABEN_NEU =
'H';
ELSE IF
STAMMSATZ.SALDO < O THEN ANZEIGE_SOLL_HABEN_NEU =
'S';
ELSE AN Z EIGE_SOLL_HABEN_NEU
=
ZEILE.NEUER_SALDO = SCHALTER =
'
';
STAMMSATZ.SALDO; 'O'B;
END; E L S E DO; CALL
DRUCKAUFBEREITUNG_LINKS; ZEILE.DATUM = END;
STAMMSATZ.DATUM;
4.2 Ein erster Versuch
45
WRITE FILE (LISTE) FROM (ZEILE); /* DRUCKEN SALDO UND NICHTBEWEGTE STAMMSAETZE */ WRITE FILE (MAUSGAB) FROM (STAMMSATZ); READ FILE (MEINGAB) INTO (STAMMSATZ); END SCHLEIFE; IF ENDFILE_BEDINGUNG_MAGNETBAND_EINGABE = " T B THEN DO; IF SCHALTER_1 = '1'B THEN CALL ENDE; ELSE CALL BEWEGUNGEN_OHNE_STAMMSAETZE; END; END NICHTBEWEGTE_STAMMSAETZE_UND_GRUPPENWECHSEL; /*******************************************************, BEWEGUNGEN_OHNE_STAMMSAETZE: PROC; ZEILE_1 = 'O'; PUFFER_STAMMSATZ.SATZART = KARTENART; ZEILE.KONTONUMMER, PUFFER_STAMMSATZ.KONTONUMMER = KONTONUMME R_BEWEGUNG; PUFFER_STAMMSATZ.SALDO = 0; ZEILE.ALTER_SALDO = 0; DO WHILE (KONTONUMMER_BEWEGUNG = PUFFER_ STAMMSATZ.KONTONUMMER & ENDFILE_ BEDINGUNG_BEWEGUNGSDATEN_EINGABE = ' 0' B) ; PUFFER_STAMMSATZ.DATUM
=DATUM_ BEWEGUNG; PUFFER_STAMMSATZ.SALDO = PUFFER_ STAMMSATZ.SALDO + BET RAG_BEWEGUNG; CALL DRUCKAUFBEREITUNG_RECHTS_UND_ DRUCKEN;
READ FILE (LEINGAB) INTO (LOCHKARTEN SATZ); END SCHLEIFE; ZEILE.NEUER SALDO = PUFFER STAMMSATZ.
46
4. Kritik der herkömmlichen Programmierung SALDO; I F PUPFER_STAMMSATZ.SALDO > 0 THEN AN Z EIGE_SOLL_HABEN_NEÜ =
'H';
ELSE I F PÜFFER_STAMMSATZ.SALDO < 0 THEN ANZEIGE_SOLL_HABEN_NEU =
'S';
ELSE AN Z EIGE_SOLL_HABEN_NEU = ' WRITE F I L E
(LISTE)
WRITE F I L E
(MAUSGAB) FROM
' ;
FROM Z E I L E ; ZEILE_1 = •
•;
(PUFFER_ STAMMSATZ);
I F ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE
-
'1'B
THEN CALL FINALISIERUNG; END BEWEGUNGEN_OHNE_STAMMSAETZE; /***************************************************** DRUCKAUFBEREITUNG_LINKS s PROC; ZEILE_1 =
'O';
ZEILE.KONTONÜMMER «
STAMMSATZ.KONTONUMMER;
ZEILE.ALTER_SALDO = STAMMSATZ.SALDO; I F STAMMSATZ.SALDO > O THEN AN Z EIGE_SOLL_HABEN =
'H'; ELSE
I F STAMMSATZ.SALDO < O THEN ANZ EIGE_SOLL_HABEN =
'S'; ELSE
AN Z EIGE_SOLL_HABEN = '
';
END DRUCKAUFBEREITUNG_LINKS; DRUCKAUFBEREITUNG_RECHTS_UND_DRUCKEN: / * DRUCKEN BEWEGUNGSKARTEN
PROC;
*/
ZEILE.BELEG_NUMMER = BELEGNUMMER_BEWEGUNG; ZEILE.DATUM = DATUM BEWEGUNG;
4.2 Ein erster Versuch
47
I F BETRAG_BEWEGUNG > 0 THEN ZEILE.HABEN = BETRAG_BEWEGUNG; ELSE ZEILE.SOLL WRITE F I L E END
BETRAG_BEWEGUNG;
(LISTE)
FROM
ZEILE_1
=
'
(ZEILE); ';
DRUCKAUFBEREITUNG_RECHTS_UND_DRUCKEN;
FINALISIERUNG: PROC; KONTONUMME R_BEWEGUNG « SCHALTER_1 =
1
9 99999 ' ;
"MB;
CALL NICHTBEWEGTE J3TAMMSAETZE_UND_GRUPPENWECHSEL; END FINALISIERUNG; /******************************************************/ ENDE:
PROC; END
MAGNETBAND_DATEIVERARBEITUNG_STRUKTURIERT_ 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 4714
4711 4715
4711 4716
DATEIVERARBEITUNG
Programmablauf bei Dateiende
BEWEGTE_STAMMSAETZE
FINALISIERUNG ->• NICHTBEWEGTE STAMMSAETZE ->2 x DO WHILE Schleife -*• ENDE
BEWEGTE_ ST AMMS AETZE
FINALISIERUNG NICHTBEWEGTE STAMMSAETZE -»• 1 x DO WHILE Schleife -»• ENDE
NICHTBEWEGTE_ STAMMSAETZE
BEWEGUNGEN OHNE STAMMSAETZE
BEWEGUNGEN_OHNE_STAMM SAETZE ->• FINALISIERUNG -> NICHTBEWEGTE_STAMMSAETZE (DO WHILE Schleife wird übergangen) ENDE FINALISIERUNG NICHTBEWEGTE STAMMSAETZE - > l x D O WHILE Schleife -»-ENDE
48
4. Kritik der herkömmlichen Programmierung
Dieses Beispiel ist im Grunde genommen eine Persiflage auf die GOTO-loseProgrammierung. Es 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 Erreichen 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 „NICHTBEWEGTE STAMMSAET 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 CALL-Anweisungen könnte der Programmierer auch durch GOTO-Anweisungen ersetzen. Diese Erscheinung haben Knuth und Floyd bereits 1971 beobachtet [27, S. 23 ff.]. Sie weisen daraufhin, daß ein GOTO-ähnlich strukturiertes Programm rekursive Prozeduren verwendet und mit einer Nullprozedur endet, zwei Merkmale, die auch das obige Beispiel aufweist. Aus diesem Programmierbeispiel kann geschlossen werden, daß der Programmierer das Ziel, eine Aufgabe in einen übersichtlichen linearen Programmablauf umzusetzen, mit dem Instrumentarium der Software-Bauelementelehre allein schwer erreichen kann. Sie bietet ihm nur Programmbausteine an, ohne ihr Zusammenspiel in einem größeren System aufzuzeigen. So bemerkt auch Jackson, daß es nicht ausreicht zu verlangen, den Steuerfluß von Programmen allein mit Hilfe der DO WHILE-Anweisung und der IF THEN ELSE-Anweisung zu konzipieren, ohne nicht auch gleichzeitig zu sagen, wie diese Konstruktionselemente in ein größeres Programm einzubringen sind [25, S. 2]. Eine solche Bedingung überfordert auch den erfahrenen Programmierer. Es wird deshalb eine Software-Konstruktionslehre benötigt, 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 Entwurfsstrategien 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. Dieses Entwurfsprinzip, das 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 zunächst unabhängig von einer Programmiersprache die „Architektur" eines Pro-
50
5. Strukturierter Softwareentwurf
grammes 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 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
Abb. 5 Prinzip des strukturierten Entwurfs.
5.1 Entwurfsstrategien
51
Abstraktionsstufen genannt, die nach oben hin größere Aufgabennähe und nach unten Realisierung bedeuten. Auf diesem „Abstraktionsstufenkonzept" basieren die meisten Konstruktionsmethoden des modernen Softwareentwurfs. Es stellt quasi eine Minimalforderung dar, die zu erfüllen ist, damit ein Programmentwurf das Attribut „systematisch" tragen kann. Es soll deshalb jetzt dieses Abstraktionsstufenkonzept an Hand der Fallstudie des Kapitels 3.1 exemplarisch dargestellt werden. Jede Dateiverarbeitung läßt sich auf drei Teilaufgaben abbilden, nämlich: 1.
die Arbeiten, die am Beginn eines Programmlaufs durchzuführen sind, wie das Eröffnen der Dateien, das Lesen des ersten Bewegungs- und Stammsatzes
2.
die eigentliche
3.
die -ßncfarbeiten, nachdem in der Bewegungsdatei oder der Stammdatei das Dateiende erreicht wurde.
Dateiverarbeitung
Formal könnte man deshalb die 1. Abstraktionsstufe wie folgt beschreiben: begin Abstraktionsstufe-1 Dateieröffnung Dateiverarbeitung Dateischließen end Abstraktionsstufe-1 Auf der Abstraktionsstufe-2 sind die drei Teilaufgaben der übergeordneten Abstraktionsstufe zu präzisieren. Hierbei wird man natürlich, falls notwendig, auf die Konstruktionselemente der strukturierten Programmierung zurückgreifen (die Endlosschleife wird in diesem Beispiel als „do forever" markiert). begin Abstraktionsstufe-2 begin Dateieröffnung open STAMMDATEI, BEWEGUNGSDATEI read STAMMSATZ read BEWEGUNGSSATZ end Dateieröffnung begin Dateiverarbeitung do forever if B = S then Funktion BEWEGTE_STAMMSAETZE if eof STAMMDATEI | eof BEWEGUNGDATEI then leave if B > S then Funktion NICHTBEWEGTE_STAMMSAETZE if eof STAMMDATEI | eof BEWEGUNGSDATEI then leave tf B < S then Funktion BEWEGUNGEN_OHNE_STAMMSAETZE end do forever end Dateiverarbeitung
5. Strukturierter Softwareentwurf
52
end
begin Dateischließen tfeof STAMMDATEI do while ( - . eof BEWEGUNGSDATEI) Funktion BEWEGUNGEN_OHNE_STAMMSAETZE end do while jf eof BEWEGUNGSDATEI do while ( - . eof STAMMDATEI) Funktion NICHTBEWEGTE_STAMMSAETZE end do while end Dateischließen Abstraktionsstufe-2
In gleicher Weise sind nun auf der nächsten Abstraktionsstufe die drei neugewonnenen Teilaufgaben, nämlich Verarbeiten der bewegten Stammsätze, der nichtbewegten Stammsätze und der Bewegungen ohne Stammsätze, zu verfeinern. begin Abstraktionsstufe-3 begin Funktion BEWEGTE_STAMMSAETZE Gruppenanfang Verarbeiten Gruppe Gruppenwechselarbeiten read STAMMSATZ end Funktion BEWEGTE_STAMMSAETZE begin Funktion NICHTBEWEGTE_STAMMSAETZE Gruppenanfang Drucken Stammdaten read STAMMSATZ end Funktion NICHTBEWEGTE_STAMMSAETZE begin Funktion BEWEGUNGEN_OHNE_STAMMSAETZE Gruppenanfang Anlegen neuen STAMMSATZ Verarbeiten Gruppe Gruppenwechselarbeiten end Funktion BEWEGUNGEN_OHNE_STAMMSAETZE end Abstraktionsstufe-3 Schließlich sind auf einer letzten Abstraktionsstufe wiederum die neuen Teilaufgaben der 3. Abstraktionsstufe zu verfeinern: begin Abstraktionsstufe-4 begin Gruppenanfang Druckaufbereitung KONTONUMMER Druckaufbereitung ALTER_SALDO end Gruppenanfang
5.1 Entwurfsstrategien
53
begin Verarbeiten Gruppe do while (KONTONUMMER_BEWEGUNG = KONTONUMMER_ST AMMSATZ & - i eof BEWEGUNGSDATEI) updaten STAMMSATZ Druckaufbereitung Bewegungsdaten write Zeile read BEWEGUNGSSATZ end do while end Verarbeiten Gruppe begin Gruppenwechselarbeiten Drucken Summenzeile write STAMMSATZ end Gruppenwechselarbeiten end Abstraktionsstufe-4 Es fällt auf, daß bei diesem Programmentwurf Daten nur soweit berücksichtigt wurden, wie es unbedingt notwendig war. Diese Strategie entspricht dem Geheimnisprinzip. Praktiker fragen oft, ob es einen Erfahrungswert gibt für die Anzahl der Abstraktionsstufen, aus denen ein Programmentwurf bestehen soll. Hierauf gibt es keine generelle Antwort; denn die Anzahl der Abstraktionsstufen hängt von der Komplexität der Aufgabenstellung ab. Eine solche Aufgabenmodularisierung 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-
54
5. Strukturierter Softwareentwurf
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 Betriebsaufgabe. 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 Betriebsaufgabe 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 Aufgabenerfüllung notwendig ist, das Objekt, auf das sich eine Teilaufgabe bezieht, die Sach- und Arbeitsmittel, die in die Aufgabenerfü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 1
Genauer gesagt handelt es sich um ablauforganisatorische Aufgaben.
55
5.1 Entwurfsstrategien
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. Auf Grund der herausgearbeiteten Merkmale ist der prozeßorientierte Ansatz für eine Programmentwicklung, die bottom-up vorgeht, besser geeignet als für die Top-down-Strategie. Bottom-up-Verfahren sind nicht als generell überholt zu betrachten, sondern haben nach wie vor für die Variation von bestehenden Programmsystemen („Variantenkonstruktion") eine Bedeutung. Neben den aufgabenorientierten und prozeßorientierten Entwurfsmethoden hat sich in der Programmierpraxis in letzter Zeit, vor allem initiiert durch die schnelle Verbreitung der Ideen von Jackson, noch ein dritter Ansatz durchsetzen können, der von den Datenstrukturen ausgeht, die einer Programmieraufgabe zugrunde liegen. Ingenieure entwerfen technische Gegenstände schon immer iterativ. So ist auch der strikte top-down Entwurf von Programmen eine Idealvorstellung, die ein nützliches Denkmodell zur Verfügung stellt, aber die Intuition und die Erfahrung des Software-Konstrukteurs nicht ersetzen kann. Er wird sowohl top-down als auch bottom-up arbeiten. Deshalb kann es auch sinnvoll sein, die Top-downStrategie mit dem Bottom-up-Ansatz zu einem Up-down-Prinzip zu kombinieren, bei dem der Software-Entwurf im Kern eines Systems beginnt und sowohl nach oben als auch nach unten vordringt. Damit lassen sich nun die SoftwareEntwurfsstrategien in folgendem Schema zusammenfassend darstellen: Name:
top-down
bottom-up
up-down
Entwurfsprinzip:
Vom Ganzen zum Einzelteil: stufenweise Verringerung der Komplexität einer Aufgabe
Vom Einzelteil zum Ganzen: Kombination von Elementarteilen zu komplexeren Systemen
Hardest first: Der Entwurf beginnt im Kern eines Systems
Anwendung:
Neukonstruktion eines Softwaresystems
Konstruktion von Varianten eines schon vorhandenen Softwaresystems
Experimenteller Entwurf von Softwaresystemen
Methode:
Abstraktionsstufenkonzept
Klassen:
— aufgabenorientierte Verfahren
prozeßorientierte Verfahren
— aufgabenorientierte Verfahren
5. Strukturierter Softwareentwurf
56
— datenorientierte Verfahren — prozeßorientierte Verfahren
— datenorientierte Verfahren
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 Aufgäbet 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 Funktionsbezogener Entwurf (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.
5.2 Methoden des Top-down Entwurfs
57
Das Softwaresystem besteht aus 83 000 PL/1-Anweisungen und wurde in einer Zeit von 22 Monaten mit einem Arbeitsaufwand von 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 plus 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 Baumdiagramm, auch Strukturübersicht genannt, fest. Anschließend definiert er in einer zweiten Entwurfsphase, dem Feinentwurf, für jede Teilaufgabe die sequentielle Folge der Arbeitsprozesse, die notwendig sind, um diese Teilaufgabe zu lösen. Dabei werden auch für jeden Prozeß die Eingabeund 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-
58
5. Strukturierter Softwareentwurf INPUT
PROCESS
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 j in Abbildung 6 das Eingabedatenelement i ! in die beiden Ausgabeelemente 0 ! 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 einen Sonderfall, bei dem aufgrund einer Verzweigung im Prozeß P 3 der Steuer-
5.2 Methoden des Top-down Entwurfs
59
fluß entweder an dieser Stelle oder nach dem Prozeß P 4 die Process-Section verläßt. Ein Nachteil der HIPO-Technik ist darin zu sehen, daß sie für das Konstruktionselement „Schleife" kein eigenes Symbol zur Verfügung stellt. Hier kann sich der Programmdesigner nur in der Weise behelfen, daß er verbal diesen wichtigen Baustein der strukturierten Programmierung beschreibt (z.B. Prozeß P x wird so lange durchlaufen, bis EOF auftritt). Im Feinentwurf sind noch einmal zwei Phasen zu unterscheiden. In einem Überblicksdiagramm werden die Eingabe- und Ausgabedaten der Hauptfunktionen eines Programms aufgelistet und die Arbeitsprozesse, die damit verbunden sind, skizziert. In dieser Weise geben sie einen Überblick über die zugeordneten Funktionen der nächstniederen Hierarchiestufe. Anschließend werden in Detaildiagrammen die Teilfunktionen weiter in Unterfunktionen dekomponiert. Detaildiagramme werden für alle die Funktionen erstellt, die nicht mehr weiter zerlegbar sind. Sie stellen damit die niedrigste hierarchische Stufe im HIPOEntwurf dar. Dabei erhebt sich die Frage, wie weit in der HIPO-Methode eine Aufgabe schrittweise zerlegt werden soll. Es wird gefordert, daß der Detaillierungsgrad so weit zu treiben ist, daß Detaildiagramme direkt in einen Code umgesetzt werden können [23, S. 28]. Die HIPO-Technik nimmt eine Zwitterstellung zwischen der Klasse der aufgabenorientierten und der datenorientierten Entwurfsmethoden ein; denn vom Ansatz 0 = P(I) her steht der Funktionsbegriff im Mittelpunkt. Insofern ist es gerechtfertigt, diese Methode auch „funktionsbezogenen Entwurf" zu nennen. Da die Funktionen aber von den Daten ausgehen, könnte man diese Methode auch in die Klasse der datenorientierten Verfahren einordnen. HIPO-Diagramme stellen nicht nur die Ergebnisse des Feinentwurfs dar, sondern sie dienen auch zur Programmdokumentation. Allerdings ist der Arbeits- und Platzaufwand für die Erstellung dieser Diagramme sehr hoch. Der Vorteil der HIPO-Methode liegt deshalb mehr auf der Seite der Dokumentationshilfe als der Entwurfsmethodik [45, S. 74]. Diese skizzenhafte Beschreibung soll mit einem Beispiel vertieft werden, wofür wiederum auf die Fallstudie des Kapitels 3.1 zurückgegriffen wird. In der ersten HIPO-Entwurfsphase, dem Grobentwurf, hat der Programmdesigner eine Strukturübersicht der Teilaufgaben der sequentiellen Dateiverarbeitung anzulegen. Hierbei geht man zweckmäßigerweise in gleicher Weise wie beim Abstraktionsstufenkonzept vor, so daß sich folgendes Organigramm ergibt:
60
5. Strukturierter Softwareentwurf 0
Im nächsten Entwurfsschritt sind nun für jede hierarchische Stufe des Grobentwurfs die groben HIPO-Diagramme anzulegen. Sie zeigen nicht nur für jede Teilfunktion die Eingabe-und Ausgabedaten, sondern stellen auch bereits in Grundzügen die Programmlogik dar, indem sie angeben, unter welchen Bedingungen welche Teilfunktionen auszuführen sind (1. Teil des Feinentwurfs, Aufstellung von Überblicksdiagrammen).
61
5.2 Methoden des Top-down Entwurfs 2. Feinentwurf:
grobe HIPO-Diagramme
Dateieröffnung (Diagramm 1) Eingabe
Verarbeitung
Ausgabe
1. Eröffnung Stamm- und Bewegungsdatei
Stammsatz
2. Lesen 1. Stammsatz Bewegungsdatei
3. Lesen 1. Bewegungssatz
Bewegungssatz
Dateiverarbeitung (Diagramm 2)
Bewegungssatz
1. B = S bewegte Stammsätze
Stammsatz
2. B > S nichtbewegte Stammsätze Stammsatz
3. B < S Bewegungen ohne Stammsätze
Dateischließen (Diagramm 3)
1. Bei EOF Stammdatei Bewegungen ohne Stammsätze
2. Bei EOF Bewegungsdatei nichtbewegte Stammsätze
Zeile
62
5. Strukturierter Softwareentwurf
2. Feinentwurf:
Eingabe
1
grobes HIPO-Diagramm „bewegte Stammsätze" (Diagramm 2.1) 1
Verarbeitung
Ausgabe
Aus drucktechnischen Gründen wird in den folgenden Darstellungen der o f f e n e Pfeil durch einen einfachen Pfeil ersetzt.
5.2 Methoden des Top-down Entwurfs 2. Feinentwurf: grobes HIPO-Diagramm „nichtbewegte Stammsätze" (Diagramm 2.2)
Eingabe
Verarbeitung
Ausgabe
5. Strukturierter Softwareentwurf
64
2. Feinentwurf: grobes HIPO-Diagramm „Bewegungen ohne Stammsätze' (Diagramm 2.3)
Eingabe
Verarbeitung
Ausgabe
Stammdatei
5.2 Methoden des Top-down Entwurfs
65
In der zweiten Phase des Feinentwurfs muß der Programmdesigner die Teilfunktionen der tiefsten hierarchischen Stufe der Überblicksdiagramme weiter in Unterfunktionen auflösen. In unserer Fallstudie sind es die Funktionen „Gruppenanfang", „Verarbeiten Gruppe" und „Gruppenwechselarbeiten" (Aufstellung von Detaildiagrammen). 3. Feinentwurf: detailliertes HIPO-Diagramm „Gruppenanfang"
Eingabe
Verarbeitung
Ausgabe
66
5. Strukturierter Softwareentwurf
3. Feinentwurf: detailliertes HIPO-Diagramm „Verarbeitung Gruppe"
Eingabe
Verarbeitung
Ausgabe
Dieses Beispiel zeigt anschaulich die Vorteile und Nachteile der HIPO-Methode. Ihre Stärke liegt in der einfachen, bildhaften Darstellungsweise eines Softwareentwurfs. An Hand der Überblicksdiagramme kann man sich schnell in die Funktionsweise eines Programmsystems einarbeiten, ohne zu stark auf Details eingehen zu müssen. Die Struktur der HIPO-Diagramme zwingt den Softwaredesigner, alle Eingabe- und Ausgabedaten zu spezifizieren. Allerdings sind sie umständlich zu zeichnen und außerdem unübersichtlich wegen der sich über-
5.2 Methoden des Top-down Entwurfs
67
schneidenden Pfeile. Der Steuerfluß ist nur schwer zu erkennen, was damit zusammenhängt, daß es keine eigenen Symbole für die Iteration und die Selektion gibt. Um Zeichenarbeit zu sparen, werden auf dem Markt heute Softwarewerkzeuge angeboten, wie das Programmpaket HIPO-Draw von IBM (s. Kapitel 5.3).
5.2.2 Datenstrukturorientierter Entwurf 5.2.2.1 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 von 18 Programmierbeispielen 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 vier Entwurfsphasen: (1)
der Analyse und dem Entwurf aller Datenstrukturen einer zu programmierenden Aufgabe (Eingabedaten und Ausgabedaten)
(2)
Untersuchung der Datenstrukturen auf Korrespondenzen (Strukturkonflikte)
(3)
der 1:1-Abbildung der in der Phase 1 entworfenen Datenstrukturen auf eine Programmstruktur
(4)
Darstellung der Programmlogik in einem Pseudocode (schematic logic).
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 Entwurf von Informationssystemen gelernt, daß eine strenge Unterscheidung von funktioneller und physische 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 von sequentiell organisierten Dateien ausgeht
5. Strukturierter Softwareentwurf
68
(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. Für die 1. Entwurfsphase greift Jackson auf die drei Programmbausteine der strukturierten Programmierung zurück, 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:
In dieser Baumstruktur geht der Steuerfluß von links nach rechts. Programmschleifen, die in der strukturierten Programmierung natürlich häufig vorkommen, nennt Jackson Iterationen und markiert sie durch einen hochgestellten Stern:
X
*
Y Jackson läßt in einer Iteration auch die leere Menge des zu iterierenden Datenoder Programmbausteins zu. Die Selektion, die eine Auswahl aus zwei oder mehr alternativen Möglichkeiten darstellt (IF THEN ELSE-Anweisung oder CASEVerzweigung), symbolisiert er duch einen hochgestellten kleinen Kreis:
5.2 Methoden des Top-down Entwurfs
69
Abbildung 7 bringt ein Beispiel für den Zusammenhang zwischen Datenstruktur und Programmstruktur bei sequentiell organisierten Dateien. Die Eingabedaten dieser Aufgabe bestehen aus einer Datei, die nach Gruppen und Untergruppen sortiert vorliegt. Es ist eine Liste mit Untergruppen- und Gruppensummen sowie einer Endsumme zu drucken. In dieser Aufgabe besteht eine natürliche Korrespondenz zwischen den beiden Datenstrukturen Eingabedatei und Liste sowie der Programmstruktur; denn die Anzahl und die Reihenfolge der Iterationen ist für beide Datenstrukturen gleich. Außerdem werden sie auch gleichzeitig vom Programm bearbeitet, so daß jeder Datenkomponente eine Programmkomponente entspricht. Beispielsweise leitet die Programmkomponente „Satz" aus einem Eingabesatz eine Zeile der Liste ab. 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:
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
70
5. Strukturierter Softwareentwurf
auswirken kann. Diese Entwurfsstrategie verlängert offensichtlich ein Programm. Sie kann schon jetzt als Nachteil der Jackson-Methode vermerkt werden.
Abb. 7
Beispiel eines Programmentwurfs nach Jackson.
Die Forderung nach einer 1:1-Abbildung der Datenstruktur auf die Programmstruktur ist nicht immer realisierbar. Als Beispiel führt 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
5.2 Methoden des Top-down Entwurfs
71
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, schematisch dargestellt:
Es gibt Beispiele, bei denen 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". Schematisch läßt sie sich wie folgt darstellen:
Während das Eingabeprogramm Daten in die Zwischendatei schreibt, verarbeitet das Ausgabeprogramm den zuletzt geschriebenen Satz. Falls die verwendete Programmiersprache nicht über die Multitasking-Funktion verfügt, kann man sie in der seriellen Programmverarbeitung dadurch simulieren, daß das invertierte Programm zum Unterprogramm des ersten Programms wird oder auch vice versa:
72 /Eingabe- / / struktur/
5. Strukturierter Softwareentwurf Eingabe-
Ausgabe-
programm
progranmi
/Ausgabe- / I struktur/
An diesem Beispiel wird noch einmal deutlich, welche Schwierigkeiten entstehen, wenn sich der Programmdesigner in seiner Arbeit unbedingt an Datenstrukturen klammern muß. Es wurde auch schon gezeigt, daß ein aufgabenorientierter Programmentwurf, wenn er auf dem Geheimnisprinzip basiert, von vornherein Strukturkonflikte vermeidet [44a], so daß dort keine Notwendigkeit besteht, den umständlichen Umweg über die Programminversion zu gehen. 5.2.2.2 Die Warnier-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: „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). Zur Darstellung des Programmentwurfs benutzt Warnier zusammen mit einer verbalen Beschreibung der Programmkomponenten, Programmablaufpläne und einen Pseudocode, um den endgültigen Programmentwurf zu dokumentieren. Das Beispiel der sequentiellen Dateiverarbeitung aus dem Kapitel 5.2.2.1 (Abb. 7) wäre in der Warnier-Methode wie folgt darzustellen:
5.2 Methoden des Top-down Entwurfs
Eingabedatei
Dateiniveau
Gruppenniveau
Untergruppen-
Gruppe (a mal)
Untergruppe (b mal)
Satz (c mal)
Programmniveau Dateianfang (1 mal) Programm (Datei-
73
• Gruppen^v Verarbeitung
Verarbeitung)• (a mal)
Dateiende (1 mal)
Gruppenniveau I Gruppenanfang | (1 mal)
Untergruppenniveau | Untergruppenverarbeitung | (1 mal)
I Untergruppen- ^PSatzverarbeitung S^ Verarbeitung (c mal) (b mal) Gruppenende (1 mal)
Untergruppenende (1 mal)
Aus der hierarchischen Programmstruktur wird die Programmlogik abgeleitet und mit Hilfe der konventionellen Symbole in Form von Programmablaufplänen dargestellt:
74
5. Strukturierter Softwareentwurf
10
20
30
40
50
60
70
a mal
5.2 Methoden des Top-down Entwurfs
75
Die Symbole des Programmablaufplans werden numeriert und in einer Tabelle zusammengefaßt. Anschließend gibt der Programmdesigner zu jeder Nummer die wichtigsten Funktionen an:
Nummer im Programmablaufplan
Funktion (Pseudocode)
10
Löschen Endsumme, Eröffnen der Eingabedatei Lesen 1. Datensatz
20
Übertragen Gruppennummer in ein Vergleichsfeld Löschen Endsumme Gruppe Druckaufbereiten Gruppenanfang
30
Übertragen Untergruppennummer in ein Vergleichsfeld Löschen Endsumme Untergruppe Druckaufbereiten Untergruppenanfang
40
Satzverarbeitung, z.B. Addieren Drucken 1 Zeile Lesen nächster Datensatz Wenn gleiche Untergruppennummer, wiederholen 40
50
Addieren Untergruppensumme zu Gruppensumme Druckaufbereiten Untergruppensumme Drucken 1 Zeile Wenn gleiche Gruppennummer, wiederholen ab 30
60
Addieren Gruppensumme zu Endsumme Druckaufbereitung Gruppensumme Drucken 1 Zeile Solange nicht EOF, wiederholen ab 20
70
Druckaufbereiten Endsumme Drucken 1 Zeile, Schließen der Eingabedatei
Wenn man Programme in der Warnier-Methode entwirft, gibt es im Gegensatz zur Jackson-Methode keine Notwendigkeit, Korrespondenzen zwischen Datenstrukturen herzustellen. Insofern ist diese Methode leichter anzuwenden. Darüber hinaus bringt Warnier einen neuen Gesichtspunkt in die Fachdiskussion ein, 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.
5. Strukturierter Softwareentwurf
76
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 und weiterentwickelt. In den 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:
I
Wir bezeichnen in der Taks T! die READ-Anweisung als Ereignis „Xi" und den REWRITE-Zugriff als „ x 2 " . In analoger Form werden in T 2 die Ereignisvariablen „ x 3 " und „ x 4 " vergeben. Wir stellen nun eine Wertetafel der 16 Variationen
5.2 Methoden des Top-down Entwurfs
77
ü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ß. Xj
x2
X3
x4
Verriegelung V
0
0
0
0
0
0
0
0
1
0
0
0
1
0
0
0
0
1
1
0
1
0
0
0
0
1
0
1
1
0
1
1
0
1
0
1
1
1
1
0
0
0
1
0
1
1
1
0
0
1
0 0 0
1
1
1
1
0
1
1
1
0 0
1
1
1
0
1
1
1
1
Bemerkungen
kann nicht auftreten
kann nicht auftreten 0
1
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 = ( - , x , AX 2 A -.X 3 AX 4 ) v ( - , x , AX 2 AX 3 A -.X 4 ) v (x, A - , x 2 A - i x 3 A X 4 ) Vor der Durchführung jedes Dateizugriffs prüft das Programm, ob V den Wert Eins hat. Ist das der Fall, dann muß die Programmausführung 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.
78
5. Strukturierter Softwareentwurf
5.2.3 Aufgabenorientierter Entwurf 5.2.3.1 Die Constantine-Methode 5.2.3.1.1
Grundlagen
Der Ansatz von Constantine unterscheidet sich von den bisher beschriebenen 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 ist deshalb der typische Repräsentant eines aufgabenorientierten Entwurfsverfahrens, bei dem der Lösungsalgorithmus im Vordergrund steht. Im strukturierten Entwurf leitet Constantine top-down aus der Aufgabenstellung Programmodule ab, die möglichst einfach und unabhängig sein sollen. In der Fachliteratur wird deshalb seine Methode auch „Composite Design" genannt. Constantine geht 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, wo-
5.2 Methoden des Top-down Entwurfs
79
durch 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 dieDatenfluß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 aufgerufenen 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 (EXTERNAL-Definition in PL/1 oder COMMON-Bereich in FORTRAN). Sie fuhren 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.]. Damit ergeben sich fünf Kopplungsarten, wobei die Kopplungsstärke von Art zu Art zunimmt: Datenflußkopplung: 1. Parameterübergabe 2. Parameterübergabe 3. Parameterübergabe oder COMMON 4. Parameterübergabe Steuerflußkopplung.
durch Call by value durch Call by reference durch Variable mit Attribut EXTERNAL durch globale Variable (interne Prozeduren)
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].
5. Strukturierter Softwareentwurf
80
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. Beispiel:
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 Etngabeprozedur, 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 zugreifen. Als Beispiel nennt er einen informationellen Arbeitsprozeß, dessen Anweisungen dieselben Eingabedaten be-
5.2 Methoden des Top-down Entwurfs
81
nutzen [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 Berechnung 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 = ', X); PUT LIST ('ENDE'); END DRUCKEN; 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
82
5. Strukturierter Softwareentwurf
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. (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 2
Bekanntlich ist das Simultanisierungsniveau in PL/1 die Prozedurebene.
5.2 Methoden des Top-down Entwurfs
83
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 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 J E
Abb. 8
AUFGABE
HMIULE/AUFGARE
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
5. Strukturierter Softwareentwurf
84
Untergebene ein Vorgesetzter führen kann. Diesen Begriff übernimmt Constantine und empfiehlt, daß ein Modul fünf bis neun Untermodule haben soll.
5.2.3.1.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 the interrelationship between those components in the best possible w a y " [53, S. 7], Diese Begriffsbestimmung sagt nichts Spezifisches zur Constantine-Methode aus. Jeder gute Programmierer hat auch bisher versucht, möglichst kurze und schnelllaufende Programme zu schreiben. Was ihm aber bei seiner Arbeit fehlte, war eine Lehre, wie er ein solches Ziel sicher erreichen kann. Constantine führt allerdings zusätzlich zum „strukturierten Entwurf" auch noch den Terminus „Topdown Design" ein. Seine Begriffsbestimmung entspricht unserer Auffassung vom strukturierten Entwurf, indem er definiert: „Top-down design requires the designer to design a program in terms of its major functions" [53, S. 333].
5.2 Methoden des Top-down Entwurfs Symbol
85 Definition
5. Strukturierter Softwareentwurf
86 Symbol
Definition Datenfluß: M o d u l A ruft M o d u l B auf und übergibt die Parameter X und Y an B. M o d u l B gibt den Parameter Z zurück.
1
Eingabe
Ausgabe
X.Y
Z
Vereinfachte darstellung
Datenfluß-
Steuerfluß: Der Steuerfluß geht von A nach B.
V e r e i n f a c h t e Steuerflußdarstellung: Bei der EOFBedingung geht der Steuerf l u ß von A nach B .
A b b . 9 Darstellungsmethode hierarchisch strukturierter Programmsysteme nach Constantine [ 4 7 , S. 1 2 6 , 5 3 ] .
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 Unterfunk-
5.2 Methoden des Top-down Entwurfs
87
tionen 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, kann der Datenfluß durch einen Kreis und der Steuerfluß durch einen Punkt markiert werden. Die Pfeile, die eine Flußrichtung repräsentieren, können mit den zu fließenden Daten bzw. den Steuerungsinformationen markiert werden. Falls der Platz dafür nicht ausreicht, kann man diese Angaben auch in einer Liste zusammenfassen. 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 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 Fei-
88
5. Strukturierter Softwareentwurf
der angesprochen werden müssen. Auch kann ein Bewegungssatz aus mehreren Lochkarten bestehen, die dann zusammen einen funktionellen Bewegungssatz bilden. In Abbildung 10 wurde 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 Ausgabepro-
Abb. 10
Strukturdiagramm für das Updaten einer Stammdatei [53, S. 315],
5.2 Methoden des Top-down Entwurfs
89
zesse 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 JacksonMethode 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.3.2 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 /Inalysis Design 7echnique ist. Diese Strukturanalyse einer zu lösenden Aufgabe trägt die Attribute top down, hierarchisch und modular [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 ConstantineMethode, 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.
90
5. Strukturierter Softwareentwurf
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 unserer 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
F U N K T I O N S E I N H E I T : INITIIERENDER DÄTENFLUSS
. (z.
B. BEWEGUNGSDATEN)
EINGABE
AUSGABE ( z . B. NEUE STAMMDATEI)
( z . B. STAMMDATEI) |
Abb. 11
SADT-DarsteUungsmethode
MECHANISMUS
5.2 Methoden des Top-down Entwurfs
91
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ß, allerdings 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
Abb. 12 Anwendung der SADT-Methode auf den Gemüseanbau [46, S. 4 - 2 ] ,
92
5. Strukturierter Softwareentwurf
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.2.4 Das Geheimnisprinzip von Parnas Die Forderung von Constantine, die Modulkopplung zu minimieren, kann man auch in der Weise formulieren, daß jeder Modul für seine Arbeit möglichst wenig Informationen über andere Module benötigen soll. Parnas leitet aus diesem Ansatz die Regel ab: Die interne Konstruktion eines Moduls einschließlich der dort verwendeten Datenstrukturen soll der Umgebung verborgen bleiben [36, S. 1053 ff.]. Dieses „Geheimnisprinzip" (information hiding) bedeutet, daß die Umgebung eines Moduls nichts über seine Interna weiß. Die internen Eigenschaften eines Moduls sind durch seine Schnittstellen definiert. Dieser Ansatz ist im Grunde genommen die Antithese zur Jackson-Methode, die davon ausgeht, daß am Beginn eines Programmentwurfs alle Daten und ihre Strukturen, die ein Programm verarbeitet, offen zu legen sind, um sie auf Korrespondenzen untersuchen zu können. Horninghat auf der Grundlage des Geheimnisprinzips einen neuen Programmbaustein eingeführt, den er Datenkapsel nennt [21b]. Nach Rechenberg besteht eine Datenkapsel aus der Vereinbarung lokaler Daten und einer Menge von Prozeduren, die diese Daten verwalten. Die Datenkapsel bildet einen eigenen Programmodul mit internen Prozeduren, die auf diese Daten zugreifen. Andere Programmodule, die sich auf die Daten der Datenkapsel beziehen, können das nicht direkt, sondern nur über eine Zugriffsfunktion tun [39a]. In der Anwendungsprogrammierung wird das Konstruktionselement „Datenkapsel" vor allem für die physische Datenhaltung verwendet. Die Struktur der Eingabe- und Ausgabedaten ist nur diesem Datenverwaltungsmodul bekannt. Er übergibt an „operationale" Prozeduren funktionelle Datensätze, deren Struktur von den auf sie angewendeten Operationen bestimmt wird (abstrakte Daten). In letzter Konsequenz führt die Verwendung einer Datenkapsel für den E/A-Verkehr zu einem datenbankorientierten Programmentwurf, wie ihn White und Anderson vorgeschlagen haben [50a]. In einem solchen Softwaresystem wird der gesamte intermodulare Datenverkehr von einer Datenbank wie im Telefonverkehr „vermittelt". Sie verwaltet auch alle externen Datenstrukturen. Die einzelnen Programmodule sind rein aufgabenorientiert ausgelegt. Alle Fragen, die externe Datenträger betreffen, deligiert ein solches System an die Datenbank, so daß sie die Funktion der „Datenverwaltung" im weitesten Sinn ausübt.
5.2 Methoden des Top-down Entwurfs
93
Verwendet ein Programm-Designer die Datenkapsel, dann resultiert aus diesem Ansatz die in Abbildung 13 dargestellte Top-down-Struktur. In der Praxis kommt kein Programmsystem ohne die Vereinbarung globaler Variablen aus. Im Sinne des Geheimnisprinzips ist aber ihre Anzahl zu minimieren. Das operationale System kommuniziert mit dem Datenverwaltungssystem über funktionelle Datensätze. Da im strukturierten Entwurf von Anwendungsprogrammen interne Prozeduren dominieren [17a], werden in der Praxis diese funktionellen Datensätze häufig auch den Charakter globaler Variablen haben.
Abb. 13 wurde.
Generelle Struktur eines Programms, das nach dem Geheimnisprinzip konstruiert
Aus Abbildung 13 folgt, daß ein aufgabenorientiertes Programmsystem, das auf der Grundlage des Geheimnisprinzips entworfen wurde, aus drei Hauptkomponenten (Datenkapseln) besteht: 1. 2. 3.
der Programmsteuerung dem operationalen System, das alle Arbeitsprozesse enthält dem Datenverwaltungssystem, das den gesamten peripheren Datenverkehr abwickelt.
5. Strukturierter Softwareentwurf
94
5.3 Methodenvergleich Es wurden im Kapitel 5.2 die fünf wichtigsten Software-Entwurfsmethoden, die gegenwärtig dem Anwendungsprogrammierer zur Verfügung stehen, kurz beschrieben. Welche Methode soll er nun für eine spezifische Aufgabe aus diesem Methoden-Spektrum auswählen? In der Fachliteratur gibt es bisher nur wenige Untersuchungen, die sich mit dieser Frage beschäftigen [44b, 21a], Es sollen deshalb hier die wichtigsten Kriterien für einen Methodenvergleich zusammengestellt werden. 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 (Jackson, Warnier) und die aufgabenorientierten Methoden (Constantine, SADT). In dieser Klassifizierung nimmt die HIPO-Technik eine Zwitterstellung ein, indem sie von den Daten ausgeht und aus ihnen Aufgaben ableitet. Tab. 2 Charakteristische Merkmale der Softwaxeentwuifsmethoden.
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.
5.3 Methodenvergleich
95
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 Bundesrepublik Deutschland versucht das Normblatt DIN 66220 eine solche Vereinheitlichung, unabhängig von schon bestehenden Firmennormen, in die Wege zu leiten [17]. Österle hat darauf hingewiesen, daß vom Programmablauf her gesehen eine Ähnlichkeit zwischen der normierten Programmierung und datenorientierten Entwurfsverfahren, wie der Jackson-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-Verzweigungen. 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], Die Vorteile und Nachteile der einzelnen Softwaresignmethoden 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
96
5. Strukturierter Softwareentwurf
Tab. 3 Darstellungsmittel der Softwareentwurfsmethoden.
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
Jackson, der aber die Schnittstellenproblematik nicht beherrscht. Als einziger der hier aufgeführten Autoren von Softwareentwurfsverfahren hat Warnier versäumt, eine eigene bildhafte 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. 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 Pro3 4 5
Es wurde als Darstellungsmittel die Symbolik der Programmablaufpläne unterstellt. J: ja, N: nein Dieses Symbol wurde erst in der Neuausgabe vom September 1977 definiert und eingeführt [16],
5.3 Methodenvergleich
97
grammablaufplä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 umgangssprachlichen 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 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 < bedingung > [THEN < arbeitsprozeß >] [ELSE < arbeitsprozeß >] ENDIF Das Schlüsselwort THEN oder das Schlüsselwort ELSE dürfen unterdrückt werden, beide zusammen nicht. Aus Übersichtlichkeitsgründen sollen auch alle Syntaxkomponenten, die von PL/1 übernommen wurden, untereinander notiert werden. (2)
DO WHILE-Struktur: DO WHILE arbeitsprozeß > . . . ENDDO
5. Strukturierter Softwareentwurf
98 (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. Von einer Softwareentwurfsmethode wünscht sich der Programmdesigner, daß sie möglichst alle Phasen eines Softwareprojektes abdeckt. Leider erfüllt diese Forderung keiner der bisher bekanntgewordenen Ansätze (s. Tabelle 4). HIPO, Constantine und SADT sind auf die ersten Phasen eines Programmentwurfs ausgerichtet. Hingegen eignet sich der Ansatz von Jackson vor allem für den Feinentwurf und die Codierung. Für die Praxis bedeutet diese Feststellung, daß der Softwaredesigner mehrere Methoden beherrschen muß, aus denen er für seine Arbeit die optimale auswählt. Darin unterscheidet sich auch der wissenschaftlich fundierte Softwareentwurf von einer handwerklichen Tätigkeit. Ohne den Berufsstand des Handwerkers abwerten zu wollen, ist doch festzustellen, daß man dort in der Regel nur eine Methode beherrscht und anwendet. Ein Wissenschaftler hingegen sollte in der Lage sein, zur Lösung bestimmter Fragenkomplexe aus einem Methodenreservoire schöpfen zu können.
5.3 Methodenvergleich Tab. 4
99
Phasenbezogenheit der Softwareentwurfsmethoden HIPO
Jackson
Warnier
Constan tine
SADT
Aufgabenanalyse Grobentwurf Feinentwurf Codierung Test
I
Jede konstruktive Tätigkeit besteht aus zwei Komponenten, nämlich einem schöpferischen Teil und relativ viel Routinetätigkeit. Es liegt nahe zu versuchen, den Computer für Routinetätigkeiten einzusetzen. Dieses Ziel verfolgen im Maschinenbau seit längerem CAD-Systeme. Auf dem Software-EngineeringGebiet stecken analoge Ansätze noch in den Kinderschuhen. Tabelle 5 gibt einen Überblick über Software-Werkzeuge, die gegenwärtig angeboten werden. Tab. 5 Methode
Automatisierte Werkzeuge für den Softwareentwurf Softwarewerkzeug
HIPO
HIPO-draw, PET/X 1150, PRIME-Computer
Jackson
JSP-COBOL, SLOGAN (Schematic Logic Analyser) DIPROTOR
6. LITOS (Linzer Technique of Softwaredesign), eine neue Methode des Softwareentwurfs und der Softwaresimulation1 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 2 . 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. 1
2
Diese Bezeichnung soll auch Assoziationen zum griechischen Begriff „Lyttos" wachrufen, den Plato in seinen „Gesetzen" erwähnt und der dort als Prototyp eines soziologischen Systems beschrieben wird, das sich durch eine einfache und klare Struktur auszeichnet. s. Begriffsbestimmung „Programm", nach DIN 4 4 3 0 0 „eine zur Lösung einer Aufgabe vollständige Anweisung zusammen mit allen erforderlichen Vereinbarungen" [13, S. 5 ]
6.2 Die vier Entwurfsphasen in LITOS
101
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 /lu/ftaworganisation als den Gebilde- und Beziehungszusammenhang der Elemente eines realen Systems. Die /lA/aw/organisation 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 top 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 14 zeigt die hierarchisch strukturierte Aufbauorganisation für diese Aufgabe, die sich als Gesamtheit der Teilfunktionen der Dateiverarbeitung darbietet.
102
6. LITOS, eine neue Methode des Softwareentwurfs .
6.2 Die vier Entwurfsphasen in LITOS
103
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 und den Anweisungen, zunächst in Gestalt der Hauptsteuerungsanweisungen. Diese Form der Aufgabenanalyse bedeutet aber nicht, daß im operationalen System keine Vereinbarungen zugelassen sind. Der Unterschied ist darin zu sehen, daß im Vereinbarungsteil Arbeitsobjekte globalen Charakters definiert werden, deren Anzahl im Sinne des Geheimnisprinzips natürlich zu minimieren ist, so daß im Idealfall dieser Programmteil leer ist. Im operationalen System hingegen haben Daten nur lokale Bedeutung. Die meisten Vereinbarungen werden deshalb dort zu finden sein. In der Stapelverarbeitung, die unserem Beispiel zugrunde liegt, kommt dem Steuerungsteil, der dem operationalen System überlagert ist, keine große Bedeutung zu. Anders ist aber die Situation im Mensch-Maschine Dialog, für den eine größere Menge von Kommandos notwendig ist, wie beispielsweise das Übersetzen von Kommandos, das Aufsuchen von Daten und Programmen und das Aufbereiten von Bildschirmmasken. Auf der hierarchischen Stufe „2" unterscheiden wir zwischen der Datenverwaltung und dem operationalen Systemteil, den man auch als algorithmischen Teil bezeichnen könnte. Auf der Abstraktionsstufe „3" wird die Datenverwaltung in zwei Datenkapseln, nämlich die Dateieröffnung, zusammen mit den Eingabeprozessen, und das Drucken zerlegt. In diesen beiden Datenkapseln werden sämtliche physischen Eingabe- und Druckprozesse zusammengefaßt, so daß in den nachfolgenden Abbildungen die Bezeichnungen „Lesen" und „Drucken" (s. z.B. Abbildung 18) nur noch funktionelle Bedeutung haben. In dieser Weise erhält man ein wartungsfreündliches Programm, in dem sich Änderungen, die den Eingabe-/Ausgabeverkehr betreffen, nicht auf den operationalen Teil auswirken und vice versa. Das operationale System wird weiter in die drei Hauptfunktionen jeder Dateiverarbeitung untergliedert, 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.
6. LITOS, eine neue Methode des Softwareentwurfs
104 (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).
6.2.2 Der Feinentwurf
Steuerfluß
*
Funktionseinheit bzw. Prozedur, die mehrfach iterativ durchlaufen wird (Schleife)
Simultanverarbeitung:
11 ir~c
Aufspaltung Abb. 15
1
Datenfluß
I II
—¡r
Synchronisation
Sammlung
LITOS Darstellungsmethode für hierarchisch strukturierte Programmsysteme.
Den Fall, daß Stammsätze gelöscht werden, wollen wir hier nicht berücksichtigen.
6.2 Die vier Entwurfsphasen in LITOS
105
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 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. 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
Eingabedaten
Ausgabedaten Eingabedaten F12 = Ausgabedaten F11
Eingabedaten F 12 * Ausgabedaten F11 Abb. 16 Schematisches Beispiel für den Feinentwurf eines hierarchisch strukturierten Funktionsplans.
106
6. LITOS, eine neue Methode des Softwareentwurfs
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], Strecke: (Sequenz)
A b b . 17
E n t w u r f s d o k u m e n t a t i o n in der L I T O S - M e t h o d e .
6.2 Die vier Entwurfsphasen in LITOS
107
Der Feinentwurf liefert folgende Informationen: Welche Funktionseinheiten haben steuernden Charakter? Wo werden Daten erzeugt? Welche Daten benötigt eine Teilaufgabe? Welche Daten verarbeitet eine Funktionseinheit? Abbildung 16 demonstriert diese Aussagen an einem Beispiel. Die Funktionseinheit F 1 wird dort iterativ durchlaufen. Sie ruft nacheinander F 11 und F 12 auf. Nach jedem Schleifendurchlauf geht der Steuerfluß, nachdem F 12 ausgeführt wurde, an F 1 zurück, was nicht explizit dargestellt werden muß, da sich dieser Ablauf von selbst aus dem Schleifencharakter ergibt. Die Ausgabedaten von F 11 HIERARCHISCHE
STAMMSATZ
STUFE
BEWEGUNGSSATZ
GLIEDERUNGSMERKMAL
KONTONUMMER-BEWEGUNG BEWEGTE
STAMMSÄTZE
-
KONTONUMMER-STAMMSATZ
ABLAUFORGANISATOR. AUFGABEN ZEILE,KONTONUMMER_STAMMSATZ,
BEWEGUNGSSATZ STAMMSATZ
SALDO.STAFIMSATZ
JUL N
|L^||GRUPPEN-|L^ 1 - f J w E C H S E L II
LESEN STAMMSATZ
BEWEGUNGSSATZ
UPDATEN STAMMSATZ
INFORMATIONELLE
>
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 WHILE STAMMDATEN:
EOF CALL
NICHTBEWEGTE STAMMSÄTZE
A b b . 18 (Fortsetzung S. 1 0 8 )
108
Abb. 18
6. LITOS, eine neue Methode des Softwareentwurfs
Feinentwurf der Teilfunktion „bewegte Stammsätze" der Fallstudie.
gehen als Ganzes in F 12 ein, so daß es sich bei diesen beiden Prozeduren um sequentiell gebundene Programmodule im Sinne von Constantine handelt. Im unteren Teil der Abbildung 16 wird die Alternative dargestellt, daß die Eingabedaten von F 12 nicht identisch sind mit den Ausgabedaten von F 11. Wie in jeder Entwurfsmethode, die auf der strukturierten Programmierung beruht, so müssen auch in LITOS die Bauelemente, die von der strukturierten Programmierung entwickelt worden sind, zur Verfügung stehen (s. Abbildung 17). Diese Ausführungen zur zweiten Entwurfsphase sollen wiederum an Hand unserer Fallstudie weiter vertieft werden. Abbildung 18 zeigt den Feinentwurf der Teilfunktion „bewegte Stammsätze", wie er sich mit Hilfe der LITOS-Methode ergibt. Diese Teilfunktion 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 („5") aus vier Funktionseinheiten, die als Ergebnis unserer organisatorischen Tätigkeit selbst Ganzheiten darstellen, zugleich aber durch die gemeinsam zu erfüllende Teilfunktion „bewegte Stammsätze" ganzheitlich verbunden sind, nämlich: — — — —
der Druckaufbereitung der Stammdaten dem Verarbeiten aller Bewegungsdaten eines Stammsatzes dem Gruppenwechselprozeß bei Änderung des Ordnungsmerkmals in der Bewegungsdatei dem Lesen eines neuen Stammsatzes aus der Stammdatei, nachdem ein Gruppenwechsel stattgefunden hat (funktioneller Leseprozeß in der Datenkapsel „Eingabe", s. Abb. 14).
Stammdaten sind auch in den beiden Teilfunktionen „Bewegungen ohne Stammsätze" und „nichtbewegte Stammsätze" auszudrucken. Um die Programmlänge zu verkürzen, wird deshalb die Funktionseinheit „Druckaufbereitung Stamm-
6.2 Die vier Entwurfsphasen in LITOS
109
daten" in die Datenkapsel „Drucken" aufgenommen und dort über einen eigenen Eingang angesprochen. Eine ähnliche Feststellung gilt für das Verarbeiten der Bewegungsdaten und den Gruppenwechselprozeß, die beide als interne Prozeduren auch von der Teilfunktion „Bewegungen ohne Stammsätze" aufgerufen werden können. Da in der Regel mehrere Bewegungssätze je Stammsatz vorliegen, wird die Bewegungsdatenverarbeitung in eine DO WHILE-Schleife inkorporiert. Die Komponenten dieser Schleife sind dann bereits drei prozeßorientierte informationelle Teilaufgaben: — — —
Updaten des Stammsatzes durch einen Bewegungssatz Drucken eines Bewegungssatzes Lesen des nächsten Satzes der Bewegungsdatei.
Stößt die Programmsteuerung in dieser Schleife auf einen Bewegungssatz, dessen Kontonummer größer ist als die Kontonummer des Stammsatzes, dann verläßt das Programm die DO WHILE-Schleife und der Steuerfluß geht automatisch zum Gruppenwechselprozeß weiter. Zu den Gruppenwechselarbeiten gehören die Aufbereitung der Summenzeile jeder Gruppe, das Drucken dieser Zeile und das 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 an ganz verschiedenen Stellen im Programm und auf verschiedenen hierarchischen Niveaus des funktionellen Entwurfs zu finden. 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 leere 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. Im Funktionsplan der Abbildung 18 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_ STAMM SAETZE" in einem eigenen Programmteil „DATEISCHLIESSEN" 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.
6. LITOS, eine neue Methode des Softwareentwurfs .
110
BEWEGUNGSSATZ
HIERARCHISCHE STUFE
KONTONUMMER-BEWEGUNG < KONTONUMMER-STAMMSATZ
ZEILE,KONTONUMHER J I W E G U N G ,
2
BEWEGUNGSSATZ ZEILE
DRUCKAUFBEREITUNG STAMMDATEN
A N L E G E N NEUEN
> ZEILE
STAMMSATZ
PUFFER_ STAMMSATZ
VERARBEITEN • EEWEGUNGSDATEN EINES S T A M M S A T Z E S PUFFER_ STAMMSATZ, ZEILE
GRUPPENWECHSEL
Abb. 19
F e i n e n t w u r f d e r T e i l f u n k t i o n „ B e w e g u n g e n o h n e S t a m m s ä t z e " der F a l l s t u d i e .
In gleicher Weise gehen wir beim Feinentwurf der Teilfunktionen „Bewegungen ohne Stammsätze" (Abbildung 19) und „nichtbewegte Stammsätze" (Abbildung 20) vor. Auch hierbei sind zunächst wieder auf der hierarchischen Stufe „5" 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 Stammdatenübergabe an 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".
6.2 Die vier Entwurfsphasen in LITOS
111
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 20). 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 „ 5 " 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-
Abb. 20
Feinentwurf der Teilfunktion „nichtbewegte Stammsätze" der Fallstudie.
112
6. LITOS, eine neue Methode des Softwareentwurfs
sichtlichen und klaren Struktur der Dateiverarbeitungsfunktionen fuhrt, 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. In gleicher Weise muß man nun noch die beiden Datenkapseln „Dateieröffnung/ Eingabe" und „Drucken" top-down strukturieren. Der Programmdesigner geht hierbei so vor, daß er sämtliche funktionellen Eingabe- und Druckprozesse aus den bisherigen Entwürfen zusammenfaßt. Dabei zeigt sich, daß alle Eingaben eine Selektion zwischen dem physischen Lesen von Bewegungssätzen und dem physischen Lesen von Stammsätzen sind, die Druckprozesse eine Selektion zwischen „Druckaufbereitung Stammdaten", „Drucken Bewegungssatz" und „Drucken Summenzeile". Allerdings werden in den Datenkapseln diese Selektionen nicht durch Steuerungsanweisungen sondern durch verschiedene Eingangspunkte realisiert. 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 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 Steuerungs- und CALL-Anweisungen eines Programms zusammen mit denjenigen Prozessen, die Steuerungsvariable erzeugen und verarbeiten.
6.2 Die vier Entwurfsphasen in LITOS
Abb. 21 Steuerungslogik Funktion „Dateiverarbeitung" der Fallstudie.
113
114
6. LITOS, eine neue Methode des Softwareentwurfs
In unserer Fallstudie ist die Steuerungslogik vor allem auf das operationale System beschränkt. Sie beruht 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 funktionelle Lesen der Bewegungsdatei und der Stammdatei. Da in die Schleifensteuerung außerdem aber auch die Endfile-Bewegungen 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 EndfileMaßnahmen je nachdem, ob die Bewegungsdatei oder die Stammdatei als erstes 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, 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ß".
Abb. 22
Steuerungslogik Funktion „Dateischließen" der Fallstudie.
6.2 Die vier Entwurfsphasen in LITOS
115
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. Im Grunde genommen empfiehlt die Fachliteratur schon seit längerem, ein Programmprodukt nicht nur top-down zu entwerfen und zu programmieren, sondern auch beim Testen der einzelnen Programmkomponenten in gleicher Weise vorzugehen [45, S. 197]. Dazu ist es notwendig, die niederen hierarchischen Stufen durch Platzhalter (Dummies) zu simulieren. Sind die höchsten Stufen fehlerfrei, so ersetzt der Programmierer top-down die Platzhalter durch endgültige Programmversionen, wobei er zum Testen der niedrigeren hierarchischen Stufen allerdings neue Platzhalter schreiben muß. Für dieses Top-down-Testen haben sich in der Praxis vier Möglichkeiten herauskristallisiert, Platzhalter zu simulieren, wobei der Programmieraufwand von 1. zu 4. zunimmt: (1)
Simulation der Platzhalter in der überlagerten Abstraktionsstufe: Beispielsweise wird eine CALL-Anweisung in eine PUT LIST-Anweisung als Zeichenkette eingefügt.
(2)
Der Platzhalter druckt selbst einen Hinweis aus, der angibt, daß er in der Programmausführung aufgerufen wurde.
(3)
Beim Aufruf des Platzhalters wird auch die Parameterübergabe simuliert.
(4)
Beim Aufruf des Platzhalters werden von der überlagerten Abstraktionsstufe die Aktualparameter mit übergeben und der Arbeitsprozeß im Platzhalter simuliert, so daß auch die Ergebnisse dieses Prozesses zurückgegeben werden können.
Gegen dieses Top-down-Testen kann eingewendet werden, daß der zusätzliche Programmieraufwand erheblich ist, zumal man die Platzhalter nicht für die endgültige Programmversion einsetzen kann. In der LITOS-Phase 4 wird deshalb versucht, eine Simulationsmethode anzubieten, bei der vom Simulationsprogramm möglichst viel als Rahmenprogramm für die Codierung übernommen werden kann. Wir lehnen uns dabei an den Fall 2 der Top-down-Teststrategie an, indem wir alle relevanten CALL-Anweisungen ausführen lassen. Wir haben schon daraufhingewiesen, 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.
116
6. LITOS, eine neue Methode des Softwareentwurfs . . .
(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 Listen Verarbeitung, ü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 ZEIGERBEWEGUNG; BEWEGUNGSSATZ = P -+ BEWEGUNG; Diese beiden Anweisungen haben folgende Bedeutung. In der Listenverarbeitung 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.
P (Stellung von " P " in der ersten Anweisung)
6.2 Die vier Entwurfsphasen in LITOS
117
Das Programm „MAGNETBANDDATEIVERARBEITUNGSTRUKTURIERT_ V E R S I O N 2 S T E U E R U N G S L O G I K ' ' zeigt nun die vollständige Simulation des strukturierten Entwurfs der Dateiverarbeitung. 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ß der Programmteil „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. MAGNETBAND_DATEIVERARBEITUNG_STRUKTURIERT_VERSION_2_ STEUERUNGSLOGIK
:
PROC OPTIONS(MAIN); / * * * * * * * * * * * * * * * * * VEREINBARUNGSSYSTEM
***************/
DCL DATEIEROEFFNUNG_DATEIVERWALTUNG
ENTRY,
BEWEGUNGSEINGABE
ENTRY,
STAMMSATZEINGABE
ENTRY,
DRUCKROUTINE
ENTRY,
DRUCKAUFBEREITUNG_STAMMDATEN
ENTRY,
DRUCKEN_BEWEGUN GS SAT Z
ENTRY,
DRUCKEN_SUMMENZEILE
ENTRY,
DATEIVERARBEITUNG_DATEISCHLIESSEN ENTRY; / * * * * * * * * * * * * * * * * * * * * STEUERUNGSTEIL * * * * * * * * * * * * * * * * * / CALL DATEIE ROE FFNUN G_DATEIVE RWALTUNG; CALL DRUCKROUTINE; / * * * * * * * * * * * * * * * * * OPERATIONALES SYSTEM
**************/
CALL DATEIVERARBEITUNG_DATEISCHLIESSEN; END MAGNETBAND_DATEIVERARBEITUNG_STRUKTURIERT_VERSION_2 STEUERUNGSLOGIK; /••••••••a********************************************/
118
6. LITOS, eine neue Methode des Softwareentwurfs . . .
/ * * * * * * * * * HIERARCHISCHE STUFE 3
*********************/
DATE IE ROE FFN UN G_DATE I VE RWALTUN G : P ROC ; DCL BEWEGUNGS_SATZ
CHAR
(6),
STAMM_SATZ
CHAR
(6);
/ * SIMULATION FUNKTIONELLE DATENSAETZE DCL
*/
(ENDFILE_BEDINGUNG_STAMMDATEN_EINGABE, ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE)
/ * * * * * * * * * SIMULATION DATEIEN DURCH LISTEN / * SIMULATION BEWEGUNGSDATEI
BIT ( 1) ;
**************/
*/
DCL 1 BEWEGUNGSSATZ BASED (ZEIGER_1) 2 KONTONUMMER_BEWEG CHAR
STATIC,
(6),
2 ZEIGER_BEWEGUNG POINTER, / * SIMULATION VON ORDNUNGSMERKMALEN WORT (10)
CHAR
INIT
*/
(6)
( ' 1 ' , '2" , ' 2 ' , ' 3 ' , ' 3 ' , ' 3 ' ,
'9',
' 12' , '15* , ' 1 5 ' ) , ( HEAD_1,P,Q
) POINTER STATIC;
/ * AUFBAU BEWEGUNGSDATEI
*/
ALLOCATE BEWEGUNGSSATZ ; P,HEAD_1 = ZEIGER_1; KONTONUMME R_BEWE G = WORT ( 1 ) ; DO I = 2 TO 1 0 ; ALLOCATE BEWEGUNGSSATZ ; KONTONUMMER_BEWEG = WORT ( I ) ; P ->
ZEIGER_BEWEGUNG, P = ZEIGE R_1 ; END;
P - > ZEIGER_BEWEGUNG = NULL / * SIMULATION STAMMDATEI
*/
DCL 1 STAMMSATZ BASED (ZEIGER_2) 2 KONTONUMMER CHAR
(); STATIC,
(6),
2 ZEIGER_STAMM POINTER, / * SIMULATION VON ORDNUNGSMERKMALEN WO RT_1
(5)
INIT
CHAR
*/
(6)
('1','2', '4', '9',
'11'),
6.2 Die vier Entwurfsphasen in LITOS
119
HEAD_2 POINTER
STATIC;
/ * AUFBAU STAMMDATEI
*/
ALLOCATE STAMMSATZ; P,HEAD_2 = ZEIGER_2; KONTONUMMER = WORT_1
(1);
DO I = 2 TO 5 ; ALLOCATE STAMMSATZ ; KONTONUMMER = WORT_1
(I);
P -> ZEIGER_STAMM,P = ZEIGER_2; END; P - > ZEIGER_STAMM
= NULL
();
DCL (K ONT ON UMME R_BE WEGU N G,K ONT ON UMME R_ST AMMSAT Z) CHAR
(6);
/ * * * * * * * * * SIMULATION DATEIEROEFFNUNG
****************/
P = HEAD_1 ; Q = HEAD_2; RETURN; / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
BEWEGUNGSEINGABE
: ENTRY
(BEWEGUNGS_SATZ, ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE); I F P = NULL () THEN ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE
=
'1•B;
ELSE DO; K ONT ON UMME R_BEWE GUN G = P -> KONT ONUMME R_BEWE G; P = P ->
ZEIGER_BEWEGUNG;
END ; I F ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE
=
'1'B
THEN RETURN; BEWEGUNGS_SATZ = KONTONUMMER_BEWEGUNG; RETURN ; / A * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
STAMMSATZEINGABE
: ENTRY
(STAMM_SATZ,ENDFILE _BEDINGUNG_STAMMDATEN_EINGABE) ; I F Q = NULL () THEN
120
6. LITOS, eine neue Methode des Softwareentwurfs . . . ENDFILE_BEDINGUNG_STAMMDATEN_EINGABE
=
'1'B;
ELSE DO; KONTONUMMER_STAMMSATZ = Q -> KONTONUMMER; Q = Q -> ZEIGER_STAMM; END; I F ENDFILE_BEDINGUNG_STAMMDATEN_EINGABE
=
"1'B
THEN RETURN; STAMM_SATZ = K ON T ON UMME R_S T AMMS AT Z RETURN ; END DATEIEROEFFNUNG_DATEIVERWALTUNG; /A****************************************************/ DRUCKROUTINE : PROC; DCL LISTE FILE RECORD OUTPUT ENV
(F(88)CTLASA);
/ * VEREINBARUNG SIMULATION DRUCKLISTE DCL 1 ZEILE
*/
STATIC,
2 VORSCHUBSTEUERUNG
CHAR(1),
2 LEERZEICHEN^
CHAR(3),
2 KONTONUMMER
CHAR(6),
2 LEERZEICHEN_2
CHAR(2),
2 FREE
CHAR(14),
2 REST
CHAR(62);
OPEN FILE
(LISTE);
DCL ZEILE_1 CHAR (88) ZEILE_1 = WRITE FILE
DEFINED Z E I L E ;
'0'; (LISTE)
FROM
(ZEILE);
RETURN ; / * * * * * * * * * * * * * ENDE LOKALE VEREINBARUNGEN DRUCKAUFBEREITUNG_STAMMDATEN
************/
: ENTRY
(KONTONR_STAMMSATZ,SALD_STAMMSATZ); DCL KONTONR_STAMMSATZ CHAR SALD_STAMMSATZ
CHAR
(6), (62);
ZEILE .KONTONUMMER = KONTONR_STAMMSATZ ; ZEILE .FREE
= SALD_STAMMSATZ; RETURN;
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
6.2 Die vier Entwurfsphasen in LITOS DRUCKEN_BEWEGUNGSSATZ
121
: ENTRY
(TEXT);
DCL TEXT C H A R ( 6 2 ) ; REST = TEXT; WRITE FILE
(LISTE)
ZE ILE_1
= •
FROM ( Z E I L E ) ; ' ;
RETURN ; / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
DRUCKEN_SUMMENZEILE
: ENTRY
(TEXT_1);
DCL TEXT_1 CHAR (6 2) ; REST = TEXT_1; WRITE FILE
(LISTE)
ZEILE_1
=
FROM
(ZEILE);
'O';
RETURN; END DRUCKROUTINE; /*****************************************************/ DATEIVERARBEITUNG_DATEISCHLIESSEN
: PROC;
DCL (ENDFILE_BEDINGUNG_STAMMDATEN _EINGABE, ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE) STATIC I N I T / * * * * * * * * * LOKALE SATZVEREINBARUNGEN
(1)
('O'B);
**********/
/ * VEREINBARUNG BEWEGUNGSSATZ DCL KONTONUMMER_BEWEGUNG
BIT
*/
CHAR(6);
/ * VEREINBARUNG STAMMSATZ * / DCL KONTONUMMER_STAMMSATZ DCL BEWEGUNGSSATZ CHAR(6) STAMMSATZ DCL HELP
CHAR(6)
CHAR(6); DEFINED K ONT ON UMME R_BEWE GUN G, DEFINED KONTONUMMER_STAMMSATZ;
CHAR(62);
CALL BEWEGUNGSEINGABE (BEWEGUNGSSATZ, ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE); CALL STAMMSATZEINGABE (STAMMSATZ,ENDFILE_BEDINGUNG_STAMMDATEN_EINGABE); / * * * * * * * * * * * * * DATEIVERARBEITUNG
*************/
DO WHILE (ENDFILE BEDINGUNG BEWEGUNGSDATEN EINGABE = ' O ' B &
122
6. LITOS, eine neue Methode des Softwareentwurfs . . . ENDFILE_BEDINGUNG_STAMMDATEN_EINGABE
=
'O'B);
I F KONTONUMMER_BEWEGUNG = KONTONUMMER_STAMMSATZ THEN CALL BEWEGTE_STAMMSAETZE; IF
(K ONT ON UMME R_BEWE GUN G > KONTONUMMER_STAMMSATZ) & (ENDFILE_BEDINGUNG_STAMMDATEN_EINGABE
=
'O'B)
THEN
CALL NICHTBEWEGTE_STAMMSAETZE; IF
(KONTONUMMER_BEWEGUNG < KONTONUMMER_STAMMSATZ) & (ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE
=
'O'B) THEN
CALL BEWEGUNGEN_OHNE_STAMMSAETZE; END; / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
/*************
DATEISCHLIESSEN
HELP = '
*************/
NACH EOF
CALL DRUCKEN_BEWEGUNGSSATZ
'; (HELP);
I F ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE
=
'1'
THEN DO; DO WHILE
(ENDFILE_BEDINGUNG_STAMMDATEN_EINGABE =
'O'B);
CALL NICHTBEWEGTE_STAMMSAETZE; END; END; I F ENDFILE_BEDINGUNG_STAMMDATEN_EINGABE
=
•1'B
THEN DO; DO WHILE
{ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE =
'O'B) ;
CALL BEWEGUNGEN_OHNE_STAMMSAETZE; END; END; /*************
ENDE HIERARCH.
/*************
PROZEDUREN HIERARCH.
BEWEGTE_STAMMSAETZE HELP = CALL
STUFE
3
*************/
STUFE
: PROC;
'NUMMER STAMM';
DRUCKAUFBEREITUNG_STAMMDATEN (KONTONUMMER_STAMMSATZ,HELP);
4
*******/
6.2 Die vier Entwurfsphasen in L I T O S CALL
123
VERARBEITEN_BEWEGUNGSDATEN
(KONTONUMMER_ STAMMSATZ);
HELP
CALL
=
'GRUPPENWECHSELPROZESS';
/*
GRUPPENWECHSEL
DRUCKEN_SUMMENZEILE /*
CALL
*/
ENDE
GRUPPENWECHSEL
(HELP); */
STAMMSATZEINGABE (STAMMSATZ, ENDFILE_BEDINGUNG_STAMMDATEN_EINGABE);
END BEWEGTE_STAMMSAETZE; / * * * * * * * * * * * * * * * * * * * * * * * * *,* * * * * * * * * * * * * * * * * * * * * * * * * * * * / NICHTBEWEGTE_STAMMSAETZE 'NUMMER
:
PROC;
HELP
=
STAMM';
CALL
DRUCKAUFBEREITUNG_STAMMDATEN
HELP
=
CALL
D RU CK EN _BEWE GUN GS S AT Z
CALL
STAMMSATZEINGABE
(KONTONUMMER_STAMMSATZ,HELP); 'NICHTBEWEGTER
STAMMSATZ'; (HELP) ;
(STAMMSATZ, ENDFILE_BEDINGUNG_STAMMDATEN_EINGABE); END N I C H T B E W E G T E _ S T A M M S A E T Z E ; /*****************************************************/
BEWEGUNGEN_OHNE_STAMMSAETZE DCL
:
PROC;
PUFFER_KONTONUMMER_STAMMSATZ
LIKE
KONTONUMMER_ STAMMSATZ;
HELP
=
CALL
DRUCKAUFBEREITUNG_STAMMDATEN
'NEUE
NUMMER';
(KONTONUMMER_BEWEGUNG,
HELP);
PUFFER_KONTONUMMER_STAMMSATZ
=
KONTONUMME R_BEWEGUNG; CALL
VE RARBEITEN_BEWEGUNGSDATEN (PUFFER_KONTONUMMER_STAMMSATZ);
HELP
=
'GRUPPENWECHSELPROZESS
CALL
DRUCKEN
OHNE SUMMENZEILE
(HELP);
BEWEGUNGEN STAMMSATZ';
6. LITOS, eine neue Methode des Softwareentwurfs
124
END BEWEGUNGEN_OHNE_STAMMSAETZE; / * * * * * * * * * * * * * ENDE PROZ. HIERARCH. STUFE 4 / * * * * * * * * * * * * * HIERARCHISCHE STUFE 5 VE RARBEITEN_BEWEGUNGS DATEN : PROC
**********/
*****************/
(KONTONUMMER_STAflM _SATZ);
DCL KONTONUMMER_STAMM_SATZ CHAR ( 6 ) ; DO WHILE
(KONTGNUMMER_BEWEGUNG = KONTONUMMER_STAMM_SATZ & ENDFILE_BEDINGUNG_BEWEGUNGSDATEN _EINGABE = / * UPDATEN STAMMSATZ
'01B);
*/
HELP = KONTONUMMER_BEWEGUNG; SUBSTR ( H E L P , 6 , 5 6 )
= 'NR.BEWEGUNG
/ * DRUCKEN BEWEGUNGSSATZ
PROZESS"; */
CALL DRUCKEN_BEWEGUNGSSATZ /*
LESEN BEWEGUNGSSATZ
(HELP);
*/
CALL BEWEGUNGSEINGABE (BEWEGUNGSSATZ, ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE) ; END; END VERARBEITEN_BEWEGUNGSDATEN; /*****************************************************/ END DATEIVERARBEITUNG_DATEISCHLIESSEN; /*****************
PROGRAMMENDE
**********************/
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" ( „ H E A D _ 1 " und „ H E A D _ 2 " ) zuweist. Dieselben Hilfszeiger benutzen wir für das Lesen der Bewegungsdatei und der Stammdatei. Sobald ein Hilfszeiger dabei den Wert „Null" annimmt, hat die Programmausführung das Dateiende erreicht, so daß sie automatisch zu den Dateiendearbeiten weitergeht. Dabei ist zu berücksichtigen, daß die nächste Zuordnungsanweisung „KONTONUMMER_BEWEGUNG = P -> KONTONUM M E R _ B E W E G " b z w . „KONTONUMMER_STAMMSATZ = Q->KONTONUM M E R " nicht mehr ausführbar ist. Die Programmausführung muß diese Anweisung deshalb im Fall des Listenendes überspringen. Wir erreichen dies durch eine zusätzliche IF-Anweisung, die später im echten Programm durch eine ON END FILE-Anweisung ersetzt wird.
6.3 Strukturierte Programmierung der Fallstudie
125
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. Unsere eigene Erfahrung hat gezeigt, daß der Aufwand für ein solches Simulationsprogramm kleiner ist als die Programmierung von Platzhaltern.
6. LITOS, eine neue Methode des Softwareentwurfs
126
tsi
saco Eh CO
M z« o z w o
co CO w N o OS
eu co co w tsj O OS o 2 04 5 ij o H w CO s « w u m M • 2 os z H O4 04 D OS O
0 THEN ZEILE.HABEN = BETRAG_BEWEGUNG
ELSE ZEILE.SOLL
= BETRAG_BEWEGUNG
I F ZEILE.SOLL
= O THEN LOESCHUNG = '
I F ZEILE.KONTONUMMER
=
1
THEN ZEILE.VORSCHUBSTEUERUNG = WRITE FILE
(LISTE)
' ;
' '0';
FROM ( Z E I L E ) ;
ZEILE_1
= '
' ;
RETURN ; /•A**************************************************/ DRUCKEN_SUMMENZEILE
: ENTRY
DCL SALDO_STAMMSATZ
PIC'S(7)9V99';
(SALDO_STAMMSATZ);
SELECT; WHEN (SIGN
(SALDO_STAMMSATZ)
ANZEIGE_SOLL_HABEN_NEU WHEN (SIGN
=
= 1)
'H';
(SALDO_STAMMSATZ)
ANZEIGE_SOLL_HABEN_NEU
=
= -1)
'S';
OTHERWISE ANZEIGE_SOLL_HABEN_NEU
1
= '
;
END; ZEILE.NEUERSALDO WRITE FILE
(LISTE)
ZEILE_1
= SALDO_STAMMSATZ; FROM
(ZEILE); =
'0';
RETURN; END DRUCKROUTINE; z***************************************************** DATEIVERARBEITUNG_DATEISCHLIESSEN
: PROC;
DCL MAUSGAB FILE RECORD OUTPUT; DCL
(ENDFILE_BEDINGUNG_STAMMDATEN_EINGABE, ENDFILE BEDINGUNG BEWEGUNGSDATEN EINGABE)
BIT
(1)
132
6. LITOS, eine neue Methode des Softwareentwurfs . . . STATIC I N I T
/ * * * * * * * * * LOKALE SATZVEREINBARUNGEN
('O'B);
*********/
/ * VEREINBARUNG BEWEGUNGSSATZ
*/
DCL 1 BEWEGUNGS_SATZ, 2 KONTONUMMER_BEWEGUNG
CHAR(6),
2 BELEGNUMMER_BEWEGUNG
CHAR(4),
2 DATUM_BEWEGUNG
CHAR(6),
2 BETRAG_BEWEGUNG
PIC'S9999V99',
2 TEXT
CHAR(56),
2 KARTENART
CHAR( 1 ) ;
/ * VEREINBARUNG STAMMSATZ
*/
DCL 1 STAMM_SATZ, 2 SATZART_STAMMSATZ
CHAR(1),
2 KONTONUMMER_STAMMSATZ
CHAR(6),
2 DATUM_STAMMSATZ
CHAR(6),
2 SALDO_STAMMSATZ DCL BEWEGUNGSSATZ CHAR(80) STAMMSATZ OPEN FILE
PIC'S7(9)V99'; DEFINED BEWEGUNGS_SATZ,
CHAR(2 3) DEFINED STAMM_SATZ; (MAUSGAB);
CALL BEWEGUNGSEINGABE (BEWEGUNGSSATZ, ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE); CALL STAMMSATZEINGABE (STAMMSATZ,ENDFILE_BEDINGUNG_STAMMDATEN_EINGABE); / * * * * * * * * * * * * * DATEIVERARBEITUNG
*************/
DO WHILE (ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE ENDFILE_BEDINGUNG_STAMMDATEN_EINGABE IF
= 'O'B & =
'O'B);
KONTONUMMER_BEWEGUNG = KONTONUMMER_STAMMSATZ
THEN
CALL BEWEGTE_STAMMSAETZE; IF
(KONTONUMMER_BEWEGUNG > KONTONUMMER_STAMMSATZ) & (ENDFILE_BEDINGUNG_STAMMDATEN_EINGABE
= 'O'B)
THEN
CALL NICHTBEWEGTE_STAMMSAETZE; IF
(KONTONUMMER_BEWEGUNG < KONTONUMMER_STAMMSATZ) & (ENDFILE BEDINGUNG BEWEGUNGSDATEN EINGABE =
"O'B)
6.3 Strukturierte Programmierung der Fallstudie
133 THEN
CALL BEWEGUNGEN_OHNE_STAMMSAETZE; END; / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
/ * * * * * * * * * * * * * DATEISCHLIESSEN IF
*************/
EN D FILE _BE DIN GUN G_BE WE GUN GS DATEN _E IN GABE = ' 1 ' THEN DO;
DO
WHILE
(ENDFILE_BEDINGUNG_STAMMDATEN_EINGABE =
1
01B) ;
CALL NICHTBEWEGTE_STAMMSAETZE; END; END; I F ENDFILE_BEDINGUNG_STAMMDATEN_EINGABE
=
'1'B
THEN DO; DO WHILE
(ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE =
1
O'B) ;
CALL BEWEGUNGEN_OHNE_STAMMSAETZE; END; END; / * * * * * * * * * * * * * ENDE HIERARCH. STUFE 3
****************/
/ * * * * * * * * * * * * * PROZEDUREN HIERARCH. STUFE 4 BEWEGTE_STAMMSAETZE
**********/
: PROC;
CALL DRUCKAUFBEREITUNG_STAMMDATEN (KONTONUMMER_STAMMSATZ,SALDO_STAMMSATZ); CALL VE RARBEITEN_BEWEGUN GS DATEN
(STAMM_SATZ);
/ * GRUPPENWECHSEL * / CALL DRUCKEN_SUMMENZEILE WRITE FILE
(SALDO_STAMMSATZ);
(MAUSGAB) FROM (STAMM_SATZ);
/ * ENDE GRUPPENWECHSEL * / CALL STAMMSATZEINGABE (STAMMSATZ, EN DFI LE_BE DIN GUN G_ST AMMDATEN _E IN GABE) ; END BEWEGTE_STAMMSAETZE; /*****************************************************/ NICHTBEWEGTE STAMMSAETZE : PROC;
134
6. LITOS, eine neue Methode des Softwareentwurfs . . . DCL NULL_BETRAG
PIC'S9999V99';
NULLJ3ETRAG = O ; CALL DRUCKAUFBEREITUNG STAMMDATEN (KONTONUMME R_ST AMMSAT Z,SALDO_STAMMSATZ) ; CALL DRUCKEN_BEWE GUN GS S AT Z ( '
' ,
DATUM__STAMMSATZ ,NULL_BETRAG) ; WRITE F I L E CALL
(MAUSGAB)
FROM
(STAMM_SATZ);
STAMMSATZEINGABE (STAMMSATZ, ENDFILE_BEDINGUNG_STAMMDATEN_EINGABE);
END NICHTBEWEGTE_STAMMSAETZE; /*****************************************************/ BEWEGUNGEN_OHNE_STAMMSAETZE
:
DCL 1 PUFFER_STAMMSATZ
PROC;
LIKE
STAMM_SATZ;
PUFFER_STAMMSATZ.SALDO_STAMMSATZ CALL
=
0;
DRUCKAUFBEREITUNG_STAMMDATEN (KONTONUMME R_BEWEGUNG, PUFFER_STAMMSATZ.SALDO_STAMMSATZ);
PUFFER_STAMMSATZ.KONTONUMMER_STAMMSATZ
=
KONTONUMMER_BEWEGUNG; CALL
VERARBEITEN_BEWEGUNGSDATEN (PUFFER_STAMMSATZ);
CALL D RUCK EN _ S UMMEN Z E I LE (PUFFE R_STAMMSATZ.SALDO_STAMMSATZ) ; WRITE F I L E END
(MAUSGAB)
FROM
(PUFFER_STAMMSATZ);
BEWEGUNGEN_OHNE_STAMMSAETZE;
/*************
ENDE P R O Z . H I E R A R C H . STUFE
/*************
HIERARCHISCHE
VERARBEITEN_BEWEGUNGSDATEN
STUFE
: PROC
5
4
**********/
*****************/
(STAMM_SATZ);
DCL 1 STAMM_SATZ, 2 SATZART_STAMMSATZ
CHAR
(1),
2 KONTONUMMER_STAMMSATZ
CHAR
(6),
2 DATUM_STAMMSATZ
CHAR
(6),
2 SALDO_STAMMSATZ DO WHILE
PIC'S9999999V99';
(KONTONUMMER BEWEGUNG = KONTONUMMER.STAMMSATZ &
135
6.3 Strukturierte Programmierung der Fallstudie
EN DFILE_BE DIN GUN G_BEWE GUNGS DATEN _EINGABE = / * UPDATEN STAMMSATZ
'0'B);
*/
SATZART_STAMMSATZ = KARTENART; DATUM_STAMMSATZ
= DATUM_BEWEGUNG;
SALDO_STAMMSATZ
= SALDO_STAMMSATZ + BETRAG_BEWEGUNG;
/ * DRUCKEN BEWEGUNGSSATZ
*/
CALL DRUCKEN_BEWEGUNGSSATZ (BELEGNUMMER_BEWEGUNG,DATUM_BEWEGUNG, BET RAG_BEWE GUN G) ; / * LESEN BEWEGUNGSSATZ
*/
CALL BEWEGUNGSEINGABE (BEWEGUNGSSATZ, ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE); END; END VERARBEITEN_BEWEGUNGSDATEN; /*****************************************************/ END DATEIVERARBEITUNG_DATEISCHLIESSEN; / * * * * * * * * * * * * * PROGRAMMENDE
**************************/
Dieses Kapitel wollen wir abschließen, indem wir den zweiten Versuch, die Fallstudie strukturiert zu programmieren, zusammenfassend analysieren. Es hat sich gezeigt, daß durch einen methodischen Programmentwurf, der eine organisatorische Tätigkeit darstellt, die Programmierung zur reinen Codiertätigkeit wird. Sie beginnt mit der Definition der Arbeitsobjekte, die sich auf ein möglichst kleines globales Vereinbarungssystem und lokale Vereinbarungsteile in den Datenkapseln aufteilen. Als Bezeichner für diese Arbeitsobjekte sollen die Programmierer leicht verständliche Namen wählen, damit dieses Vereinbarungssystem bereits dokumentierenden Charakter hat. Diese Regel wird leider nicht einmal in der Fachliteratur konsequent eingehalten (s. z. B. [25, S. 211 ]). Willkürliche Abkürzungen für Variable, wie „X" oder „Y", können in mathematischen Anwendungen vertretbar sein. Für das breite Spektrum der betrieblichen Datenverarbeitung sind sie nicht benutzerfreundlich und daher abzulehnen. Der zusätzliche Schreib aufwand des Codierers, der durch sprechende Bezeichner entsteht, wird durch die bessere Lesbarkeit und damit auch die leichtere Wartung eines solchen Programmsystems ausgeglichen. Um das Qualitätsmerkmal der Änderungsfreundlichkeit zu erreichen, ist eine genaue „Buchführung" über die
136
6. LITOS, eine neue Methode des Softwareentwurfs
Arbeitsobjekte und ihrer Attribute zu fordern. In Analogie zu einem Kompilierer könnte man auch daran denken, die Funktionen des Vereinbarungsteils zu automatisieren (Data Dictionary). Nach dem Vereinbarungssystem wird das „Steuerungssystem" programmiert. Es übernimmt ausschließlich Steuerungsaufgaben, die in der herkömmlichen Programmierung eine „Hauptsteuerleiste" ausführte. Von hier ab können dann einzelne Programmierer oder Programmierteams selbständig Programmodule parallel programmieren; denn sowohl der Steuerfluß als auch der Datenfluß sind durch die Entwurfsphase und die Programmierung der Hauptsteuerleiste vorgegeben und, soweit es den Steuerfluß betrifft, auch schon überprüft. Es ist auch denkbar, in größeren Softwaresystemen ebenfalls den Datenfluß in Analogie zum Steuerfluß zu simulieren und damit zu testen. Im Programm „MAGNETBAND DATEIVERARBEITUNG STRUKTURIERT VERSION 2 " ist es gelungen, Relikte der herkömmlichen Programmierung, wie Programmschalter und GOTO-Anweisungen, zu vermeiden, ohne daß darunter die Benutzerfreundlichkeit infolge Programmiertricks leidet. GOTO-ähnliche Strukturen kommen auch nicht mehr vor. Eine Ausnahme bildet nur das Steuerungssystem. Weil bereits vor der Programmierung ein Top-down-Entwurf vorlag, konnten die Regeln der strukturierten Programmierung in der Codierphase konsequent eingehalten werden. Programmteile mit globaler Bedeutung, in unserem Beispiel allein verkörpert durch die ENTRY-Vereinbarungen, werden nach dem Hauptteil notiert und auch erst an dieser Stelle im Programm abgewickelt. Wenn man das Geheimnisprinzip für den Programmentwurf einsetzt, ergibt sich eine vereinheitlichte Programmstruktur, die sich folgendermaßen skizzieren läßt:
6.4 Zur Änderungsfreundlichkeit von LITOS-Produkten
137
Eine solche Systematik in der Programmierung hat zur Folge, daß sich, unabhängig von der Aufgabenstellung, eine einheitliche Programmstruktur ergibt, ein Ziel, das auch die normierte Programmierung verfolgt. Die Programmliste zusammen mit den Ergebnissen des Grob- und Feinentwurfs wirkt nahezu selbstdokumentierend, so daß sich weitere verbale Beschreibungen in der Programmdokumentation fast erübrigen.
6.4 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:
138
6. LITOS, eine neue Methode des Softwareentwurfs
— 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 „MAGNETBAND DATEIVER 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 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} {11,15,13,16,20}
Das falsch sortierte Ordnungsmerkmal „ 1 3 " 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. Der Programmteil DATEIVERARBEITUNG ruft deshalb die Teilfunktion Bewegungen ohne Stammsätze auf. Wenn auf den Bewegungssatz „ 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 Programm die Sortierkontrolle einzubauen ist. 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 muß deshalb vor dem Gruppen Wechselprozeß und zwar nach dem Lesen des Bewegungssatzes ansprechen. Da das Programm auf Grund des Geheimnisprinzips zwischen funktionellen und physischen Leseprozessen differenziert, gibt es zwei Möglichkeiten, eine Sortierkontrolle zu entwerfen: (1) (2)
nach dem physischen Leseprozeß, d.h. in der Datenkapsel , ,DATEIEROEF FNUNG _DATEI VERWALTUNG " nach dem funktionellen Leseprozeß (s. Abbildung 18).
Da aber die Datenverwaltung im Sinne des Geheimnisprinzips den Inhalt eines physischen Datensatzes und damit auch die Ordnungsmerkmale, die die Sortier-
6.4 Zur Änderungsfreundlichkeit von LITOS-Produkten
139
Abb. 24 Feinentwurf der Prozedur „Verarbeiten Bewegungsdaten" mit Sortierkontrolle der Bewegungsdatei.
folge konstitutieren, nicht kennt, ist die Sortierkontrolle nach dem funktionellen Leseprozeß in das Programm einzubauen. Zu diesem Zweck nehmen wir in die Funktionseinheit „Lesen Bewegungssatz" eine zusätzliche Anweisung auf, die ein Unterprogramm „Sortierkontrolle" aufruft (s. Abbildung 24). Es überprüft die Sortierreihenfolge. Wenn dort ein Fehler gefunden wird, übergibt es an die Datenkapsel „DRUCKROUTINE" eine Fehlermeldung (neuer Eingangspunkt: „DRUCKEN_FEHLERMELDUNG"), die anstelle des neuen Saldos einen Hinweis ausdruckt. Anschließend wird der nächste Bewegungssatz gelesen, so daß der Programmablauf 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. Da das Programm nach dem Geheimnisprinzip entworfen wurde, kann man die Teile des Programms, die geändert werden müssen, leicht lokalisieren. Da sowohl der Druckprozeß als auch der Verarbeitungsprozeß von der Sortierkontrolle beeinflußt wird, gehören die geänderten Stellen zur „DRUCKROUTINE" und zur Datenkapsel „DATEIVER ARBEITUNG_DATEISCHLIESSEN" und zwar dort zum Vereinbarungsteil und der internen Prozedur,,VERARBEITEN_BEWEGUNGSDATEN". Der nachfolgende Programmausschnitt weist die geänderten Programmteile aus.
140
6. LITOS, eine neue Methode des Softwareentwurfs . . .
MAGNETBAND_DATEIVERARBEITUNG_STRUKTURIERT_VERSION_2: PROC OPTIONS
(MAIN);
/ * * * * * * * * * * * * * * EINBAU SORTIERKONTROLLE
**************/
/ * * * * * * * * * * * * * * VEREINBARUNGSSYSTEM
******************/
/ * * * * * * * * * * * * * * NEUE VEREINBARUNGEN
******************/
DCL
DRUCKEN_FEHLER
ENTRY;
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
/ * REST MAIN_PROZEDUR, DATEIEROEFFNUNG_DATEIVE RWALTUNG, DRUCKROUTINE,
DRUCKAUFBEREITUNG_
STAMMDATEN, DRUCKEN_BEWEGUNGSSATZ,
DRUCKEN_
SUMMENZEILE WIE URSPRUENGLICHE FASSUNG * / /*****************************************************/ / * * * * * * * * * * * * * * EINBAU SORTIERKONTROLLE
**************/
DRUCKEN_FEHLER : ENTRY (KONTONUMMER_ZEILE,BELEGNUMMER_BEWEGUNG,DATUM_BEWEGUNG, TEXT); DCL KONTONUMMER_ZEILE C H A R ( 6 ) , TEXT
CHAR(10),
TEX
CHARCIO)
TEX
= TEXT;
ZEILE.KONTONUMMER
= KONTONUMMER_ZEILE;
DEFINED ZEILE
POSITION(74);
ZEILE.BELEG_NUMMER = BELEGNUMMER_BEWEGUNG; IF
ZEILE.DATUM
= DATUM_BEWEGUNG;
ZEILE.KONTONUMMER
= '
' THEN ZEILE.VORSCHUB
STEUERUNG = WRITE FILE ZEILE_1
(LISTE)
FROM = '
'O';
(ZEILE); 1
;
RETURN; / * * * * * * * * * * * * * * ENDE DER SORTIERKONTROLLE * * * * * * * * * * * * / /*****************************************************/ DATEIVERARBEITUNG_DATEISCHLIESSEN
: PROC;
/ * * * * * * * * * * * * * * EINBAU SORTIERKONTROLLE DCL ALTE_KONTONR
CHAR
**************/
(6);
ALTE_KONTONR = KONTONUMMER_BEWEGUNG; / * REST' DATEIVERARBEITUNG,
DATEISCHLIESSEN,
6.4 Zur Änderungsfreundlichkeit von LITOS-Produkten
141
BEWEGTE_STAMMSAETZE, NICHTBEWEGTE_STAMMSAETZE, BEWEGUNGEN_OHNE_STAMMSAETZE WIE URSPRUENGLICHE FASSUNG */ /************* HIERARCHISCHE STUFE 5 *****************/ VE RARBEITEN_BEWEGUNGSDATEN : PROC (STAMM_SATZ); DCL 1 STAMM_SATZ, 2 SATZART_STAMMSATZ
CHAR (1),
2 KONTONUMMER_STAMMSATZ CHAR (6), 2 DATUM_STAMMSATZ
CHAR (6),
2 SALDO_STAMMSATZ
PIC 1S9999999V99';
DO WHILE (KONTONUMMER_BEWEGUNG = KONTONUMMER_STAMMSATZ & ENDFILE_BEDINGUNG_BEWEGUNGSDATEN EINGABE = '0'B); /* UPDATEN STAMMSATZ */ SATZART_STAMMSATZ = KARTENART; DATUM_STAMMSATZ
= DATUM_BEWEGUNG;
SALDO_STAMMSATZ
= SALDO_STAMMSATZ + BETRAG_BEWEGUNG;
/* DRUCKEN BEWEGUNGSSATZ */ CALL DRUCKEN_BEWEGUNGSSATZ (BELEGNUMMER_BEWEGUNG,DATUM_BEWEGUNG, BETRAG_BEWEGUNG); /* LESEN BEWEGUNGSSATZ */ CALL BEWEGUNGSEINGABE (BEWEGUNGSSATZ, ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE); /************* EINBAU SORTIERKONTROLLE ***************/ CALL SORTIERKONTROLLE; END; SORTIERKONTROLLE : PROC; DCL TEXT CHAR (10); DO WHILE (KONTONUMMER_BEWEGUNG < ALTE_KONTONR & ENDFILE_BEDINGUNG_BEWEGUNGSDATEN _E INGABE = 'O'B) ; TEXT = 'SORTFEHLER1;
6. LITOS, eine neue Methode des Softwareentwurfs
142
CALL DRUCKEN_FEHLER (KONTONUMMER_BEWEGUNG,BELEGNUMMER_BEWEGUNG, DATUM_BEWEGUNG,
TEXT);
CALL BEWEGUNGSEINGABE (BEWEGUNGSSATZ, ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE); END; ALTE_KONTONR = KONTONUMMER_BEWEGUNG; RETURN; END SORTIERKONTROLLE; END VERARBEITEN_BEWEGUNGSDATEN; / * * * * * * * * * * * * 1: ****************************************/ END DATEIVERARBEITUNG_DATEISCHLIESSEN; / * * * * * * * * * * * * * PROGRAMMENDE
**************************/
In einem zweiten Beispiel wollen wir diskutieren, wie in der LITOS-Methode entworfene Programme auf größere Programmänderungen reagieren. Zu diesem Zweck gehen wir von der Aufgabe aus, die Fallstudie auf das Verfahren „B" nach Langers (s. Kapitel 3.1) umzustellen. Die Bewegungsdaten werden nach wie vor in aufsteigender Folge der Ordnungsmerkmale sortiert angeliefert und auch in derselben Reihenfolge verarbeitet. Die Stammdatei greift aber zu ihren Datenbeständen sequentiell oder direkt zu. Sie ist damit in der Lage, unmittelbar Auskünfte über einzelne Kontenbestände zu geben. Als Speichermittel für diese Stammdatei wird ein Magnetplattenspeicher unterstellt. Häufig werden bei dieser Organisationsform im Updateprozeß die nichtbewegten Stammsätze nach der Verarbeitung der Bewegungsdatei ausgedruckt (s. z. B. [43, S. 222]). Wir wollen jedoch beim Übergang vom Verfahren ,,A" nach „B" an der Liste, die der Drucker ausgibt, nichts ändern (s. Abbildung 2). Diese Spezifikation bedeutet, daß nichtbewegte Stammsätze an der Stelle der Sortierfolge auszudrucken sind, an der sie in der Stammdatei stehen. Diese Forderung läßt sich nur dadurch erfüllen, daß die Programmlogik, die auf dem Vergleich der Ordnungsmerkmale von Bewegungsdatei und Stammdatei beruht, erhalten bleibt. Es ändert sich deshalb am Grobentwurf des Programmsystems, das diese Aufgabe löst (s. Abbildung 14), nichts. Dieselbe Feststellung gilt auch für den Feinentwurf (Abbildungen 18, 19, 20). Um Bewegungsdaten sowohl sortiert als auch unsortiert verarbeiten zu können, greift man in der Praxis häufig auf die indexsequentielle Dateiorganisation zurück [50, S. 58], die über einen Adreßindex einen direkten Zugriff zu gespeicherten Stammsätzen zuläßt. Die sequentielle Verarbeitungsform verlangt.
6.4 Zur Änderungsfreundlichkeit von LITOS-Produkten
143
daß die Stammsätze lückenlos gespeichert sind. Für Stammsätze, die im Zuge der Teilfunktion „Bewegungen ohne Stammsätze" neu anzulegen sind, ist deshalb kein Speicherplatz vorhanden. Um sie in die vorgegebene Ordnungsfolge einreihen zu können, wäre es notwendig, die gesamte Stammdatei von der betreffenden Stelle ab in der Menge der Ordnungsmerkmale um einen Datensatz zu verschieben, was natürlich mit einem großen Zeitaufwand verbunden ist. Um diese ständige Umorganisation zu vermeiden, wird in der Regel in der indexsequentiellen Dateiorganisation ein Überlaufbereich angelegt, der alle Neuzugänge aufnimmt [50, S. 67], so daß nur in gewissen Zeitabständen eine Neuorganisation einer solchen Stammdatei notwendig ist. Diese Schwierigkeiten vermeidet die direkte Adressierung, bei der ein Stammsatz unmittelbar unter seinem Ordnungsmerkmal in der Datei gespeichert wird [50, S. 223]. In PL/1 kann man diese Dateiform durch das Dateiattribut „REGIONAL (1)" definieren [42, S. 206]. Hierbei wird der gesamte Plattenspeicher in Regionen unterteilt und die einzelne Region, die einen Datensatz aufnimmt, über eine Nummer angesprochen. Sie ist identisch mit dem Ordnungsmerkmal des zu speichernden Datensatzes. Da in der Regel nicht alle Elemente aus der vorgegebenen Menge von Ordnungsmerkmalen besetzt sind, treten Lücken im Datenbestand auf. Die Fachliteratur nennt deshalb diese Dateiform auch „gestreut gespeicherte Organisation" [43, S. 84]. Die nichtaktiven Sätze, die diese Lücken verursachen, sind in PL/1 in ihrer höchsten Stelle durch ein Byte markiert, das aus acht. Binärzeichen Ems besteht [43, S. 211]. Die PL/1-Nomenklatur nennt sie „Scheinsätze". Bei der Verarbeitung von REGIONAL (l)-Dateien muß man beachten, daß infolge der direkten Adressierung im Stammsatz selbst sein Ordnungsmerkmal nicht mitgespeichert ist, weil es durch die Speicheradresse verkörpert wird. Um in unserer Fallstudie die Programmlogik der Magnetbandverarbeitung beibehalten zu können, wird beim Updaten die Stammdatei sequentiell Satz für Satz gelesen und dabei untersucht, ob der unter einer Speicheradresse aufgerufene Satz einen Scheinsatz verkörpert. Ist dies der Fall, geht die Programmsteuerung zur nächsten Adresse der Stammdatei weiter und zwar so lange, bis sie einen aktiven Stammsatz gefunden hat. Seine Speicheradresse in Gestalt der Regionnummer wird festgehalten und in einer eigenen Speicherzelle (im nachfolgenden Programm als „KONTONUMMER" bezeichnet) aufbewahrt. Anschließend findet wieder der Vergleich mit dem Ordnungsmerkmal des zur Verarbeitung anstehenden Bewegungssatzes statt. Von diesem Punkt ab arbeitet die Programmsteuerung in der bekannten Weise weiter, indem aus dem Vergleich der Ordnungskriterien von Bewegungs- und Stammdatei die drei Teilfunktionen bewegte Stammsätze, nichtbewegte Stammsätze und Bewegungen ohne Stammsätze resultieren, so daß wir die Programmteile DATEIVERARBEITUNG und DATEISCHLIESSEN ohne Änderungen aus der Magnetbandlösung übernehmen können. Bei diesem sequentiellen Suchen von aktiven Sätzen erreicht die Programmsteuerung natürlich einmal das Ende der Stammdatei. Beim ersten Ver-
144
6. LITOS, eine neue Methode des Softwareentwurfs
such, einen Satz außerhalb der vorgegebenen Dateigröße zu lesen, spricht der Bedingungscode „56" an [43, S. 218], über den wirwie in der Magnetbandverarbeitung die Endfile-Bedingung der Stammdatei aktivieren ( E N D F I L E B E DINGUNGSTAMMDATENEINGABE = 'l'B). Als nächstes wäre wieder zu untersuchen, welche Stellen des Programmcodes von den Änderungen des Programmentwurfs betroffen werden. Da er auf dem Geheimnisprinzip beruht, wurden alle Eingabeprozesse in einer eigenen Datenkapsel „DATEIEROEFFNUNG_DATEIVERWALTUNG" zusammengefaßt. Dies bedeutet, daß primär sie von der Umstellung auf ein anderes peripheres Speichermittel betroffen ist; denn der operationale Teil soll über die Herkunft von Daten nichts wissen, um von dieser Information unabhängig arbeiten zu können. Um aber Daten in der Dateiform „REGIONAL (1)" verarbeiten zu können, muß der Eingabedatenkapsel das Ordnungsmerkmal „KONTONUMMER", das ihr bisher verborgen war, weil es für sequentielle Leseprozesse nicht benötigt wurde, mitgeteilt werden. Weiterhin bewirkt die Umstellung auf Magnetplattenspeicher, daß jetzt die Stammdatei der Eingabeseite („MEINGAB") identisch ist mit der Stammdatei der Ausgabe („MAUSGAB"), die bisher noch im operationalen Teil lag. Es ist zweckmäßig, nunmehr beide unter einem neuen Namen („STAMM") zusammenzufassen und in derselben Datenkapsel zu lesen und zu schreiben. Wir führen zu diesem Zweck in der Prozedur „DATEIEROEFFNUNG_DATEIVER WALTUNG" einen neuen Eingangspunkt ein („REWRITE"). Über ihn wird der auf den neuesten Stand gebrachte Stammsatz in die Stammdatei zurückgeschrieben. Mit diesen einleitenden Erläuterungen ist das Programm MAGNETPLAT TEN DATEIVERARBEITUNG STRUKTURIERT wieder selbsterklärend. Diese zweite größere Programmänderung bestätigt die Beobachtung, die wir schon beim Einbau der Sortierkontrolle gemacht haben, daß in top down strukturierten Softwaresystemen Programmodifikationen schnell und einfach durchzuführen sind. Aufgrund ihrer Modularität sind sie flexibel genug, um sich an neue Aufgabenstellungen anpassen zu können. Strukturierter Systementwurf ist eine notwendige, aber nicht hinreichende Bedingung, um eine Änderungsfreundlichkeit zu erreichen. Daneben muß man eine systematische Programmdokumentation verlangen, um die Programmlogik Außenstehenden leicht verständlich darzulegen, so daß sie in die Lage versetzt werden, die Auswirkungen von Programmänderungen beurteilen zu können. Die LITOS-Entwurfsmethode ist ein gutes Werkzeug für solche Aufgaben.
6.4 Zur Ànderungsfreundlichkeit von LITOS-Produkten
145
MAGNETPLATTEN_DATEIVERARBEITUNG_STRUKTURIERT: PROC OPTIONS
(MAIN);
/ * * * * * * * * * * * * * * * * * * VEREINBARUNGSSYSTEM
**************/
DCL DATEIEROEFFNUNG_DATEIVERWALTUNG
ENTRY,
BEWEGUNGSEINGABE
ENTRY,
STAMMSATZEINGABE
ENTRY,
REWRITE
ENTRY,
DRUCKROUTINE
ENTRY,
D RUCKAUFBE REITUN G_S TAMMDATEN
ENTRY,
DRUCKEN_BEWEGUNGSSATZ
ENTRY,
DRUCKEN_S UMMEN Z EILE
ENTRY,
DATEIVERARBEITUNG DATEISCHLIESSEN ENTRY; / * * * * * * * * * * * * * * * * * * * * STEUERUNGSTEIL * * * * * * * * * * * * * * * * * / CALL DATEIE ROEFFNUNG_DATEIVE RWALTUNG ; CALL DRUCKROUTINE; / * * * * * * * * * * * * * * * * * OPERATIONALES SYSTEM
**************/
CALL DATEIVERARBEITUNG_DATEISCHLIESSEN; END MAGNETPLATTEN_DATEIVERARBEITUNG_STRUKTURIERT; /*****************************************************/ / * * * * * * * * * HIERARCHISCHE STUFE 3
*********************/
DATEIE ROE FFNUN G_DATEIVE RWALTUN G : P ROC ; DCL LEINGAB FILE RECORD INPUT, / * LOCHKARTENEINGABE
*/
STAMM FILE RECORD KEYED ENV
(REGIONAL(1));
/ * DATEI STAMM ERSETZT MEINGAB UND MAUSGAB * / DCL BEWEGUNGSSATZ
CHAR
(80),
STAMMSATZ
CHAR
(18);
/ * FUNKTIONELLE DATENSAETZE DCL
*/
(ENDFILE_BEDINGUNG_STAMMDATEN_EINGABE, ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE)
DCL BANDSATZ LOCHKARTENSATZ
CHAR (18) CHAR
BIT(1);
DEFINED STAMM_SATZ,
(80);
DCL 1 STAMM_SATZ, 2 KONTROLLBYTE CHAR ( 1 ) , / * ZUR ERKENNUNG VON SCHEINSAETZEN
*/
146
6. LITOS, eine neue Methode des Softwareentwurfs . . .
2 REST DCL KONTONUMMER
CHAR (17); CHAR (6),
BYTE
BIT
ONCODE
BUILTIN;
DCL CREMENT
(8),
PIC'999999';
/* DIENT ZUM ZAEHLEN */ OPEN FILE (LEINGAB), FILE (STAMM) DIRECT UPDATE; RETURN ; BEWEGUNGSEINGABE : ENTRY (BEWEGUNGSSATZ, ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE); ON ENDFILE (LEINGAB) ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE = '1'B; READ FILE (LEINGAB) INTO
(LOCHKARTENSATZ);
IF ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE = '1'B THEN RETURN; BEWEGUNGSSATZ = LOCHKARTENSATZ; RETURN ; Z*****************************************************/ STAMMSATZEINGABE : ENTRY (STAMMSATZ, KONTONUMMER, ENDFILE_BE DIN GUN G_S TAMMDAT EN_EIN GABE) ; ON KEY (STAMM) BEGIN; IF ONCODE = 56 THEN ENDFILE_BEDINGUNG_STAMMDATEN_EINGABE = '1'B; END; CREMENT = KONTONUMMER; CREMENT = CREMENT + 1 ; DO UNTIL ( (BYTE ~I = (8) '1 'B) j (ENDFILE_ BEDINGUNG_STAMMDATEN_EINGABE = '1'B)); KONTONUMMER = CREMENT; READ FILE (STAMM) INTO (BANDSATZ) KEY (KONTONUMMER); BYTE
= UNSPEC (STAMM SATZ.KONTROLLBYTE);
6.4 Zur Änderungsfreundlichkeit von LITOS-Produkten CREMENT
147
= CREMENT + 1 ; END;
CREMENT
= CREMENT -
1;
KONTONUMMER = CREMENT; I F ENDFILE_BEDINGUNG_STAMMDATEN_EINGABE THEN STAMMSATZ
=
'1'B
RETURN;
= BANDSATZ ;
RETURN ; REWRITE
: ENTRY
(STAMMSATZ,
KONTONUMMER);
REWRITE FILE
(STAMM) FROM
(STAMMSATZ) KEY (KONTONUMMER);
RETURN ; END DAT EIE ROE F FNUN G_DAT EIVE RWALTUN G ; /*******************+******•**********•****************/ DRUCKROUTINE
: PROC;
/***** IDENTISCH MIT PROGRAMM MAGNETBAND_ DATEIVERARBEITUNG_STRUKTURIERT_VERSION_2 * * * * * * * / /******************************************************/ DATEIVERARBEITUNG_DATEISCHLIESSEN DCL
: PROC;
(ENDFILE_BEDINGUNG_STAMMDATEN_EINGABE, ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE) STATIC I N I T
/*********
LOKALE SATZVEREINBARUNGEN
BIT
(1)
('O'B);
******************/
/ * VEREINBARUNG BEWEGUNGSSATZ
*/
DCL 1 BEWEGUNGS_SATZ, 2 KONTONUMMER_BEWEGUNG 2 BELEGNUMMER_BEWEGUNG
CHAR(4),
2 DATUM_BEWEGUNG
CHAR(6),
2 BETRAG_BEWEGUNG
PIC 1 S 9 9 9 9 V 9 9 1 ,
2 TEXT
CHAR(56),
2 KARTENART
CHAR(1);
/*
VEREINBARUNG STAMMSATZ
DCL KONTONUMMER_STAMMSATZ /*
CHAR(6),
CHAR(6)
NICHT MEHR TEIL STAMM SATZ
*/
*/ INIT
('OOOOOO');
148
6. LITOS, eine neue Methode des Softwareentwurfs . . .
DCL 1 STAMM_SATZ, 2 KONTROLLBYTE
CHAR(1),
2 SATZART_STAMMSATZ
CHAR(1),
2 DAT UM_S TAMMS ATZ
CHAR(6),
2 SALDO_STAMMSATZ DCL BEWEGUNGSSATZ CHAR(80) STAMMSATZ
CHAR(18)
PIC'S7(9)V99'; DEFINED BEWEGUNGS_SATZ, DEFINED STAMM_SATZ;
CALL BEWEGUNGSEINGABE (BEWEGUNGSSATZ, ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE); CALL STAMMSATZEINGABE (STAMMSATZ,KONTONUMME R_STAMMSAT Z, ENDFILE_BE DINGUNG_STAMMDATEN_EINGABE) ; / * * * * * * * * * * * * * DATEIVERARBEITUNG
***********/
DO WHILE (ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE
= 'O'B &
ENDFILE_BEDINGUNG_STAMMDATEN_EINGABE IF
=
'O'B);
KONTONUMMER_BEWEGUNG = K ON T ON UMME R_S TAMMS ATZ THEN CALL BEWEGTE_STAMMSAETZE;
IF
(KONTONUMMER_BEWEGUNG > KONTONUMMER_STAMMSATZ) & (ENDFILE_BEDINGUNG_STAMMDATEN_EINGABE = ' O ' B )
THEN
CALL NICHTBEWEGTE_STAMMSAETZE; IF
(KONTON UMME R_BEWE GUN G < KONTONUMMER_STAMMSATZ) & (ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE
=
'O'B) THEN
CALL BEWEGUNGEN_OHNE_STAMMSAETZE; END; / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
/ * * * * * * * * * * * * * DATEISCHLIESSEN
*************/
I F ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE
=
'1'
THEN DO; DO WHILE
(ENDFILE_BEDINGUNG_STAMMDATEN_EINGABE =
CALL NICHTBEWEGTE_STAMMSAETZE; END;
'O'B);
6.4 Zur Änderungsfreundlichkeit von LITOS-Produkten
END; IF ENDFILE_BEDINGUNG_STAMMDATEN_EINGABE = '1'B THEN DO; DO WHILE
(ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE = ' 0 ' B) ;
CALL BEWEGUNGEN_OHNE_STAMMSAETZE; END; END; /*********** ENDE HIERARCH. STUFE 3 ***************** /*********** PROZEDUREN HIERARCH. STUFE 4 *********** BEWEGTE_STAMMSAETZE : PROC; CALL DRUCKAUFBEREITUNG_STAMMDATEN (KONTONUMMER_STAMMSATZ,SALDO_STAMMSATZ); CALL VE RARBEITEN_BEWEGUN GS DATEN (STAMM_SATZ,KONTONUMMER_STAMMSATZ); /* GRUPPENWECHSEL */ CALL DRUCKEN_S UMMEN ZEILE
(SALDO_STAMMSATZ)
CALL REWRITE (STAMMSATZ,KONTONUMMER_STAMMSATZ); /* ENDE GRUPPENWECHSEL */ CALL STAMMSATZEINGABE (STAMMSATZ,KONTONUMMER_STAMMSATZ, ENDFILE_BEDINGUNG_STAMMDATEN_EINGABE); END BEWEGTE_STAMMSAETZE; /****************************************************
NICHTBEWEGTE_STAMMSAETZE : PROC; DCL NULL_BETRAG PIC'S9999V99'; NULL_BETRAG = 0; CALL DRUCKAUFBEREITUNG_STAMMDATEN (KONTONUMMER_STAMMSATZ,SALDO_STAMMSATZ) CALL DRUCKEN_BEWE GUNGS SAT Z (' DATUM_STAMMSATZ,NULL_BETRAG); CALL STAMMSATZEINGABE (STAMMSATZ,KONTONUMMER_STAMMSATZ,
150
6. LITOS, eine neue Methode des Softwareentwurfs . . . ENDFILE_BEDINGUNG_STAMMDATEN_EINGABE);
END NICHTBEWEGTE_STAMMSAETZE; y******************************************************/ BEWEGUNGEN_OHNE_STAMMSAETZE
: PROC;
DCL 1 PUFFER_STAMMSATZ LIKE STAMM_SATZ; DCL PUFFER_KONTONUMMER CHAR(6), PUFFER
CHAR(18)
DEFINED PUFFER_ STAMMSATZ ;
PUFFER_STAMMSATZ.SALDO_STAMMSATZ = 0 ; CALL DRUCKAUFBEREITUNG_STAMMDATEN (KONTONUMMER_BEWEGUNG, PUFFER_STAMMSATZ.SALDO_STAMMSATZ); PUFFE R__K0NT0NUMMER = KONTONUMMER_BEWEGUNG; CALL VE RARBEITEN_BEWEGUNGS DATEN (PUFFER_STAMMSATZ,PUFFER_K0NT0NUMMER); CALL DRUCKEN_SUMMENZEILE (PUFFER_STAMMSATZ.SALDO_STAMMSATZ); CALL REWRITE
(PUFFER,PUFFER_K0NT0NUMMER);
END BEWEGUNGEN_OHNE_STAMMSAETZE; / * * * * * * * * * * * * * ENDE PROZ. HIERARCH. STUFE 4 / * * * * * * * * * * * * * HIERARCHISCHE STUFE 5 VERARBEITEN_BEWEGUNGSDATEN
***********/
******************/
: PROC
(STAMM_SATZ,KONTONUMMER_STAMMSATZ); DCL 1 STAMM_SATZ, 2 KONTROLLBYTE
CHAR
(1),
2 SATZART_STAMMSATZ
CHAR
(1),
2 DATUM_STAMMSATZ
CHAR
(6),
2 SALDO_STAMMSATZ
PIC'S9999999V99';
DCL KONTONUMMER_STAMMSATZ
CHAR
(6);
DO WHILE (KONTONUMMER_BEWEGUNG = K0NT0NUMMER_STAMMSATZ & ENDFILE_BEDINGUNG_BEWEGUNGSDATEN _EINGABE = / * UPDATEN STAMMSATZ
*/
SAT ZART_S TAMMSAT Z = KARTENART; DATUM STAMMSATZ
= DATUM BEWEGUNG;
1
01B);
6.4 Zur Änderungsfreundlichkeit von LITOS-Produkten SALDO_STAMMSATZ
151
= SALDO_STAMMSATZ + BETRAG_BEWEGUNG;
/ * DRUCKEN BEWEGUNGSSATZ * / CALL DRUCKEN_BEWEGUNGSSATZ (BELEGNUMMER_BEWEGUNG,DATUM_BEWEGUNG, BETRAG_BEWEGUNG); / * LESEN BEWEGUNGSSATZ * / CALL BEWEGUNGSEINGABE (BEWEGUNGSSATZ, ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE); END; END VERARBEITEN_BEWEGUNGSDATEN; /******************************************************/ END DATEIVERARBEITUNG_DATEISCHLIESSEN; / * * * * * * * * * * * PROGRAMMENDE
*****************************/
7. Weiterführende Untersuchungen zur Software-Konstruktionslehre
7.1 Anwendung der LITOS-Methode auf allgemeine Entwurfsaufgaben Wie die SADT-Methode so erheben auch wir mit der LITOS-Methode den Anspruch, ein Werkzeug entwickelt zu haben, das nicht nur dem Softwaredesigner dient, sondern das eine allgemeine Bedeutung für den Systementwurf hat. Diese Behauptung soll in diesem Kapitel an einer exemplarischen Aufgabe verifiziert werden. Das Kernproblem, um das es im Grunde genommen in der LITOS-Methode geht, ist der Top-down Entwurf eines Systems von Funktionseinheiten, die an einer gemeinsamen Aufgabe arbeiten und dadurch eine in sich von ihrer Umwelt abgeschlossene Ganzheit bilden. Funktionseinheiten sind in diesem allgemeinen Kontext „schwarze Kästen", gekennzeichnet durch die Teilaufgaben, die sie im Systemzusammenhang ausführen. Ihr innerer Aufbau ist für das Verständnis ihres Verhaltens irrelevant. Diese Systemelemente führen wiederkehrende gleichartige Arbeiten aus. Hingegen kann die Systemaufgabe einmalig gestellt sein. Um sie zu lösen, müssen zwischen den Elementen eines solchen zweckorientierten Systems interdependente Beziehungen bestehen, die durch einen menschlichen Gestaltungsprozeß und nicht zufällig gegeben sind. Sie lassen sich als ein Aufgabenfluß darstellen, der das Ziel hat, eine Systemleistung aufzubringen, und der deshalb gerichtet ist. Dieser Fluß besteht aus zwei Schichten. Abstrakt gesehen kann man sie als Arbeitsobjekte bezeichnen, an denen die Systemelemente Veränderungen vornehmen und Steuerungsobjekte, die die Tätigkeiten der einzelnen Systemelemente koordinieren und sie damit in ein größeres Ganzes integrieren [41, S. 126]. Als Beispiel für den Wert eines solchen allgemeinen Systemdenkens in LITOS wollen wir ein Mikroprogramm entwerfen. Elemente eines Mikroprogrammsystems sind Hardwareeinheiten oder Softwarebausteine, wobei den Systemplaner im Grunde genommen nur die Teilfunktion eines solchen Elements interessieren sollte, aber nicht die Frage, ob es in Hardware oder Software realisiert wird. In der Praxis ist allerdings häufig mit dem Begriff Mikroprogrammierung die Vorstellung verbunden, einzelne Bits manipulieren zu müssen, so daß ein Mikroprogramm bottom up entsteht. Die Aufgabenstellung, von der wir ausgehen, ist die Umsetzung der dezimalen Festpunktaddition in einem klassischen von NeumannRechner in ein Mikroprogramm. Um diese Aufgabe aber interessanter zu gestalten, sollen, abweichend von der klassischen Betriebsweise dieses Rechners, dem seriellen Betrieb, soweit als möglich Teilfunktionen im Mikroprogramm
7.1 Anwendung der LITOS-Methode
153
simultan ablaufen. Wir sind uns bewußt, daß diese Annahme auf die Struktur des Rechenwerkes Rückwirkungen hat. Verbal dargestellt läßt sich damit die Aufgabenstellung in folgender Weise detaillieren. Die beiden Operanden „ X " und „ Y " der Festpunktaddition sind aus dem Hauptspeicher (Magnetkernspeicher, stellenorganisiert ausgelegt) in den Akkumulator und das 1 -Wort-Register des Rechenwerks, das die Schnittstelle zum Hauptspeicher darstellt, zu übertragen. Anschließend ist die Summe zu bilden, die den Operanden „ X " im Hauptspeicher überschreibt. Bedingt durch die Hauptspeicherorganisation ist die Wortlänge der Operanden variabel. Sie ergibt sich aus der Adresse der höchsten Speicherstelle eines Wortes und einer Wortendemarkierung in der niedrigsten Stelle (Wortmarke). Abbildung 25 zeigt den Feinentwurf des gesamten Mikroprogramms für die Ausführung der Festpunktaddition und Abbildung 26 die Detaillierung der Teilfunktion „Laden 1-Wort-Register". Wir haben das Mikroprogramm als asynchronen Operationsablauf entworfen. Es ist ein leichtes, nachträglich dieses Programm mit Hilfe der Elemente der Simultanverarbeitung (s. Abbildung 15) für eine synchrone Durchfuhrung zu erweitern. Wie Abbildung 25 zeigt, besteht der Entwurf des Mikroprogramms aus zwei OPERATIONSCODE A
ENDEMELDUNG >
VON LINKS NACH RECHTS ZUSAMMEN MIT
STELLEN-
VERSCHIEBEN NACH
RECHTS
Abb. 25 Feinentwurf Mikroprogramm für die asynchrone Ausfuhrung der Festpunktaddition in stellenorganisierter DV-Anlage mit Simultanisierung der Teilfunktionen.
7. Weiterführende Untersuchungen zur Software-Konstruktionslehre
154
LADEN*
1-WoRT
DO W H I L E
(WH T
-I O B E R L A U F
'l'B
S
I-WORT-REGISTER)
REGISTER*
t"
WLEDER-
EINSCHR,
DO UNTIL (PP = 'O'B I X'.Y' 1 " 1 0 ) UMCODIEREN
^ > 1
HAUPTSP,
^ ^ J
ADRESSE
LESEN* 1 STFI I F
#
HAUPTSP.
x: ELSE I =
X.Y RÜCK C O -
10?
ADDITION
DI EREN
HAUPTSP,
VON
1
_ OBERTRA-
ABSPAL-
GEN
T E N WM ÜBERTRAGEN 1
HPTSTELLE
-•REGENE-
PARITÄTS-
FEHLER-
PRÜFUNG
MELDUNG
1
ZEICHEN --»1-WORTREGISTER
RATIONSREG.
O THEN SIZE_VARIABLE
= SALDO_STAMMSATZ +
10000000.00
-
BETRAG_BEWEGUNG; ELSE
SIZE_VARIABLE
= SALDO_STAMMSATZ -
10000000.00
-
BETRAG_BEWEGUNG;
CALL DRUCKEN_ZWISCHENSUMME
(SIZE_VARIABLE);
SAL D0_S TAMMS AT Z = SIZE__VARIABLE ; WRITE F I L E
(MAUSGAB)
FROM
(STAMMSATZ);
SALDO_STAMMSATZ = BETRAG_BEWEGUNG; SIZE_BEDINGUNG
=
'O'B;
END; /********* CALL
ENDE LOKALE DO WHILE-SCHLEIFE
**************/
DRUCKEN_BEWEGUNGSSATZ (BELEGNUMME R_BEWEGUNG, BETRAG_BEWEGUNG);
CALL BEWEGUNGSEINGABE
DATUM_BEWEGUNG,
162
7. Weiterführende Untersuchungen zur Software-Konstruktionslehre (BEWEGUNGSSATZ, ENDFILE_BEDINGUNG_BEWEGUNGSDATEN_EINGABE); END;
END VERARBEITEN_BEWEGUNGSDATEN; /•fr****************************************************/ END
DATEIVERARBEITUNG_DATEISCHLIESSEN;
Die lokale Bedeutung der in der Prozedur,,VERARBEITEN_BEWEGUNGS DATEN" 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 „SIZE_VARIABLE" 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 18) 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
163
Im Sinne des Geheimnisprinzips könnte man aber auch Fehlerschleifen in einer eigenen Datenkapsel zusammenfassen.
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 Ver fugung zu stellen, ohne daß schädliche gegenseitige Beeinflussungen entstehen. Die Zeitpunkte, zu denen Betriebsmittel angefordert werden, sind unbekannt.
164
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.2.2 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 fiir 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 5 und 6 der Teilfunktion „bewegte Stammsätze" (s. Abbildung 18) 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 fuhren 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. 81). Deshalb ist eine Simultanisierung dieser beiden
7.4 Strukturierter Entwurf simultaner Prozesse
165
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 27). 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 „STAMM_SATZ" ein explizit übergebener Parameter ist (s. Programm „MAGNETBAND_DATEIVERARBEITUNG_STRUK TURIERT_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
166
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/l-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 (2)
lautet deshalb:
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_1(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 Eingabedaten 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
7.4 Strukturierter Entwurf simultaner Prozesse
167
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 Simultartisierbarkeit 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.
Ii
STAMMSATZ BEWEGUNGSSATZ
BEWEGTE STAMMSÄTZE
ZEILF,KDNROTMTR_STAMSATZ.
ü
A
BEWEGUNGSSATZ
SALDCLSTAMSATZ
I I STAMMSATZ
STAMMSATZ
:
LESEN STAMMSATZ
BEWEGUNGSDATEN
GRUPPEN-
SCHLEIFE
WECHSEL STAMMSATZ ZEILE
BEWEGUNGSS. ZEILE
DRUCKEN
I L jLL 1 I
BEWEGUNGSS.
I
1 "
I
STAMMSATZ
I
I
BEWEGUNGSSATZ
I
I
BEWEGUNGSSATZ
UPDATEN
LESEN
STAMMSATZ
BEWEGUNGSSATZ
M
"(l I I
STAMM
I
I I
BEWEGUNGSDATEN SCHLEIFE
Abb. 27 Simultanisierung der Teilfunktion „Bewegte Stammsätze" der Fallstudie.
168
7. Weiterführende Untersuchungen zur Software-Konstruktionslehre
Unter Anwendung dieser drei Simultanisierungsregeln haben wir den Feinentwurf der Abbildung 18 in eine simultanisierte Lösung überführt (Abbildung 27). Dabei ist zu beachten, daß in PL/1 im Normalfall 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 „BEWEGTE STAMMSAETZE". In diesem Beispiel wurden sämtliche Simultanisierungsmöglichkeiten ausgenutzt. BEWEGTE_STAMMSAETZE:/*** SIMULTANE PROGRAMMVERARBEITUNG * * * / PROC; CALL DRUCKAUFBEREITUNG_STAMMDATEN
((KONTONUMME R_
STAMMSATZ), (SALDO_STAMMSATZ)) EVENT (DRUCKAUFBEREITUNG); CALL VERARBEITEN_BEWEGUNGSDATEN
((STAMM_SATZ))
EVENT (.BEWEGUNGSSCHLEIFE) ; CALL STAMMSATZEINGABE (STAMMSATZ, ENDFILE_BEDINGUNG_STAMMDATEN_EINGABE)
EVENT
(LE S EN_S TAMMS AT Z) ; WAIT (LESEN_STAMMSATZ,BEWEGUNGSSCHLEIFE, DRUCK AUFBEREITUNG); END BEWEGTE_STAMMSAETZE; / • • • • A * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
VERARBEITEN_BEWEGUNGSDATEN: PROC (STAMM_SATZ); DCL 1 STAMM_SATZ, 2 S AT Z A RT_S TAMMS ATZ
CHAR
(1) ,
2 KONTONUMMER_STAMMSATZ CHAR
(6),
2 DATUM_STAMMSATZ
CHAR
(6) ,
2 SALDO_STAMMSATZ
PIC
'S(7)9V99';
DO WHILE (KONTONUMMER_BEWEGUNG = KON TON UMME R_ STAMMSATZ & ENDFILE_BEDINGUNG_ BEWEGUNGSDATEN_EINGABE = CALL UPDATEN_STAMMSATZ EVENT (UPDATEN); WAIT (DRUCKAUFBEREITUNG);
'O'B);
((BEWEGUNGS_SATZ))
7.4 Strukturierter Entwurf simultaner Prozesse
169
CALL DRUCKEN_BEWEGUNGSSATZ
((BELEGNUMMER_
BEWEGUNG), (DATUM_BEWEGUNG), EVENT
(BETRAG_BEWEGUNG))
(DRUCKEN);
CALL BEWEGUNGSEINGABE (BEWEGUNGSSATZ,
ENDFILE_BEDINGUNG_
BEWEGUNGSDATEN_EINGABE) EVENT
(LESEN_BEWEGUNGSSATZ);
WAIT (LESEN_BEWEGUNGSSATZ,
UPDATEN,
DRUCKEN); END; CALL DRUCKEN_SUMMENZEILE
(SALDO_STAMMSATZ);
WRITE FILE (MAUSGAB) FROM (STAMM_SATZ); /******************************************************/ / * * * * * * * * * * * * * * * NEUE PROZEDUR
************************/
UP DATEN_STAMMSAT Z: PROC
(BEWEGUNGS_SATZ);
DCL 1 BEWEGUNGS SATZ, 2 KONTONUMMER_BEWEGUNG
CHAR
(6),
2 BE LE GNUMME R_BEWEGUN G
CHAR
(4),
2 DATUM_BEWEGUNG
CHAR ( 6 ) ,
2 BETRAG_BEWEGUNG
PIC
2 TEXT
CHAR(56),
2 KARTENART
CHAR
1
S9999V991, (1);
SATZART_STAMMSATZ = KARTENART; DATUM_STAMMSATZ
= DATUM_BEWEGUNG;
SALDO_S TAMMS AT Z
= SALDO_STAMMSATZ + BETRAG BEWEGUNG;
END UPDATEN_STAMMSATZ ; END VERARBEITEN_BEWEGUNGSDATEN ; /fr*****************************************************/
8. Ausblick
Die Diskussion in der Fachwelt über die Methoden des strukturierten Softwareentwurfs und der strukturierten Programmierung ist noch in vollem Gange. Drei Entwicklungsrichtungen zeichnen sich ab: — — —
der Programmentwurf über Bildschirmterminals ein datenbankorientierter Programmentwurf 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, um wie im Hardwareentwurf zu einer computerunterstützten Konstruktionsmethodik zu kommen (CAS: Computer aided Softwaredesign). Dadurch könnte die Produktivität in einem Programmierprojekt gesteigert werden, indem der Schreib- und Zeichenaufwand des Programm de signers reduziert wird und er unmittelbaren Zugriff zu einer maschinell gespeicherten Programmdokumentation erhält. In der betrieblichen Datenverarbeitung nimmt der Einsatz von Datenbanken immer stärker zu. Es ist deshalb naheliegend, das Konzept der Datenkapsel, die Daten verwaltet, in der Weise zu erweitern, daß für diese Aufgabe eine Datenbank eingesetzt 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.
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 « e l e m e n t a u s d r u c k » [UNTIL ()] | UNTIL () [WHILE («elementausdruck»]; I D O ^ l a u f v a r i a b l e > = ^ S p e z i f i k a t i o n ^ [ ^ S p e z i f i k a t i o n ^ ] . . .; ^Spezifikation^» : : =
[TO^elementausdruck^BY^elementausdruck}»]; | BY [TO'tielementausdruck^] | REPEAT ] {[WHILE « e l e m e n t a u s d r u c k » ] • [UNTIL « e l e m e n t a u s d r u c k » ] } A2 LEA VE-Anweisung •£leaveanweisung > : : = LEAVE []; A3 SELECT-Gruppe : : = SELECT [ « a u s d r u c k » ] ; {[]' WHEN ( ^ a u s d r u c k ^ [,"Pausdruck>]. . .) ^ausführbarer prozedurteil^» 2 } . . . [{OTHERWISE | OTHER} ^ausführbarer prozedurteil>] 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 OTHERWISE-Anweisung erreicht ist. Sie wird ausgeführt, wenn kein Vergleich positiv beantwortet werden konnte. 1 2
s. [44, S. 19] s. [44, S. 18]
172
Anhang
Falls ein Programmierer die SELECT-Anweisung ohne den Zusatz ausdruckt» 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
Andler, K.: Die wirtschaftliche Auftragsmenge für Fertigung und Lager. In: Das Industrieblatt 51 (1951). Baker, F. T., H. D. Mills: Chief Programmer Teams. In: Datamation 19(1973). 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. Boehm, B. W.: Software Design and Structuring. In: E. Horowitz (Hrsg.), Practical Strategies for Developing Large Software Systems, Reading 1975. Boehm, B. W.: Software and Its Impact: A Quantitative Assessment. In: Datamation 19 (1973). Böhm, C., G. Jacopini: Flow Diagrams, turing machines and languages with only two formation rules. In: Communications of the ACM 9 (1966). Brown, R. R.: The Techniques and Practice of Structured Design a la Constantine. In: Structured Design, Infotech State of the Art Conference, London 1977. Chroust, G., N. Flödl, F. Klein: Für und wider eine ÖNORM für normierte Programmierung: In: ÖNORM 5 (1975). 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-
174
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. [17a] Elshoff, J. L.: The Influence of Structured Programming on PL/1 Program Profiles. In: IEEE Transactions on Software Engineering Vol. SE—3, No. 5 (1977), S. 364 ff. [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. [21a] Hesse, W.: Methoden und Werkzeuge zur Software-Entwicklung: Einordnung und Überblick. In: Werkzeuge der Programmiertechnik, InformatikFachberichte Nr. 43, S. 113 ff. Berlin Heidelberg-New York 1981. [21b] Horning, J. J.: Some Desirable Properties of Data Abstraction Facilities. In: SIGPLAN Notices 11 (1976) S. 60 ff. [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 1, 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).
Literaturverzeichnis
175
[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). [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., B. Shneiderman: 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. Rasami, 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. [39a] Rechenberg, P.: Daten- und Programmkontrollstrukturen. Technischer Bericht 1/77. Lehrkanzel für Informatik (Software), Universität Linz 1977. [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. [44a] Schulz, A.: Software-Entwurfsmethoden, Ein Vergleich der Programminversion nach Jackson mit der aufgabenorientierten Programm-Modulari-
176
Literaturverzeichnis sierung nach Parnas (Geheimnisprinzip). In: Angewandte Informatik 11 (1980) S. 371 ff.
[44b] Schulz, A.: Vergleich von Software-Entwurfsmethoden, IKD-Kongreß 1980, Tagungsband, S. 177 ff. [45] Schnupp, P., C. 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. [50a] White, J.R., R.K. Anderson: Supporting the Structured Development of Complex PL/I Software Systems. In: Software-Practice and Experience, 7 (1977) S. 279 ff. [51] Wirth, N.: Program Development by Stepwise Refinement. In: Communications of the ACM 14 (1971). [52] Wulf, W„ M. Shaw: Global Variable Considered Harmful. In: SIGPLAN Notices 8 (1973). [53] Yourdon, E., L.L. Constantine: Structured Design. 2. Aufl. New York 1976.
Sach- und Personenregister Ablauforganisation 96, 101, 155, 170 Abschlußtest 9, 156 Abstraktionsstufe 14, 5 1 f f „ 68, 89, 115 Adressierung direkte 143 Änderungsfreundlichkeit 11, 12, 13,49, 54, 135,137ff., 144 Anderson 92 Andler 82 Anweisung bedinge 30, 38 Arbeitsmittel 5 0 , 5 4 , 9 1 Arbeitsprozeß 54, 57, 90, 94, 101 Architektur (Programm) 49, 101 Aufbauorganisation 96, 101, 104, 170 Aufgabe 12, 13, 50, 54, 55, 56, 78, 89, 94, 100, 101, 104, 152, 157 Aufgabenanalyse 50, 54, 78, 94, 99, 103 Aufgabendekomposition 54 Aufgabenfluß 152 Aufgabenmodularisierung 54 Aufgabenstellung 49, 50, 54, 78, 94, 101, 103, 137, 144, 152, 153, 156, 163 Aufspaltung 104, 164 Auftrennung von Knoten 40 Aufnahmebedingung 31, 40, 41, 48, 158 - , globale 158, 159 - , lokale 158, 159 Back-up-Rrogrammiererfunktion 157 Baker 157 Bauelemente 13, 35ff., 40, 108 Benutzerfreundlichkeit 12, 27, 49, 135, 136 Benutzerschnittstelle 12 Betriebsmittel 11, 13, 54, 163, 164 Betriebssystem 163, 164 bewegte Stammsätze 1 6 , 1 9 , 2 0 , 2 1 , 4 1 , 47, 51, 52, 60, 62, 98, 102, 103, 107, 108, 109, 110, 113, 114, 117, 122, 132, 133, 143, 148, 149, 164, 167, 168 Bewegungen ohne Stammsätze 16, 17, 19, 20, 21, 41, 47, 48, 51, 52, 60, 64, 87, 98, 102, 104, 108, 109, 110, 113, 114, 117, 122, 123, 126, 133, 134, 138, 143, 148, 150 Bewegungsdatei 15, 16, 19, 20, 21, 28, 41, 42, 48, 61, 66, 90, 103, 109, 111, 114, 116, 117, 124, 138, 139, 142, 143 Bildschirmterminal 125, 170 Bindung, funktionelle 80, 81, 82 - , kollaterale 82, 87 - , kommunikative 80, 81 - , logische 80, 81, 82 prozedurale 80
- , sequentielle 80, 81, 82, 87, 108, 164 - , zeitliche 80, 82 - , zufällige 80 Block 30, 31,41, 158, 159 Blockbegriff 30 BNF-Notation 98 Böhm 28 Bottom-up-Ansatz 55 Bottom-up-Struktur 49 CAD-System 99 CAS 170 CALL-Anweisung 48, 79, 112, 115, 166 Call by name 165 Call by reference 79, 165, 166 Call by value 79, 165, 166, 167 CASE-Anweisung 3 0 , 6 8 , 1 7 1 CASE-Konstruktion 35, 37, 106 CASE-Struktur (Pseudocode) 98 Chefprogrammiererfunktion 157 Chefprogrammiererteam 14, 156, 157 Chu 100 Codierung 98, 99, 115, 135, 157 Common-Attribut 79 Composite-Design 78 Constantine 72, 78, 79, 80, 81, 82, 83, 84 8 6 , 8 7 , 8 9 , 9 2 , 9 4 , 9 6 , 108,164 Constantine-Methode 78ff., 89, 94, 96, 98, 99,101 Darstellung des Programmentwurfs 16, 56, 66, 67, 68, 72, 84, 86, 87, 88, 89, 91, 96, 97, 104, 105, 106 Data-Dictionary 136 Dateiende 3 1 , 4 1 , 4 8 , 1 2 4 Dateiendearbeiten 48, 124 Dateiendebedingung 1 6 , 1 9 , 2 1 , 4 8 Dateieröffnung 51, 61, 97, 102, 103, 112, 117, 118, 119, 124, 127, 128, 138, 144, 145 Dateiorganisation, gestreut gespeichert 143 - , indexsequentielle 142, 143 - , sequentielle 67, 95, 103 Dateischließen 16, 51, 52, 60, 102, 103, 109, 111, 114, 117, 122, 127, 131, 139, 143, 145, 147, 148 Dateiverarbeitung 19, 41, 51 ff., 90, 98, lOlff., 12 7 ff., 139, 143, 144 ff. - , sequentielle 15, 59, 69, 72, 95, 142 Dateiverwaltung 117, 118, 127, 128, 138, 144,145 Daten abstrakte 92 lokale 92
178 Datenbank 92,170 Datenfluß 21, 58, 76, 86, 87, 89, 96, 101, 105,106,109,136,156 Datenflußgraph 81,87 Datenflußkopplung 79 Datenkapsel 92, 93, 102, 103, 108, 109, 112, 135, 137, 139, 144, 163, 170 Datenstruktur 67, 68, 69, 70, 71, 72, 75, 78,87,89,92,94,96 Datenverwaltung 92, 102,103, 138, 144 Datenverwaltungsmodul 92 Datenverwaltungssystem 93 Deadlockproblem 79 Detaildiagramm (HIPO) 59 Dialogverkehr 125, 170 Dijkstra 2 8 , 2 9 , 3 0 , 3 1 , 5 0 DO-Gruppe 35 DO REPEAT-Anweisung 32, 171 DO-REPEAT-Schleife 31 DO UNTIL-Anweisung 32,171 DO UNTIL-Schleife 31, 32, 37, 106 DO WHILE-Anweisung 28, 30, 31, 32, 48, 171 DO WHILE-Schleife 28, 31, 32, 33, 36, 38, 4 0 , 4 1 , 4 2 , 8 0 , 106, 109 DO WHILE-Struktur (Pseudocode) 98 Dokumentation 11, 59, 96, 97, 112, 135, 157, 158 Dokumentationsverfahren 5 7 , 9 5 , 9 7 Eingangsstelle, sekundäre 30 ELSE-Ausgang 38 ELSE-Zweig 40 Elshoff 30 Endfüe-Arbeiten 109 Endfile-Bedingung 4 1 , 4 2 , 114, 117, 144 Endfile-Maßnahme 109, 114 Endlosschleife 3 3 , 3 5 , 3 7 , 5 1 , 1 0 6 Endkontonummer fiktive 16 Entwicklungskosten 9 , 5 3 , 8 3 Entwurfsfehler 9 , 1 1 2 Entwurfsmethodik 9 , 5 9 , 8 4 , 8 7 , 9 6 Entwurfsphase 49, 50, 57, 67, 76, 101, 105,108,112,115,136,156,157,164 Entwurfssprozeß 10, 67, 72,101, 103, 157 Entwurfssprache kompilierbare 115 Entwurfsstrategien 49 ff. Entwurfsverfahren 54, 57, 87, 95 External-Attribut 79 Fallunterscheidung 30 Fehlerdichte 28 Fehlerfreiheit 9 , 1 2 , 1 3 , 4 9 , 1 0 0 Fehlerrate 28 Fehlersuche 53 Feinentwurf 57, 59, 62, 63, 64, 65, 66, 86, 98, 99, 101, 104ff., 112,127, 137, 142, 153, 154,156,168
Sach- u n d Personenregister Festpunktaddition 152,153, 154,155 Festpunktmultiplikation 155 Floyd 3 2 , 4 8 , 1 5 8 FORTRAN 29, 30 Funktion 12, 50, 51, 52, 53, 57, 59, 75, 80, 81, 82, 87, 90, 91, 101,109, 164, 165,166 Funktionseinheit 19, 89, 91, 101, 104, 105, 107, 108,109, HO, 111, 114, 139, 152, 154, 155, 156, 164, 165, 166, 167, 168 Funktionsplan 1 0 5 , 1 0 9 , 1 1 1 , 1 5 6 Geheimnisprinzip 53, 92, 93, 103, 127, 136, 138, 139, 144, 163 globale Variable 79, 9 3 , 1 3 7 , 1 6 3 GOTO-Anweisung 14, 16, 27, 28, 29, 30, 31, 35, 38, 39,40, 4 1 , 4 8 , 79, 95,136 Graph, gerichteter 40 Grobentwurf 49, 57, 59, 60, 87, 89, 99, lOlff., 127, 137, 142, 156 Gruppenwechsel 16, 19, 20,41, 95,108,138 Gruppen wechselarbeiten 19, 2 0 , 5 2 , 5 3 , 6 2 , 6 4 , 6 5 , 9 5 , 109, 117, 138, 165 Gruppenwechselprozeß 19,41, 108, 109, 111, 125, 126, 138, 164 Hansen 163,164 Hauptsteuerleiste 136, 137, 163 hierarchische Gliederung 12, 96, 154 HIPO-Diagramm 58, 59, 60,62, 63, 64, 65, 66 HIPO-Technik 56ff., 72, 78, 86, 94, 96, 98, 99, 101,106 Horning 92 IF THEN ELSE-Anweisung 28, 29, 30, 35, 3 6 , 3 8 , 4 0 , 4 1 , 4 8 , 6 8 , 79 IF THEN ELSE-Kontruktion 106 IF THEN ELSE-Struktur (Pseudocode) 97 IF THEN GOTO-Konstruktion 29 Input-Section 57, 58 IPT-Methode 156 Iteration 2 8 , 6 7 , 6 8 , 6 9 , 72, 9 1 , 1 0 4 , 1 0 6 Iterationsbedingung 28, 31 Iterationsprozeß 32 Jackson 48, 67ff., 72, 84, 92, 94, 98 Jackson-Methode 67ff., 78, 89, 92, 94, 96, 99, 106 Jacopini 28 Knuth 3 0 , 3 1 , 3 2 , 4 0 , 4 8 , 112 Kohäsion 7 9 , 8 1 , 8 2 , 8 3 Kohäsionsstufen 80, 82 Komplexitätsgrad 83 Kontrollspanne 83
Sach- u n d Personenregister
179
Korrektheit 56, 98,100,112,115 Kosiol 54 Kosten 12, 13 Krämer 54 kritische Region 164
ON-Bedingung 41 Organisationslehre betriebswirtschaftliche 54,103 Organisationsschaubild 21,101, 105 OTHERWISE-Anweisung 35, 171, 172 Output-Section 57,58
Langers 15, 142 LCP-Methode 72 ff. LEAVE-Anweisung 31, 32, 33, 34, 35, 37, 171 Lesbarkeit 12,135 Liste 3 1 , 3 3 , 3 4 , 1 1 6 , 1 2 4 Listenende 33, 34, 124 Listenkopf 33, 34, 124 Listenverarbeitung 31, 116 LITOS-Darstellungsmethode 104 ff. LITOS-Methode 76, 100ff., 152ff., 164
Parnas 54,92 PL/1-Optimizing Compiler 10, 31, 171 Planungsphase 50 Platzhalter 115,125 Preprozessor 116 Process-Section 5 7 , 5 8 , 5 9 Produktivität (Programmierung) 53, 155, 157, 158, 170 Programm, strukturiertes 2 9 , 3 1 , 4 0 Programmablaufplan 19, 21, 27, 28, 29, 36, 38,39,40,58, 73,75,96,97 Programmänderung 53,103, 137ff. Programmbibliothek 157, 158 Programmdokumentation 10, 11, 12, 55, 56, 59, 112, 137, 138, 139, 144, 156, 170 Programmebene 50 Programmende 41, 103 Programmentwurf 9, 11, 16, 29, 56, 70, 78, 97, 125, 127, 135, 170 - , datenbankorientiert 92,170 Programmentwurfssprache 97,98,100 Programmierfehler 9, 79 Programmierkosten 9 Programmierphase 112,156 Programmierprojekt 112, 156 ff. Programmierstil 29,49, 50, 157 Programmierteam 49, 156,157 Programmiertechnologie 10 Programmierung 9, 12, 21, 29,49, 78, 112, 127, 135, 136, 137, 156, 157, 158 - , herkömmliche 17,19, 27, 28ff., 40 Programminversion 6 8 , 7 1 , 7 2 Programmlänge 31,108 - , dynamische 16, 100 - , statische 16,100 Programmlogik 67, 73, 75, 112ff., 138, 142, 143, 144, 156, 170 Programmodul 13,49, 54, 56, 69, 78, 82, 87, 92, 94, 96, 108, 136, 157, 158 Programmodularisierung 54, 78 Programmnukleus 50 Programmschalter 21, 136 - , binärer 41,48 Programmschleife 29,31,32,39, 68,158ff. - , globale 158ff. - , iterative 28, 32 - , lokale 158ff. Programmspezifikation 12 Programmsteuerung 30, 41, 93, 112ff., 143
Magnetband-Dateiverarbeitung 15ff., 42ff., 102 ff., 117ff., 127 ff., 138ff„ 140ff., 143,159 ff. Magnetplatten-Dateiverarbeitung 15,142, 143, 144, 145 ff. Markenkonstante 35,171 Markenvariable 30,33 McGowan 95 Mensch-Maschine-Dialog 103 Methodenreservior 98 Methodenvergleich 94 ff. Mikroprogramm 15 2 ff. Mikroprogrammierung 15 2 ff. Mills 157 Modul 12, 13,49, 53, 54,69, 78, 79, 80, 8 1 , 8 2 , 8 3 , 8 4 , 8 5 , 8 6 , 87,92 Modularisierung 13, 20,41, 82, 105 Modulbegriff 87 Modulgröße 7 9 , 8 2 , 8 3 - , optimale 79, 82, 83 Modulkopplung 7 8 , 7 9 , 8 3 , 9 2 Monitor 164 Multitasking-Funktion 16, 163 Nachfolger, dynamische 41 Nassi-Shneiderman 97 nichtbewegte Stammsätze 16, 17, 19, 20, 21, 4 1 , 4 2 , 4 4 , 47,48, 51, 52, 61, 63, 98, 102, 104, 108, 109, 110, 111, 113, 114, 117, 122, 123, 126, 132, 133, 142, 143, 148, 149 Node Splitting 40 normierte Programmierung 15, 95, 100, 137 Nullprozedur 48 Österle 95 ON-Anweisung 41, 124
180 Programmsteuerungsanweisung 28, 30, 103 Programmstruktui 3 2 , 3 5 , 4 0 , 6 7 , 6 8 , 6 9 , 7 0 , 7 1 , 7 8 , 9 4 , 97,137,138 —, hierarchische 50, 72 - , logische 76 Programmstrukturierung 12,53 - , dynamische 100 - , statische 100 Programmtest 9,53, 115, 157 Programmverlängerung 40, 70 Programmverzweigung 28, 30, 31 - , bedingte 28 Programmzusammenführung 28, 36 Projektdokumentation 157 Projektleitung 53, 157 Projektsekretär 157, 158 Prozedur 41,42, 80, 81, 92,104,105, 108, 109, 127, 139, 159, 168 - , externe 36, 104 - , interne 36, 79, 81, 92, 93, 104, 109, 139 - , operational 92 rekursive 48 Prozeduraufruf, interner 79 - , simultaner 166 Prozeßfluß 16 Prüfverfahren 13, 14 Pseudocode 67, 97, 98, 100, 115 Qualitätskontrolle 13, 14 Rechenberg 92 REGIONAL (l)-Datei 143,144 Ross 89,91 RSL (Requirements Statements Language) 13 SADT-Darstellungsmethode 90 SADT-Methode 89ff., 94, 96, 98, 99, 152 Sammlung 104, 164 Schachtelungstiefe 35 Schaltalgebra 76, 77 Schalter 19, 34 Scheinsätze 143 Schleife 29, 32, 35, 39, 40, 59, 85, 95, 97, 104, 106, 107, 109, 114 - , iterative 32, 104 Schleifenabbruch 32 Schleifenbedingung 3 1 , 3 2 , 4 0 Schleifenkonstruktion 136 Schleifensteuerung 31, 32, 114, 158ff. Schleifensymbol 59, 85, 104 Schleifenvariable 32,42, 159 Schnittstelle 12,49, 55, 92, 96, 156, 157, 170 Schnupp 158 Sekretärfunktion 157, 158
Sach- und Personenregister selbstdokumentierende Programme 12, 135,137 SELECT-Anweisung 31, 35, 37, 171, 172 Selektion 67, 68, 72, 85, 91, 106, 112 Sequenz 57, 68, 85, 106 Simulation 13, 16, 100, 114, 115ff., 156, 170 Simulationsphase 100, 112 Simulationsprogramm 115,117 ff. Simulationssprache 100, 115 Simultanisierbarkeit 16, 165 ff. Simultanisierung 153, 165 ff. Simultanverarbeitung 104,106, 153, 164 ff. Software 10, 11, 152 Software-Bauelementekunde 14, 28ff., 48 Softwaredokumentation 11 Software-Engineering 1 3 , 1 4 , 8 1 , 9 9 , 1 5 5 Software-Entwurf, aufgabenorientierter 54, 55,56, 59, 78ff., 156 - , datenorientierter 5 5 , 5 6 , 5 9 , 6 7 , 7 2 , 9 4 , 95 - , datenstrukturorientierter 55, 67ff., 78, 94 —, funktionsbezogen 56 ff. - , prozeßorientierter 54,55,105 - , strukturierter 48,49ff., 170 Software-Konstruktionslehre 13, 14, 27, 48, 49ff., 72, 79, 170 Softwarekosten 9, 137 Softwareprojekt 98, 155, 156 Softwaretechnologie 12, 13, 158, 170 Softwarewerkzeug 67, 99 Sortieralgorithmus 33 Sortierfehler 138 Sortierkontrolle 138, 139, 140ff„ 144 Sortierprogramm 3 3 ff. Sortierprozeß 33 Speicherplatzzuweisung, dynamische 30, 80 Spezifikationen 13, 56, 156, 158 sprechende Bezeichner 13, 27, 135 Stammdatei 15, 16, 17, 19, 20, 21, 41, 42, 48, 61, 62, 63, 64, 65, 87, 88, 90, 104, 108, 109, 110, 111, 114,117, 124, 138, 142, 143, 144, 159 Steuerfluß 16, 21, 31, 57, 58, 67, 76, 79, 86, 87, 91, 92, 96, 100, 101, 104, 105, 106, 107, 108, 109, 114,156 Steuerflußkopplung 79 Steuerflußlogik 100 Steuerungsanweisung 112, 116 Steuerungslogik 112ff. Steuerungsvariable 42, 112 Strecke 30,57 Struktogramm 97 Struktur, alternative 72 GOTO-ähnliche 48,136
181
Sach- und Personenregister - , repetitive 72 Strukturblock 30, 31, 36, 57, 87 Strukturdiagramm 84, 87, 88, 89 Strukturkonflikt 67, 71 strukturierte Organisation 155 strukturierte Programmierung 1 4 , 1 5 , 1 6 , 21, 27, 28ff., 4 0 , 4 1 , 4 8 , 51, 56, 57, 68, 79, 87, 95, 97, 108, 127ff., 138, 170 strukturierter Entwurf 21, 56, 57, 78, 84, 89, 95, 97, 106, 117, 158, 164, 170 - , simultaner Prozesse 163ff. Strukturierung, hierarchische 5 7 , 7 2 , 9 6 , 104 Strukturierungsstrategien 16,49ff. Synchronisation 7 9 , 1 0 4 , 1 6 5 , 1 6 6 , 167 Synchronisationsschnitt 164 System, operationales 93, 102, 103, 114, 117, 127, 145 Systemanalyse 50, 112 Systembeschreibungen 11 Systementwurf 91, 152,155 Technologie 11, 12 Teilaufgabe 49, 50, 54, 57, 76, 78, 82, 94, 101, 103, 105, 107, 109,110, 152, 156, 157, 164 - , informationelle 54, 101, 109 Teilfunktion 1 9 , 2 1 , 4 1 , 9 4 , 101, 103, 108, 109, 110, 111, 114, 117, 138, 143, 152, 153,154,155,164,167 Teilprozeß 105
Teilsystem 49 Test 9, 13, 56, 99, 115 ff., 156, 157 THEN-Zweig 40 Top-down Design 14, 82, 84 Top-down Entwurf 15, 16, 56ff., 109, 136, 152, 157, 162, 164, 170 Top-down Programmierung 14, 115, 156 Top-down Testen 115 Universalität 12 Unterprogramm 20,40, 117, 139, 166 Up-down-Prinzip 55 Überblicksdiagramm 5 9 , 6 5 , 6 6 Van Leer 97, 100 Variantenkonstruktion 55 Verarbeitungsanweisung 30,41 Vereinbarung 93, 102, 103, 135, 136 Verfeinerung schrittweise 50 Warnier 72, 75, 76, 77, 94, 96, 99 Wartbarkeit 1 2 , 4 9 , 5 3 Wartung 112, 135, 137, 156, 157 Wartungskosten 9, 13, 83 WHEN-Anweisung 35, 171,172 White 92 Wirkungsgrad 12 Wirth 50,54 Zählschleife 32 Zuverlässigkeit 1 2 , 1 3 , 4 9
w DE
G A. Schulz
Walter de Gruyter Berlin-New York Einführung in das Programmieren in PL/1 15,5 x 23 cm. 306 Seiten. 1975. Plastik flexibel DM 36,ISBN 311003970 2 (de Gruyter Lehrbuch)
A. Schulz
Höhere PL/1-Programmierung 15,5 x 23 cm. 214 Seiten. 1976. Plastik flexibel DM 36,ISBN 3110048620 (de Gruyter Lehrbuch)
G. Niemeyer
Einführung in das Programmieren in PASCAL Mit Sonderteil UCSD-PASCAL-System 15,5 x 23 cm. 167 Seiten. 1980. Plastik flexibel DM 24,ISBN 311008280 2 (de Gruyter Lehrbuch)
G. Niemeyer
Einführung in das Programmieren in ASSEMBLER Systeme IBM, Siemens, Univac, Interdata 4. Auflage. 15,5 x 23 cm. 303 Seiten. 1982. Plastik flexibel DM 39,50 ISBN 3110087588 (de Gruyter Lehrbuch)
K. Hambeck
Einführung in das Programmieren in COBOL 3. verbesserte Auflage. 15,5 x 23 cm. X, 163 Seiten. 1981. Kartoniert DM 26,- ISBN 311 008693 X (de Gruyter Lehrbuch)
Kimm / Koch Simonsmeier / Tontsch Th. Spitta B. Gasch H. Franck
Einführung in Software Engineering 15,5 x 23 cm. 306 Seiten. 1979. Plastik flexibel DM 38,ISBN 3110078368 (de Gruyter Lehrbuch)
Systematische Einführung in die kommerzielle EDV Problemlösen mit COBOL 15,5 x 23 cm. XVI, 369 Seiten. 1979. Plastik flexibel DM 48,- ISBN 311007544 X (de Gruyter Lehrbuch)
Preisänderungen vorbehalten
w DE
G
Walter de Gruyter Berlin-New York
FORTRAN-Lexikon Ehinger/Fussy/ Herrmann / Hoffmann Anweisungen und Begriffe 15,5 x 23 cm. XX, 492 Seiten. 360 Stichworte. 1982. Kartoniert DM 48,- ISBN 311 0083590
W. E. Spieß F. G. Rheingans
Einführung in das Programmieren in FORTRAN 6. Auflage. 15,5 x 23 cm. 217 Seiten. Mit 19 Abbildungen und 13 Tabellen. 1980. Plastik flexibel DM 22,ISBN 311008135 0 (de Gruyter Lehrbuch)
W. E. Spieß G. Ehinger H. Siebert
Programmierübungen in FORTRAN 2., durchgesehene und erweiterte Auflage. 15,5 x 23 cm. 157 Seiten. 1980. Plastik flexibel DM 26,ISBN 311 0083671 (de Gruyter Lehrbuch)
Höhere FORTRAN-Programmierung Eine Anleitung zum optimalen Programmieren 2., bearbeitete Auflage. 15,5 x 23 cm. 237 Seiten. 1980. Broschiert DM 38,- ISBN 3110082268 (de Gruyter Lehrbuch)
IDV-Lernprogramm: FORTRAN Ein PU-Lehrgang für Ingenieure, Techniker, Ökonomen und Naturwissenschaftler Autor: IDV-Institut für elektronische Datenverarbeitung, Zürich, M. Kryka und B. Flükiger 2 Bände in 1 Band. 21,3 x 30 cm. XXIV, 190 Seiten. 1971. Gebunden DM 68,- ISBN 3110035766 (Coproduktion mit dem Verlag Paul Haupt, Bern)
E. W. Mägerle
Einführung in das Programmieren in BASIC 2., durchgesehene Auflage. 15,5x23 cm. 112 Seiten. 1980. Plastik flexibel DM 19,80 ISBN 311008227 6 (de Gruyter Lehrbuch)
s. Dworatschek
Grundlagen der Datenverarbeitung 6., völlig neu bearbeitete und erweiterte Auflage. 15,5.x 23 cm. 538 Seiten. Mit über 200 Abbildungen, 211 Übungsaufgaben und einem Anhang mit 59 Fotos. 1977. Kartoniert DM 44,- ISBN 3110071908 (de Gruyter Lehrbuch) Preisänderungen vorbehalten