In diesem Abschnitt werden wir einige Möglichkeiten in Ada kennen lernen, um ein Programm zu strukturieren.
Die einfachste Möglichkeit der Strukturierung sind Prozeduren. Zum Beispiel kann man das Zeichnen des Tieres wie folgt in eine Prozedur stecken:
procedure Draw_Animal is
begin
Put ('d');
end Draw_Animal;
Dieser Code sieht ein bisschen so aus wie die Main-Prozedur Run vom vorherigen Abschnitt. Wir wollen sie aber nicht in eine Datei für sich allein schreiben (könnten wir aber; diese Datei würde dann draw_animal.adb heißen). Eine Möglichkeit wäre, den Code zwischen dem is und dem begin der Prozedur Run des vorigen Abschnittes einzufügen. Die Zeile, die das Tier anzeigt, kann dann durch die folgende Zeile ersetzt werden:
Draw_Animal;
Ein anderes Beispiel ist das Löschen des Tiers:
procedure Delete is
begin
Put (Ada.Characters.Latin_1.BS);
Put (' ');
Put (Ada.Characters.Latin_1.BS);
end Delete;
Hier haben wir den Code des vorangegangen Abschnittes wiederverwendet. Schließlich können wir auch beschreiben, was es heißt, nach rechts zu rücken:
procedure Move_Right (X : Integer) is
begin
Delete;
for I in 1 .. X loop
Put (' ');
end loop;
Draw_Animal;
end Move_Right;
Hier haben wir bereits die beiden anderen Prozeduren Delete und Draw_Animal aufgerufen. Das Argument X wird hier dazu verwendet, die Anzahl der Blöcke zu zählen, die das Tier nach rechts geht, also wie viele Leerzeichen gedruckt werden müssen.
Das Programm Run, das wir bereits gesehen haben, könnte also wie folgt geschrieben werden:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Characters.Latin_1;
procedure Run is
procedure Draw_Animal is
begin
Put ('d');
end Draw_Animal;
procedure Delete is
begin
Put (Ada.Characters.Latin_1.BS);
Put (' ');
Put (Ada.Characters.Latin_1.BS);
end Delete;
procedure Move_Right (X : Integer) is
begin
Delete;
for I in 1 .. X loop
Put (' ');
end loop;
Draw_Animal;
end Move_Right;
begin
Draw_Animal;
for I in 1 .. 10 loop
delay 0.5;
Move_Right (1);
end loop;
New_Line;
end Run;
Bei einem so einfachen Programm kann man sich streiten, ob es wirklich ein Vorteil ist, so viele Prozeduren zu schreiben, aber man kann nicht bestreiten, dass das Ergebnis, insbesondere die Schleife im Hauptprogramm, viel einfacher zu verstehen ist.
Prozeduren, die inhaltlich zusammen gehören, können und sollten in ein gemeinsames Package getan werden. Ein Package ist einfach eine Einheit von Definitionen. Ein solches Package ist oft (aber nicht immer) in einer eigenen Datei. Versuchen wir uns doch einmal an einem Package für das Tier, und fügen den folgenden Code in die Datei animal.adb ein (der Code der Prozeduren wurde ausgelassen, er ist unverändert):
with Ada.Characters.Latin_1;
with Ada.Text_IO; use Ada.Text_IO;
package body Animal is
procedure Draw_Animal is
begin
...
end Draw_Animal;
procedure Delete is
begin
...
end Delete;
procedure Move_Right (X : Integer) is
begin
...
end Move_Right;
end Animal;
Wie man sehen kann, ist ein solches Package nichts weiter als eine Liste von Prozedurdefinitionen.
Ein Package besteht immer aus zwei Welten, der Innenwelt und der Außenwelt. Die Außenwelt nennt man auch die öffentliche Sicht des Packages, oder sein Interface, und sie enthält (im Optimalfall) nur, was Benutzer des Pakets wissen müssen. Die Innenwelt nennt man auch die private Sicht, und sie besteht aus den Einzelheiten der Implementation. Zum Beispiel ist es nicht unbedingt wichtig, zu wissen, wie genau das Tier gezeichnet und gelöscht wird, und solche Informationen sollten also nicht in der Außenwelt zu finden sein.
Die Datei animal.adb, die wir bereits oben angegeben haben, beschreibt die Innenwelt des Packages Animal. In Ada ist es üblich, die Außenwelt in einer Datei animal.ads anzugeben. Sie kann hier wie folgt aussehen:
package Animal is
procedure Move_Right (X : Integer);
end Animal;
Sie enthält momentan nur die Prozedur Move_Right! Man bemerke auch die Abwesenheit des Prozedurkörpers von Move_Right, hier ist nur wichtig, zu wissen, das es eine solche Prozedur gibt.
Das Main-Programm run.adb können wir nun wie folgt schreiben:
with Animal; use Animal;
procedure Run is
begin
Draw_Animal;
for I in 1 .. 10 loop
delay 0.5;
Move_Right (1);
end loop;
New_Line;
end Run;
Das Package Animal wird durch die erste Zeile eingebunden. Das Hauptprogramm ist dem weiter oben bereits angegebenen identisch, außer dass jetzt die Prozedurdefinitionen in animal.adb sind.
Die Deklaration:
with Animal;
beschreibt, dass wir das Package Animal benutzen wollen. Wenn wir nichts weiter angeben, sind die Objekte, die in Animal definiert sind, zwar verfügbar, aber sie müssen mit dem Packagenamen als Präfix, gefolgt von einem Punkt, geschrieben werden:
Animal.Move_Right (1);
ist ein solcher Aufruf mit Präfix. Wenn man das Präfix weglassen möchte, dann fügt man folgende Zeile hinzu:
use Animal;
Nun können alle Objekte in Animal ohne Präfix verwendet werden, wie es im Beispiel auch geschehe ist. Die Präfixnotation ist aber immer noch möglich.
Es gibt auch