Aufrufbare Blöcke (Unterprogramme)
Das folgende Struktogramm beschreibt die Ausgabe der ersten Hälfte des Liedes „Auf einem Baum ein Kuckuck saß.“.
Auf einem Baum ein Kuckuck sass, Variante 0 [Struktogramm DIN 66261 - A].--------------------------------------------------------.
| |
| .----------------------------------------------------. |
| | Zeile "Auf einem Baum ein Kuckuck, -" ausgeben | |
| |----------------------------------------------------| |
| | | |
| | .------------------------------------------------. | |
| | | Zeile "Sim sa la dim, bam ba," ausgeben | | |
| | |------------------------------------------------| | |
| | | Zeile "Sa la du, sa la dim -" ausgeben | | |
| | '------------------------------------------------' | |
| |----------------------------------------------------| |
| | Zeile "Auf einem Baum ein Kuckuck sass." ausgeben | |
| '----------------------------------------------------' |
|--------------------------------------------------------|
| |
| .----------------------------------------------------. |
| | Zeile "Da kam ein junger Jaeger, -" ausgeben | |
| |----------------------------------------------------| |
| | | |
| | .------------------------------------------------. | |
| | | Zeile "Sim sa la dim, bam ba," ausgeben | | |
| | |------------------------------------------------| | |
| | | Zeile "Sa la du, sa la dim -" ausgeben | | |
| | '------------------------------------------------' | |
| |----------------------------------------------------| |
| | Zeile "Da kam ein junger Jaegersmann." ausgeben | |
| '----------------------------------------------------' |
|--------------------------------------------------------|
| |
| .----------------------------------------------------. |
| | Zeile "Der schoss den armen Kuckuck, -" ausgeben | |
| |----------------------------------------------------| |
| | | |
| | .------------------------------------------------. | |
| | | Zeile "Sim sa la dim, bam ba," ausgeben | | |
| | |------------------------------------------------| | |
| | | Zeile "Sa la du, sa la dim -" ausgeben | | |
| | '------------------------------------------------' | |
| |----------------------------------------------------| |
| | Zeile "Der schoss den armen Kuckuck tot." ausgeben | |
| '----------------------------------------------------' |
'--------------------------------------------------------'AusgabeAuf einem Baum ein Kuckuck, -
Sim sa la dim, bam ba,
Sa la du, sa la dim -
Auf einem Baum ein Kuckuck sass.
Da kam ein junger Jaeger, -
Sim sa la dim, bam ba,
Sa la du, sa la dim -
Da kam ein junger Jaegersmann.
Der schoss den armen Kuckuck, -
Sim sa la dim, bam ba,
Sa la du, sa la dim -
Der schoss den armen Kuckuck tot.
Es fällt auf, daß sich ein bestimmter Block (eine bestimmte Folge von Anweisungen) wiederholt, nämlich der Refrain. Da Redundanz (Wiederholung) im Quellcode immer schlecht ist, weil sie den Code unnötig aufbläht, liegt es nahe, den Refrain-Block nur einmal zu schreiben und sich an den Stellen, an denen er benötigt wird, darauf zu beziehen.
Block "Refrain ausgeben" [Struktogramm DIN 66261 - A].------------------------------------------------.
| Zeile "Sim sa la dim, bam ba," ausgeben |
|------------------------------------------------|
| Zeile "Sa la du, sa la dim -" ausgeben |
'------------------------------------------------'
Um sich auf einen Block beziehen zu können, kann der Block benannt werden, weil der Name eines Blocks als Bezug auf diesen Block verwendet und aufgerufen werden kann.
Wiederholt auftretende Vorgänge kann man also einmal als Block (als eine Verbundanweisung) definieren und dann beliebig oft über eine Referenz (über den Namen des Blocks) ausführen lassen (aufrufen), ohne daß der Block im Programmtext jedesmal erneut ausgeschrieben (kopiert) werden muß.
Die Ausführung eines solchen aufrufbaren Blocks wird durch einen Aufruf des Blocknamens angestossen, der Name eines Blocks ist also etwas Aufrufbares.
Die in vielen Programmiersprachen gegebene Möglichkeit zu dieser Form der Benennung eines Blocks wird in dieser Lektion durch die Benennung von Struktogrammen wiedergegeben. An der Stelle der Anweisung "Struktogramm "Refrain ausgeben" aufrufen" wird dann der Block "Refrain ausgeben" ausgeführt, man sagt dazu auch, der aufrufbare Block werde aufgerufen. Daher wird solch ein Block auch als aufrufbarer Block bezeichnet. In einzelnen Programmiersprache gibt für aufrufbare Blöcke auch noch jeweils spezielle Bezeichnungen.
Auf einem Baum ein Kuckuck sass, Variante 1 [Struktogrammaehnliche Darstellung]Refrain ausgeben
.--------------------------------------------------------.
.-->| Zeile "Sim sa la dim, bam ba," ausgeben |
| |--------------------------------------------------------|
| | Zeile "Sa la du, sa la dim -" ausgeben |
| '--------------------------------------------------------'
|
|
| Hauptstruktogramm
| .--------------------------------------------------------.
| | |
| | .----------------------------------------------------. |
| | | Zeile "Auf einem Baum ein Kuckuck, -" ausgeben | |
| | |----------------------------------------------------| |
|'----- Block "Refrain ausgeben" aufrufen | |
| | |----------------------------------------------------| |
| | | Zeile "Auf einem Baum ein Kuckuck sass." ausgeben | |
| | '----------------------------------------------------' |
| |--------------------------------------------------------|
| | |
| | .----------------------------------------------------. |
| | | Zeile "Da kam ein junger Jaeger, -" ausgeben | |
| | |----------------------------------------------------| |
|'----- Block "Refrain ausgeben" aufrufen | |
| | |----------------------------------------------------| |
| | | Zeile "Da kam ein junger Jaegersmann." ausgeben | |
| | '----------------------------------------------------' |
| |--------------------------------------------------------|
| | |
| | .----------------------------------------------------. |
| | | Zeile "Der schoss den armen Kuckuck, -" ausgeben | |
| | |----------------------------------------------------| |
'----- Block "Refrain ausgeben" aufrufen | |
| |----------------------------------------------------| |
| | Zeile "Der schoss den armen Kuckuck tot." ausgeben | |
| '----------------------------------------------------' |
'--------------------------------------------------------'AusgabeAuf einem Baum ein Kuckuck, -
Sim sa la dim, bam ba,
Sa la du, sa la dim -
Auf einem Baum ein Kuckuck sass.
Da kam ein junger Jaeger, -
Sim sa la dim, bam ba,
Sa la du, sa la dim -
Da kam ein junger Jaegersmann.
Der schoss den armen Kuckuck, -
Sim sa la dim, bam ba,
Sa la du, sa la dim -
Der schoss den armen Kuckuck tot.
Man sagt auch, daß beim Aufruf eines Blocks, die Kontrolle an diesen Block übergeben werde—ist die Ausführung des Blockes beendet, dann wird die Kontrolle wieder an den Aufrufer zurückgegeben : genauer gesagt wird die Kontrolle an den Sequenzpunkt direkt nach dem Aufruf zurückgegeben.
In der Abbildung der Kontrollflußdarstellung verbindet eine Linie die Anweisungen in der Reihenfolge ihrer Ausführung. Dabei ist die zeitliche Reihenfolge der Ausführungen durch dreistellige Zahlen angegeben. Man sieht daß der Block "Refrain ausgeben" von der Kontrolle dreimal durchlaufen wird.
Auf einem Baum ein Kuckuck sass [Kontrollflussdarstellung]/| /| /|
/ | / | / |
/ | / | / |
| / | / |
Eingang Refrain | geben | |
V .--------- | -------------- | --------------- | ---------.
| | Zeile " 003 a la dim, ba 008 " ausgeben 013 |
| |--------- | -------------- | --------------- | ---------|
| | Zeile " 004 du, sa la d 009 ausgeben 014 |
| '--------- | -------------- | --------------- | ---------'
| | | |
| / / / / / /
| u / ruktogra / / / / /
.-- | - / -------- / --- / -------- / ---- / -------- / -.
| | / / / / / / |
| . | / -------- / --- / -------- / ---- / -------- / -| |
| 001 e "Auf / em / m ein Ku / ck, / ausgebe / | |
| | | -------- / --- / -------- / ---- / -------- / ---| |
| |002 ck "Re / in / geben" a / ufen / / | |
| |--------- / --- / -------- / ---- / -------- / -----| |
| | Zeile 005 ei / Baum ei / ucku / sass." a / eben | |
| '-------- | -- / -------- / ---- / -------- / -------' |
|---------- | - / -------- / ---- / -------- / ----------|
| | / / / / |
| .-------- | / -------- / ---- / -------- / ----------. |
| | Zeile 006 m ein / er J / er, -" a / eben | |
| |-------- | -------- / ---- / -------- / ------------| |
| | Block 007 ain au / ben" / frufen / | |
| |----------------- / ---- / -------- / --------------| |
| | Zeile "Da kam 010 ung / Jaegersm / ." ausgeben | |
| '---------------- | --- / -------- / ----------------' |
|------------------ | -- / -------- / -------------------|
| | / / |
| .---------------- | / -------- / -------------------. |
| | Zeile "Der sch 011/ n armen / kuck, -" ausgeben | |
| |---------------- |/--------- / ---------------------| |
| | Block "Refrain 012 eben" a / ufen | |
| |------------------------- / -----------------------| |
| | Zeile "Der schoss den a 015 Kuckuck tot." ausgeben | |
| '------------------------- | ------------------------' |
'--------------------------- | --------------------------'
|
|
V
Ausgang
Beim Aufruf eines Blocks müssen einige Aktionen erledigt werden, die nicht nötig wären, wenn der aufgerufene Block an Stelle des Aufrufs stünde. Die Kontrolle muß an den Block übertragen werden und von diesem wieder zurückgegeben werden, dabei müssen gelegentlich auch noch Verwaltungsdaten verarbeitet werden, für die dann auch noch Speicher beschafft und wieder freigegeben werden muß. Diese Aktionen kosten Zeit und stellen einen Zusatzaufwand durch einen Aufruf dar, sie werden auch als Aufrufkosten (call overhead ) bezeichnet. Da die Vorteile der besseren Gliederung diese Kosten aber meistens überwiegen, sollte man sich von den Aufrufkosten nie von der Definition aufrufbarer Blöcke abhalten lassen. Außerdem kann die Wiederholung eines Blocks im Gegensatz zum Aufruf das Programm vergrößern und dadurch eine effiziente Ausführung im Pufferspeicher (Cache ) des Prozessors verhindern. Deswegen kann ein Programm, das Blöcke aufruft, trotz der Aufrufkosten sogar schneller sein als ein Programm, das alle Blöcke an der Stelle der Aufrufe enthält. Da es im Allgemeinen also nicht auf allgemeine und einfache Weise erkannt werden kann, ob ein Aufruf anstelle einer Kopie ein Programm schneller oder langsamer macht, sollte man sich über den Aspekt der möglichen Laufzeitkosten eines Aufrufs zunächst keine weiteren Sorgen machen. Dennoch ist es zum Verständnis einiger Programmiertechniken und Fachtexte hilfreich, den Begriff „Aufrufkosten“ zu kennen.
Die Verwendung des Struktogramms "Refrain ausgeben" kann man auch mit dem Gebrauch einer Abkürzung vergleichen. Wenn man eine bekannte Abkürzung, wie „u.s.w.“ verwendet, dann ruft man damit sozusagen die Bedeutung „und so weiter“ auf. So stellt auch "Struktogramm "Refrain ausgeben" aufrufen" eine Art von Abkürzung für den Block aus der Anweisung "Ausgabe "Sim sa la dim, bam ba,"" und der Anweisung "Ausgabe "Sim sa la dim, bam ba,"" dar.
Da der Name eines Blocks verwendet werden kann, um sich auf diesen Block zu beziehen (um diesen Block zu referenzieren ), kann man einen Namen allgemein auch als eine Art von Referenz bezeichnen. Allerdings kann das Wort „Referenz“ in einigen Programmiersprachen auch noch andere Bedeutungen haben. Diese Gebrauch des Wortes hat auch nichts mit der ebenfalls in Zusammenhang mit Programmiersprachen üblichen Verwendung des Wortes Referenz für ein Nachschlagewerk zu tun.
Die Vereinfachung durch „Auskoppelung“ einer bestimmten Blocks kann man auch mit der Vereinfachung eines Terms durch „Ausklammern“ eines Teilterms nach dem Distributivgesetz vergleichen:
( a + b )· c +( a + b )· d +( a + b )· e +( a + b )· f +( a + b )· g =
( a + b )·( c + d + e + f + g ).
Durch das Ausklammern eines Faktors kann ein Term unter Beibehaltung seines Wertes vereinfacht werden. Da der neue Term zu dem vorherigen äquivalent (gleichwertig) ist, spricht man hier auch von einer Äquivalenzumformung. Solche Äquivalenzumformungen spielen auch beim Programmieren eine große Rolle, wenn es darum geht die Verständlichkeit und die Änderbarkeit von Code unter Wahrung des Verhaltens zu verbessern. Sie werden als Refaktorierung (refactoring ) bezeichnet. Damit können wir sagen, daß wir die Variante 1 des Struktogramms „Auf einem Baum ein Kuckuck sass“ durch Refaktorisierung aus der Variante 0 erhalten haben. Die Beibehaltung (Invarianz) des Verhaltens ist daran zu erkennen, daß die Ausgabe beider Varianten gleich ist.
Gleichzeitig können wir in der dreimaligen Benutzung des Blocks "Refrain ausgeben" die Wiederverwendung eines einmal geschriebenen Code-Teiles erkennen. Die Wiederverwendung und Wiederverwendbarkeit (reusability ) von Code-Teilen ist ein wichtiges Ziel bei der Software-Entwicklung, da es offensichtlich Arbeit spart, einmal geschriebenen Code in verschiedenen Zusammenhängen immer wieder verwenden zu können anstatt ihn jeweils wieder neu schreiben zu müssen.
Zu einem aufrufbarem Block gehört jeweils eine Schnittstelle (Vertrag, interface, contract ) und eine Implementation (implementation ).
Die Schnittstelle umfaßt alle Informationen, die für einen Verwender des aufrufbaren Blocks sichtbar und relevant sind, also die Form des Aufrufs (syntaktischer Aspekt der Schnittstelle) und das Verhalten des Blocks (semantischer Aspekt der Schnittstelle). Bei unseren einfachen aufrufbaren Blöcken besteht die Schnittstelle aus dem Namen des Blocks und aus einer Beschreibung seines Verhaltens.
Schnittstelle des Blocks "Refrain ausgeben"Name
Refrain ausgeben
Beschreibung
Ausgabe der beiden Zeilen des Refrains (Kehrreims)
des Liedes "Auf einem Baum ein Kuckuck sass".
Ein aufrufbarer Block sollte immer einen leicht erfaßbaren Sinn haben. Er sollte eine Sinneinheit sein, deren Sinn durch ihren Namen gut beschrieben wird. Wenn kein guter kurzer Name gefunden werden kann, dann kann das auch ein Hinweis darauf sein, daß der Block vielleicht gar keinen vernünftigen Sinn hat.
Die Implementation (Realisierung, Einrichtung) eines aufrufbaren Blocks umfaßt alle Werke und Maßnahmen, die erstellt bzw. getroffen wurden, um den Block der Beschreibung entsprechend herzustellen. Im Falle unseres Blocks "Refrain ausgeben" sind das die beiden Anweisungen zur Ausgabe der beiden Zeilen.
Implementation des Blocks "Refrain ausgeben" [Struktogramm DIN 66261 - A].---------------------------------------------------------.
| Zeile "Sim sa la dim, bam ba," ausgeben |
|---------------------------------------------------------|
| Zeile "Sa la du, sa la dim -" ausgeben |
'---------------------------------------------------------'
Die Änderung der Implementation eines aufrufbaren Blocks unter Beibehaltung seiner Schnittstelle dient oft der Verbesserung einer Implementation. Diese Reimplementation ist eine Form von Refaktorisierung. Andere Programmteile, die den Block aufrufen, müssen danach nicht angepaßt werden, weil sie nur von der Schnittstelle abhängen. Dadurch wird es erleichtert, die Implementation eines Blocks zu verbessern, während dieser seine Arbeit wie zuvor erledigt. Damit das gut geht, muß es aber verhindert werden, daß andere Programmteile sich doch noch irgendwie auf Implementationsdetails beziehen können: entsprechende Maßnahmen werden als Kapselung (encapsulation ) oder Verheimlichung (information hiding ) bezeichnet. Sie sollen es bewirken, daß die Implementation eines Blocks von außen unzugänglich „eingekapselt“ ist und der Zugriff auf diesen nur über seine Schnittstelle möglich ist.
Kapselung der Implementation durch die Schnittstelle| Umgebung |
'---------------------------------------------------'
^
|
aufrufbarer Block v
.---------------------------------------------------.
| Schnittstelle |
| .-----------------------------------------------. |
| | Implementation | |
| | | |
| | | |
| | | |
| | | |
| | | |
| '-----------------------------------------------' |
'---------------------------------------------------'
Im Falle unseres Blocks "Refrain ausgeben" bedeutet Kapselung, daß bei Verwendung dieses Blocks zwar vorausgesetzt werden darf, daß diese den Refrain des Liedes Auf einem Baum ein Kuckuck saß ausgibt (Schnittstelle), aber beispielsweise nicht daß dies immer mit zwei Anweisungen geschieht, denn das ist nur eine Möglichkeit der Implementation.
Man kann dies mit einer Flugdienst vergleichen, der eine Person von der Wohnung zur Arbeit fliegt. Der Vertrag enthält dann nur diese Leistung. Wie diese Leistung erbracht wird, etwa ob mit einem kleinen oder einem großen Flugzeug oder ob der Pilot ein Mann oder eine Frau ist, sind dann Fragen der Implementation. Diese Entscheidungen kann der Dienst alleine treffen, ohne daß Rücksprache mit dem Kunden nötig wird, weil sie die vertraglich vereinbarte Leistung gar nicht betreffen. Es ist klar, daß es vorteilhaft ist, wenn die Implementation jederzeit geändert werden darf, solange die Schnittstelle weiterhin eingehalten wird, denn beispielsweise bei Krankheit eines Piloten muß der Dienst in der Lage sein, eine Vertretung schicken zu können. Der Nutzer des Dienstes sollte nicht von der Implementation abhängen, also beispielsweise von der Person des Piloten. Bei einer Kapselung könnte der Nutzer den Piloten gar nicht sehen, wie das ja oft bei Linienflugzeugen ist. Dadurch würde es wirksam verhindert werden, daß er sich auf bestimmte Eigenschaften des Piloten einstellen kann und etwa beginnt sich zu beschweren, wenn der Dienst einen anderen Pilot hat als bisher. Der Nutzer sollte zufrieden sein, solange auch der neue Pilot (die neue Implementation) den Beförderungsvertrag (die Schnittstelle) erfüllt.
Nach diesen allgemeinen Ausführungen wird nun die Behandlung des Struktogramms zur Ausgabe des Liedes fortgesetzt: Auch die Strophen des Liedes könnten noch als einzelne Struktogramme benannt werden, die dann jeweils einem benannten Block entsprechen.
Auf einem Baum ein Kuckuck sass, Variante 2 [Struktogramm DIN 66261 - A]Refrain ausgeben
.---------------------------------------------------------.
| Zeile "Sim sa la dim, bam ba," ausgeben |
|---------------------------------------------------------|
| Zeile "Sa la du, sa la dim -" ausgeben |
'---------------------------------------------------------'
Erste Strophe ausgeben
.---------------------------------------------------------.
| Zeile "Auf einem Baum ein Kuckuck, -" ausgeben |
|---------------------------------------------------------|
| Struktogramm "Refrain ausgeben" aufrufen |
|---------------------------------------------------------|
| Zeile "Auf einem Baum ein Kuckuck sass." ausgeben |
'---------------------------------------------------------'
Zweite Strophe ausgeben
.---------------------------------------------------------.
| Zeile "Da kam ein junger Jaeger, -" ausgeben |
|---------------------------------------------------------|
| Struktogramm "Refrain ausgeben" aufrufen |
|---------------------------------------------------------|
| Zeile "Da kam ein junger Jaegersmann." ausgeben |
'---------------------------------------------------------'
Dritte Strophe ausgeben
.---------------------------------------------------------.
| Zeile "Der schoss den armen Kuckuck, -" ausgeben |
|---------------------------------------------------------|
| Struktogramm "Refrain ausgeben" aufrufen |
|---------------------------------------------------------|
| Zeile "Der schoss den armen Kuckuck tot." ausgeben |
'---------------------------------------------------------'
Hauptstruktogramm
.---------------------------------------------------------.
| Struktogramm "Erste Strophe ausgeben" aufrufen |
|---------------------------------------------------------|
| Struktogramm "Zweite Strophe ausgeben" aufrufen |
|---------------------------------------------------------|
| Struktogramm "Dritte Strophe ausgeben" aufrufen |
'---------------------------------------------------------'AusgabeAuf einem Baum ein Kuckuck, -
Sim sa la dim, bam ba,
Sa la du, sa la dim -
Auf einem Baum ein Kuckuck sass.
Da kam ein junger Jaeger, -
Sim sa la dim, bam ba,
Sa la du, sa la dim -
Da kam ein junger Jaegersmann.
Der schoss den armen Kuckuck, -
Sim sa la dim, bam ba,
Sa la du, sa la dim -
Der schoss den armen Kuckuck tot.
Wegen der nominellen Transparenz kann ein Bezeichner verändert werden, wenn dies bei jeder Verwendung im Gültigkeitsbereich geschieht und keine Namenskonflikte entstehen. So kann der Bezeicher „Refrain ausgeben“ in den Bezeichner „Kehrreim ausgeben“ umbenannt werden, ohne daß sich an der Bedeutung des Blocks etwas ändert. (Nur für einen menschlichen Leser hat der Name eine andere Bedeutung, aber der durch das Struktogramm beschriebene Vorgang bleibt gleich.)
Auf einem Baum ein Kuckuck sass, 3 [Struktogramm DIN 66261 - A]Kehrreim ausgeben
.--------------------------------------------------------.
| Zeile "Sim sa la dim, bam ba," ausgeben |
|--------------------------------------------------------|
| Zeile "Sa la du, sa la dim -" ausgeben |
'--------------------------------------------------------'
Erste Strophe ausgeben
.--------------------------------------------------------.
| Zeile "Auf einem Baum ein Kuckuck, -" ausgeben |
|--------------------------------------------------------|
| Struktogramm "Kehrreim ausgeben" aufrufen |
|--------------------------------------------------------|
| Zeile "Auf einem Baum ein Kuckuck sass." ausgeben |
'--------------------------------------------------------'
Zweite Strophe ausgeben
.--------------------------------------------------------.
| Zeile "Da kam ein junger Jaeger, -" ausgeben |
|--------------------------------------------------------|
| Struktogramm "Kehrreim ausgeben" aufrufen |
|--------------------------------------------------------|
| Zeile "Da kam ein junger Jaegersmann." ausgeben |
'--------------------------------------------------------'
Dritte Strophe ausgeben
.--------------------------------------------------------.
| Zeile "Der schoss den armen Kuckuck, -" ausgeben |
|--------------------------------------------------------|
| Struktogramm "Kehrreim ausgeben" aufrufen |
|--------------------------------------------------------|
| Zeile "Der schoss den armen Kuckuck tot." ausgeben |
'--------------------------------------------------------'
Hauptstruktogramm
.--------------------------------------------------------.
| Struktogramm "Erste Strophe ausgeben" aufrufen |
|--------------------------------------------------------|
| Struktogramm "Zweite Strophe ausgeben" aufrufen |
|--------------------------------------------------------|
| Struktogramm "Dritte Strophe ausgeben" aufrufen |
'--------------------------------------------------------'AusgabeAuf einem Baum ein Kuckuck, -
Sim sa la dim, bam ba,
Sa la du, sa la dim -
Auf einem Baum ein Kuckuck sass.
Da kam ein junger Jaeger, -
Sim sa la dim, bam ba,
Sa la du, sa la dim -
Da kam ein junger Jaegersmann.
Der schoss den armen Kuckuck, -
Sim sa la dim, bam ba,
Sa la du, sa la dim -
Der schoss den armen Kuckuck tot.
Man kann sich auch davon überzeugen, daß nach Anwendung des Substitutionsprinzips wieder die ursprüngliche Variante entsteht, indem man an der Stelle der Verwendung der benannten Struktogramme wieder deren Definition einfügt.
Die Struktur der letzten Struktogrammenge läßt sich auch als Graph veranschaulichen. Dabei kann man in diesem Fall, Aufrufer über dem Aufgerufenem darstellen und erhält so eine Anordnung der Struktogramme, in der die Höhe eine Rolle spielt, um die Position eines Struktogramms in der Aufrufhierarchie zu kennzeichnen.
Auf einem Baum ein Kuckuck sass [Aufrufstruktur].-------------------------. |
| Hauptstruktogramm | top-down |
'-------------------------' |
| | | V
.------------------------' | '----------------------------.
| | |
V V V
.-------------------------. .-------------------------. .-------------------------.
| Erste Strophe ausgeben | | Zweite Strophe ausgeben | | Dritte Strophe ausgeben |
'-------------------------' '-------------------------' '-------------------------'
| | |
'-----------------------. | .---------------------------'
| | |
V V V ^
.-------------------------. |
| Refrain ausgeben | bottom-up |
'-------------------------' |
Wenn ein Programm geschrieben oder entworfen wird, dann kann man sich dabei zuerst mit dem Grobaufbau des Programmes befassen und die Details später festlegen. Im Falle des Liedes würde dies bedeuten, zuerst das Hauptstruktogramm festzulegen, also festzulegen, daß das Lied aus drei Strophen besteht, und sich dann mit der Implementation der einzelnen Strophen zu beschäftigen, also im einzelnen festzulegen, welchen Inhalt die Strophen haben. Da man hier in der Hierarchie vom Groben zum Detail voranschreitet, also sozusagen von „oben“ nach „unten“ nennt man diese Art des Vorgehens auch top-down -Vorgehensweise oder schrittweise Verfeinerung (stepwise refinment ). Im Allgemeinen ist diese Art des Vorgehens empfehlenswert, weil es ja erst durch den Grobentwurf klar wird, welche Anforderungen an die Details gestellt werden.
Es ist aber auch denkbar, daß zunächst einzelne Details entworfen oder geplant werden und diese dann erst später zu einem größeren Ganzen zusammengesetzt werden. Entsprechend wird diese Vorgehensweise auch als bottom-up -Vorgehensweise bezeichnet. Da die Struktur auf höheren Ebenen dann aber weniger von der Problemstellung als von den verfügbaren Teilen abhängt und die sinnvolle Definition der Details auch erschwert ist, wenn noch gar nicht klar ist, welche Details überhaupt benötigt werden, wird diese Vorgehensweise seltener empfohlen. Bei der Implementation, also dem Ausformulieren eines Programmentwurfs in einer bestimmten Programmiersprache, kann diese Vorgensweise aber vorteilhaft sein, weil ein bottom-up -geschriebener Block in der Regel sofort ausgeführt und getestet werden kann, während ein top-down -geschriebener Block noch nicht ausgeführt werden kann, solange die hierarchisch untergeordneten Detail-Blöcke noch nicht fertiggestellt wurden. (Es gibt allerdings Techniken, um auch hier bereits testen zu können.)
Ein anderer Grund für bottom-up -Planung kann auch der Wunsch der Wiederverwendung vorhandener Blöcke sein. Bei einer top-down -Planung könnte sich der Bedarf nach ganz anderen Blöcken ergeben. Wenn vorhandenen Blöcke jedoch zunächst zusammengebaut werden, um einer Lösung näherzukommen, so ist es sichergestellt, daß sie auch wirklich wieder verwendet werden. Die Gefahr dabei ist aber, daß die erstellte Lösung der Aufgabe weniger angepaßt ist, weil sie vielleicht auch noch gar nicht benötigte Zusatzleistungen der vorhandenen Blöcke erbringt oder die Zusammenarbeit der Blöcke in diesem Fall ineffizient ist. In der Praxis wird ein Programmierer es nicht verhindern können, immer zu bedenken, welche Möglichkeiten schon vorhanden sind und wie diese zu einer Lösung kombiniert werden können. Im Allgemeinen ist eine top-down -Planung aber als Weg zu einer problemangepaßten Lösung empfehlenswert.
In dieser Lektion wird der Aufruf eines Struktogramms im Struktogramm immer ausdrücklich beschrieben. Wenn keine Mißverständnisse möglich sind, kann diese Einleitung allerdings auch entfallen, so daß in dem Kästchen dann nur noch der Name des aufzurufenden Struktogramms steht, also beispielsweise einfach "Dritte Strophe ausgeben" statt '"Struktogramm "Dritte Strophe ausgeben" aufrufen'.
Eine Anweisung zur Ausgabe einer Zeile, wie 'Zeile "Der schoss den armen Kuckuck tot." ausgeben' wird auch als eine Elementaranweisung bezeichnet, weil sie nicht weiter in andere Anweisungen zerlegt werden kann. Ein Block ist meistens eine komplexe Anweisung (zusammengesetzte Anweisung), also keine Elementaranweisung. Wenn ein aufrufbarer Block durch seinen Namen aufgerufen wird, dann sieht dieser Aufruf aber aus wie eine Elementaranweisung, obwohl er zur Ausführung des Blocks führt, der ja keine Elementaranweisung ist. Die Elementaranweisungen sind Bestandteile der Programmiersprache. Da durch Benennung eines Blocks Aufrufe des Blockes möglich werden, die den durch die Programmiersprache definierten Elementaranweisungen ähneln, ist der Unterschied zwischen der Verwendung einer Elementaranweisung und dem Aufruf eines komplexen Blocks oft nicht groß, so daß man den Eindruck gewinnt, daß ein aufrufbarer Block eine zusätzliche Elementaranweisung bereitstellt. So gesehen, kann durch die Benennung von Blöcken eine Programmiersprache um neue Anweisungen erweitert werden, die dann ganz ähnlich wie die schon von der Programmiersprache bereitgestellten Elementaranweisungen verwendet werden können. In diesem Sinne kann ein Programmierer also eine Programmiersprache sozusagen „erweitern“, um sie an eine spezielle Aufgabenstellung anzupassen. (Obwohl die Definition aufrufbarer Blöcke genau genommen keine Erweiterung einer Programmiersprache darstellt.)
Benannte aufrufbare Blöcke spielen in vielen Programmiersprachen eine bedeutende Rolle beim Aufbau und der Strukturierung von Programmen. Ein solcher Block wird—je nach der Programmiersprache—auch als ein Unterprogramm, eine Prozedur, eine Methode, eine Unterroutine , eine Routine oder eine Funktion bezeichnet.
In diesem Text wird zur programmiersprachenunabhängigen Bezeichnung solcher aufrufbarere Blöcke die mit der internationalen Norm ISO/IEC 2382-15 verträgliche allgemeine Bezeichnung „Unterprogramm“ (subprogram ) verwendet.
“subprogram A module that has an identifier and that is invoked into the control flow from another program or by another module by means of a specific language construct and from which the control flow returns to the invoking program or module. ” (ISO/IEC 2382-15 ,1998, 15.06.03)
Die hier behandelte Art von Unterprogramm wird auch als Wirkunterprogramm bezeichnet, weil sie eine bestimmte Wirkung hervorruft.
Der Name eines Wirkunterprogramms sollte angeben, was ihr Aufruf sie tut.
Benannter Block [UML, static structure diagram].-------------. .-------------.
| Benannter | 1 1 | Name |
| Block |<>--------| |
| | | |
| | '-------------'
| |
| | .-------------.
| | 1 1 | Block |
| |<>--------| |
| | | |
'-------------' '-------------'
Beim Militär verwendet man beispielsweise bestimmte Kommands, wie das Kommando „abzählen!“, um eine ganz bestimmte für dieses Kommando festgelegte Aktion zu verlangen. Hierbei ist es wichtig, daß das Kommando „abzählen!“ einmal durch eine Anleitung für diese Aktion erklärt wurde, so daß bei späterer Verwendung die Aktion nicht mehr erklärt werden muß, sondern der Ausruf des Namens ausreicht.
Durch die Vereinbarung des Kommandos „abzählen!“ für diese Aktion wurde sozusagen ein aufrufbarer Block definiert.
Die Verwendung des Kommandos „abzählen!“ durch einen Kommandierenden kann jeweils eine Durchführung dieser Aktion (ein Vorgang) hervorrufen, es erfolgt also sozusagen ein Aufruf. Tatsächlich werden die Kommandos beim Militär oft wirklich gerufen, möglicherweise ist dies die Herkunft des Begriffs „Aufruf“.
Aber auch viele andere Phänomene außerhalb der Welt des Programmierens, wie etwa Kochrezepte, kann man als eine Art von Benennung eines Blocks ansehen.
Die Anweisungen eines aufrufbaren Blocks sollten immer einen bestimmten Sinn ergeben und nicht nur willkürlich zusammengewürfelt sein. Man kann ihn dann mit einer Anleitung zu einer bestimmten Aktion vergleichen. Eine sinnvoll benannter Block erscheint dann als Kombination aus einem Namen und einer Aktionsanleitung.
Wenn man nicht mit einem kurzen Namen oder wenigen Worten sagen kann, was ein bestimmter aufrufbarer Block macht, dann wurde er vielleicht nicht sinnvoll gebildet. Wenn man bei der Beschreibung das Wort „und“ verwenden muß, um mehrere verschiedene Aussagen über den aufrufbaren Block zusammenzufassen, dann sollte dieser vielleicht besser weiter in mehrere aufrufbare Blöcke zerlegt werden, die sich dann ohne „und“ beschreiben lassen. Wenn ein aufrufbarer Block, der genau eine bestimmte Aufgabe erfüllt, dann sagt man auch, er habe eine hohe Kohäsion (cohesion ). Das ist erstrebenswert. Die Kohäsion wird erhöht, indem ein aufrufbarer Block, der mehrere verschiedene Aufgaben enthält, in einzelne aufrufbare Blöcke zerlegt wird, die jeweils eine einzelne Aufgabe erfüllen, oder, indem mehrere verschiedene aufrufbare Blöcke, die eigentlich zusammen eine Aufgabe erledigen, zu einem einzigen aufurfbaren Block zusammengefaßt werden. Solche Änderungen sind dann wieder Refaktorisierungen.
Ein Beispiel mangelhafter Kohäsion ist ein Buch mit dem Titel „Programmieren mit C++“, das einen ausführlichen Anhang über die Geschichte von Programmiersprachen enthält. Man stelle sich beispielsweise vor, jemand sucht in eine Regal in einer Bücherei oder Buchhandlung ein Buch über die Geschichte von Programmiersprachen. Er würde wahrscheinlich nicht auf die Idee kommen, in dem Buch „Programmieren mit C++“ danach zu suchen, obwohl der Anhang vielleicht gerade das Gesuchte ist. Selbst, wenn er diesen Anhang doch entdeckte, könnte er sich ärgern, daß er den nicht interessierenden Teil über C++ -Programmierung beim Kauf mitbezahlen muß. Die Verbesserung der Kohäsion wäre es in diesem Fall natürlich, den umfangreichen Anhang als getrenntes Buch verfügbar zu machen. (Derzeit ist die Lektion, in der dieser Absatz steht auch ein Beispiel für mangelnde Kohäsion, da sie zu viele verschiedene Themen enthält.)
Es gibt auch Richtlinien, denenzufolge ein aufrufbarer Block nicht länger sein darf als eine bestimmte Zahl von Zeilen, doch sind solche starren äußerlichen Vorgaben nicht immer angebracht. Allerdings stimmt es, daß Anfänger zu monolithisch (d.h. unter Verwendung eines großen Blocks) programmieren und selten daran denken ihr Programm sinnvoll in aufrufbare Blöcke aufzuteilen. Daher sind die Blöcke von Anfängern oft zu groß und man darf die etwas starren Richtlinien („Ein aufrufbarer Block darf nicht mehr als 12 Zeilen haben.“, „Ein aufrufbarer Block muß ganz auf den Bildschirm passen.“) so verstehen, daß sie daran erinnern sollen, immer zu prüfen, ob ein Block, der zu viele verschiedene Aufgaben erledigt, nicht sinnvoll in mehrere aufrufbare Blöcke zerlegt werden kann.
Man kann einen Block auch mit dieser Lektion vergleichen: Auch bei Lektionen eines Lehrwerkes hilft es dem Leser, wenn jede Lektion sich mit einem bestimmten Thema beschäftigt und auch entsprechend benannt ist. Beschäftigen sich Lektionen dagegen stets gemischt mit mehreren Themen, können sie oft auch nicht kurz und treffend bezeichnet werden, sondern sind beispielsweise durchnumeriert. Dann ist es schwieriger, etwas wiederzufinden oder zu verstehen, worum es gerade geht.
Man kann einen aufrufbaren Block auch als einen Spezialfall des allgemeinen Konzeptes Dienst ansehen: Ein Dienst ist etwas, das eine (nützliche) Leistung erbringt. Ein Programmteil, der einen Dienst aufruft, ist ein Spezialfall des allgemeinen Konzeptes Klient : Etwas, das einen Dienst nutzt. Wichtig bei der Entwicklung eines Programmes ist der Testklient : Ein Klient, der einen Dienst mit der Absicht, diesen zu testen, nutzt. Während ein Testklient Fehler eines Dienstes finden soll, kann ein Probeklient geschrieben werden, um einen Dienst auszuprobieren, ohne viele Aspekte zu testen. In der Regel wird man während der Entwicklung zu jedem verwendeten, besonders zu jedem neu entwickelten, Dienst auch einen Testklienten schreiben, um sich von der Funktionsfähigkeit des Dienstes zu überzeugen. In dem gegebenen Beispiel sind die Blöcke zur Ausgabe der einzelnen Strophen des Liedanfangs Klienten der Blöcke zur Ausgabe des Refrains.
Benennung
Ein Unterprogramm, das eine bestimmte Wirkung hat, soll nach dieser Wirkung benannt werden. So soll ein Unterprogramm, das etwas löscht, beispielsweise mit dem Bezeichner "Loeschen" benannt werden.
Übungen
- Ein Kochrezept
- „Kartoffel-Lauch-Auflauf
- Backofen, Auflaufform, 1000 g Lauch, 1000 g Kartoffeln, 200 g Schimmelkäse, 200 g Creme fraiche, Salz, Pfeffer.
- Lauch und die Kartoffeln putzen, klein schneiden, salzen, pfeffern und in die Form geben. Schimmelkäse mit Creme fraiche und Pfeffer vermischen und darüber geben. Bei 200 °C für 50 Minuten backen.“
- Übungsfrage Was ist der Name des obenstehenden „Blocks“ (also des Kochrezeptes)?
- Übungsfrage In Zusammenhang mit Aktion wurden die Begriffe „Vorzustand“ und „Nachzustand“ eingeführt. Was ist der vorausgesetzte Vorzustand und was der bewirkte Nachzustand im Falle des Kochrezeptes?
- Ein Aufruf
Struktogramm "muster" [Struktogramm DIN 66261 - A]
.-----------------------------------------.
| Zeile "Hallo!" ausgeben |
'-----------------------------------------'Struktogramm "beispiel" [Struktogramm DIN 66261 - A]
.-----------------------------------------.
| Zeile "Test!" ausgeben |
'-----------------------------------------'Struktogramm "Hauptprogramm" [Struktogramm DIN 66261 - A]
.-----------------------------------------.
| Struktogramm "muster" aufrufen |
'-----------------------------------------'- Übungsfrage Was gibt das Struktogramm "Hauptprogramm" aus?
- Noch ein Aufruf
Struktogramm "proc1" [Struktogramm DIN 66261 - A]
.-----------------------------------------.
| Zeile "Hallo!" ausgeben |
|-----------------------------------------|
| Struktogramm "proc1" aufrufen |
'-----------------------------------------'- Übungsfrage Was macht das Struktogramm "proc1"?
- Ein weiterer Aufruf
Struktogramm "proc2" [Struktogramm DIN 66261 - A]
.-----------------------------------------.
| Struktogramm "proc2" aufrufen |
|-----------------------------------------|
| Zeile "Hallo!" ausgeben |
'-----------------------------------------'- Übungsfrage Was macht das Struktogramm "proc2"?
- Fachbegriffe
- Versuchen Sie sich jeweils zu erinnern, welche Bedeutung die folgenden Begriffe in Zusammenhang mit aufrufbaren Blöcken haben, indem Sie kurz in eigenen Worten formulieren, was der Begriff bedeuet.
- Refaktorisierung, Schnittstelle, Implementation, Wiederverwendung, Reimplementation, Kapselung, Verheimlichung, Dienst, Klient, Testklient, Kohäsion.