Inhalt

  • Was soll der Baustein tun?
  • Was leistet Ada für den Ent­wurf?
  • Was leistet Ada für die Kodie­rung?
  • Der erste Schritt zum Test
  • Zum Weiter­machen
  • Das perfekte Werk­zeug
  • Das gute, alte Buch


An­for­de­rung

Was soll der Baustein tun?

Ein Problem aus dem Projektalltag: An­for­de­run­gen hat­ten sich ge­än­dert mit der Fol­ge, dass Mel­dun­gen zum selben Ereignis zwei­mal ge­sen­det wer­den konn­ten - und dieses Ver­halten war un­er­wünscht.

Meldungen sollten daher eine kurze Zeit auf­bewahrt wer­den, um über­prü­fen zu kön­nen, ob eine Meldung zu einem Ereignis be­reits ver­schickt worden ist.

Ein geflügeltes Wort im Projektalltag war minimal in­va­siv, was hei­ßen soll, Änderungen seien so vor­zu­neh­men, dass Sei­ten­ef­fek­te mög­lichst aus­ge­schlos­sen werden können. Da liegt ein mo­du­la­rer ge­ne­ri­scher Bau­stein nahe, der au­ßer­halb des Soft­ware­systems ent­wickelt und und des­sen Ab­lauflogik auch außerhalb des Soft­ware­sys­tems ge­tes­tet wer­den kann.



Ent­wurf

Was leistet Ada für den Entwurf?

Was muss ich wissen? Ich benötige ei­nen ge­ne­ri­schen Datentyp Item­_Type (Zeile 5), des­sen Elemente gepuffert werden sollen, sei­ne Struk­tur ist im we­sent­li­chen un­er­heb­lich - aber ich muss über­prü­fen kön­nen, ob zwei Ele­men­te des Datentyps 'gleich' (Are­_Equal, Zei­le 10) sind. Und der Da­ten­typ muss Zeit­in­for­ma­tionen tra­gen (Get­_Ti­me_Stamp, Zeile 12), denn eine Meldung soll ja nur eine ge­wis­se Zeit (Sto­rage­_Duration, Zeile 8) aufbewahrt werden.

Das Paket kommt also mit zwei ge­ne­ri­schen Funk­tio­nen da­her (Zei­len 10, 12), ich muss nur ihre Schnitt­stellen kennen, ihre Im­ple­men­tie­rung ist un­er­heb­lich. Das "is <>" hier weist den Compiler an, bei der Ausprägung des ge­ne­ri­schen Pa­ke­tes selbst nach die­sen Fun­ktio­nen zu suchen.

Die Schnittstelle des generischen Pakets

Anwen­dungs­schnitt­stelle

Die Anwendungschnittstelle weist zwei Rou­ti­nen auf, mit der Pro­ze­dur Add (Zeile 16) schiebe ich ein Element in den Puffer und mit der Funk­tion Al­ready_­Handled (Zeile 18) kann über­prüft werden, ob ein Ele­ment schon im Puffer vor­han­den ist.

Die Ausnahme No_Space_To_Store_Item (Zeile 20) ist eher ein Test­hilfs­mit­tel, diese Ausnahme wird ausgelöst, wenn der Puffer voll ist, die zu­ge­ord­ne­te Meldung wird in der Im­ple­men­tie­rung schlicht ver­wor­fen.



Imple­men­tie­rung

Was leistet Ada für die Implementierung?

Als Puffer verwende ich eine Booch-Komponente, die die ge­spei­cher­ten Ein­träge gleich nach dem Zeitstempel sor­tiert. Der Zugriff auf den Puf­fer wird über ein ge­schütz­tes Objekt ('pro­tec­ted object') na­mens Con­troller (Zeile 25) ge­steu­ert.

Die Schnittstelle des Controllers weist fünf Rou­ti­nen auf, darunter ist der bewachte Ein­gang Get_­Next_To_Purge (Zeile 31); ist der Puffer leer, muss der Aufrufer dieses Ein­gangs so­lan­ge war­ten, bis ein Ein­trag da ist, notiert durch den Wäch­ter des Eingangs (Zei­le 36).

Der verwendete Puffer The_Items (Zeile 34) steht im pri­va­ten Teil der Ob­jekt­spe­zi­fi­ka­tion.

Das geschütze Objekt namens Controller
die Schnittstelle

Die Implementierung des Eingangs 'Get_­Next­_To­_Purge' ist kurz und knapp. Der Ein­gangs­auf­rufer bleibt wie gesagt solange blo­ckiert, bis der Wäch­ter hin­ter dem Schlüs­selwort when (Zeile 70) den Wert True an­ge­nom­men hat.

Die Implementierung des Eingangs
'Get_Next_To_Purge'

Die Implementierung der Funktion Al­ready_­Hand­led ver­wen­det die ge­ne­ri­sche Funk­tion Are_Equal (Zeile 85).

Die Implementierung der Funktion
'Already_Handled'

Den aktiven Teil unseres Moduls übernimmt ein Task­ob­jekt namens Timed_Purger, des­sen Rumpf aus einer Schlei­fe besteht, in der zu­nächst das älteste Element aus dem Puffer entnommen wird (Zei­le 134), dann wird die verbleibende Wartezeit berechnet (Zeile 136), um schließ­lich zu warten, bis das Ele­ment gelöscht werden kann (Zei­le 140).

Die Implementierung des Taskobjektes
'Timed_Purger'



Test

Der erste Schritt zum Test

Um das generische Paket testen zu kön­nen, müs­sen für den ak­tu­el­len Datentyp (Zeilen 11-14) die generischen Routinen be­reit­ge­stellt werden (Zei­len 16-19, 21-25).

Die Ausprägung oder Instantiierung wird in den Zei­len 35-38 vorge­nommen.

Die Ausprägung des generischen Paketes



Quell­code

Hinweis: Die Zeilennummern stimmen nicht un­be­dingt mit denen in den obi­gen Code-Aus­schnit­ten überein.

•  Der Quellcode, gezippt     Mein GNAT-Projekt zum Weiter­machen



Ada-Com­piler

•  GNAT-Ada-Compiler        von AdaCore



Lite­ratur

Das gute, alte Buch

John Barnes
Programming in Ada 2005
Addison-Wesley, 2006

Alan Burns, Andy Wellings
Concurrent and Real-Time Programming in Ada 2005
Cambridge University Press, 2007

Norman H. Cohen
Ada as a second language - based on Ada 95
The McGraw-Hill Companies, 1996