Viele Experten halten Eiffel für die beste Programmiersprache, um wirklich zuverlässige Software zu entwickeln. Mit dem neuerdings freien Eiffelstudio stellen Sie diese Theorie auf den Prüfstand.
Große Projekte setzen auf Eiffel, etwa ein rund zwei Millionen Codezeilen umfassendes System von AXA Rosenberg fürs Investment Management. Xontech, ein Boeing-Zulieferer, modelliert mit Eiffel, wie Satelliten und Radar ankommende ballistische Raketen erkennen können. Viele Universitäten lehren Eiffel als Programmiersprache in ihren Vorlesungen. Eiffel-Erfinder Bertrand Meyer [1] ist selbst Professor für Software Engineering an der ETH Zürich.
Neu: Freie Eiffel-IDE
Den ersten kommerziellen Compiler veröffentlichte Bertrand Meyers Firma Interactive Software Engineering bereits 1986. Am 21. Juni 2005 verabschiedete die European Computer Manufacturers Association (ECMA) den Standard ECMA-367 (Eiffel Analysis, Design and Implementation Language, [2]), der die aktuell gültige Spezifikation der Programmiersprache definiert. Seit Anfang April 2006 ist Eiffel auch für freie Entwickler interessant: Alternativ zur kommerziellen Lizenz gibt es die plattformübergreifende Entwicklungsumgebung Eiffelstudio nun auch in einer GPL-Version [3].
In diesem Artikel lernen Sie die Programmiersprache Eiffel und die Entwicklung von Applikationen mit Eiffelstudio näher kennen. Mehr Informationen zum Open-Source-Projekt von Eiffelstudio liefert das Wiki [4]. Neben Eiffelstudio bestehen weitere freie Compiler und Entwicklungsumgebungen, siehe Kasten “Alternativen”.
|
Alternativen |
|---|
|
Von den freien Eiffel-Compilern unterstützt neben dem von Eiffel Software nur der Gobo Eiffel Compiler den ECMA-Standard:
Neben der Entwicklungsumgebung von Eiffelstudio gibt es auch andere Möglichkeiten, Eiffel-Programme zu schreiben: die EDT (Eclipse Eiffel Development Tools) [8] und GNU Emacs oder XEmacs mit dem Eiffel-Modus [9]. Außerdem heben viele Editoren wie Vi oder Jedit die Eiffel-Syntax farbig hervor. Weder die Eclipse-EDT noch Emacs bringen jedoch einen eigenen Eiffel-Compiler mit. Deshalb kommt man nicht umhin, den Compiler von Eiffel Software oder einen der oben genannten Compiler zu installieren. |
Weil das freie Eiffelstudio noch nicht für den produktiven Einsatz geeignet ist, verwenden Sie am besten die aktuelle stabile Eiffelstudio Free Edition 5.6, mit der Sie nur nicht-kommerzielle Anwendungen erstellen dürfen. Eiffelstudio benötigt GTK+ ab Version 2.4.0. Um mit der Bibliothek Eiffelvision GUI-Applikationen zu entwickeln, müssen Sie die Entwicklungspakete von GTK+ und Libxst installieren.
Die IDE können Sie von [3] oder einem der Mirrors herunterladen. Sie entpacken dann das Archiv in ein geeignetes Verzeichnis und definieren einige Umgebungsvariablen:
export ISE_EIFFEL=/opt/Eiffel56 export ISE_PLATFORM=linux-x86 export PATH=$PATH:$ISE_EIFFEL/studio/spec/$ISE_PLATFORM/bin
»ISE_EIFFEL« muss auf das Eiffelstudio-Verzeichnis zeigen, »ISE_PLATFORM« auf die benutzte Version, also entweder »linux-x86« für 32 Bit, »linux-x86-64« für 64 Bit oder »linux-ppc« für PPC.
Mit dem Skript »/make_install« kompilieren Sie schließlich die Bibliothek Eiffelbase. Das spart später beim Übersetzen von Programmen Zeit – jedes mit Eiffelstudio erstellte Programm verwendet Eiffelbase. Wer mit Eiffelstudio auch GUIs programmieren will, sollte auch die Frage des Installers nach dem Vorkompilieren von Eiffelvision mit Ja beantworten.
Turmbau zu Paris
Bevor es richtig losgeht, sollen die folgenden Absätze in einige Eigenheiten von Eiffel einführen. Grundeinheit jedes Eiffel-Programms ist eine Klasse, die in einer Datei mit der Endung ».e« gespeichert ist. Eine Klasse repräsentiert einen abstrakten Datentyp, jedes Objekt ist eine Instanz einer Klasse. Das Erstellen des Objekts übernimmt die so genannte Creation-Prozedur, ähnlich wie der Konstruktor in Java oder C++.
Eine Klasse ist durch eine Menge von Features (Operationen) charakterisiert, die entweder Attribute oder Routinen sein können. Diese Klassifizierung von Features beruht auf ihrer Funktion: Routinen führen eine Berechnung aus, Attribute speichern einen Wert. Eiffel unterscheidet zudem zwischen Routinen mit Rückgabewert (Funktionen) und solchen ohne (Prozeduren).
Es gibt auch eine Klassifizierung nach Aufgaben: Features sind entweder Commands (ohne Rückgabewert) oder Queries (mit Rückgabewert). Queries sind entweder Funktionen oder Attribute (wenn sich der Wert im Speicher befindet). Abbildung 1 stellt diese Kategorisierung von Features dar.
Seiteneffekte vermeiden
Obwohl der Compiler es nicht eigens erzwingt, gilt beim Programmieren mit Eiffel das Command-Query-Separationsprinzip: Ein Feature sollte nicht den Status des Objekts ändern und gleichzeitig einen Wert zurückgeben. Mit anderen Worten: Eine Funktion sollte keine Nebeneffekte haben.
Klassen sind in Cluster gruppiert, die meist eine Verzeichnishierarchie widerspiegeln. Dies ist jedoch nicht zwingend erforderlich, denn Cluster sind nur eine logische Aufteilung. Sie können auch Subcluster enthalten, so wie ein Verzeichnis Unterverzeichnisse enthält. Ein System ist eine Menge von Klassen, die sich zu einer ausführbaren Datei assemblieren lässt. Typischerweise ist es eine Menge von Clustern, die Klassen enthalten. Ein System entspricht in etwa einem Programm.
In Eiffel besitzt jedes System eine Root-Klasse. Sie wird bei der Ausführung des Systems mit ihrem Konstruktor (Creation-Prozedur) – der so genannten Root-Creation-Prozedur – instanziiert. Ein System entspricht den Klassen, die die Root-Klasse direkt oder indirekt benutzt. Jedes Objekt ist eine Instanz einer Klasse. Selbst bei elementaren Typen wie »INTEGER« oder »DOUBLE« handelt es sich um Objekte.
Außerdem ist Eiffel stark typisiert. Bei der Mehrheit der Typen handelt es sich um Referenztypen (Reference Types), die nicht selbst Objekte enthalten, sondern nur Referenzen auf sie. Neben Referenztypen gibt es auch erweiterte Typen (Expanded Types), die direkt Objekte aufnehmen. Das ist bei den elementaren Typen »INTEGER«, »REAL«, »DOUBLE«, »BOOLEAN« und »CHARACTER« der Fall. Zum Beispiel ist der Wert 42 des Typs »INTEGER« ein Objekt und nicht etwa eine Referenz auf ein Objekt.
Hello, Eiffelstudio!
Sie starten Eiffelstudio mit dem Befehl »estudio«. Dann wählen Sie im ersten Dialog einfach »Next«, um ein neues Projekt zu erstellen und geben im folgenden Dialog den Projektnamen und das Verzeichnis an. Nach einem Klick auf »OK« legt Eiffelstudio das Projekt an und kompiliert das System.
Die Benutzeroberfläche ist in vier Bereiche unterteilt (siehe Abbildung 2). Der linke untere Fensterbereich (1) zeigt die Cluster eines Projekts, in dem Sie wie in Verzeichnissen navigieren. In den Clustern befinden sich die Klassen, aus denen das Projekt besteht.
Wenn Sie eine Klasse anklicken, zeigt der Editor (2) sie an. Links vom Editor befindet sich die Auflistung aller Features der aktuellen Klasse (3). Falls Eiffelstudio nicht alle Features einer Klasse auflistet, müssen Sie das System noch einmal kompilieren.
Durch Anklicken eines Features springt der Editor automatisch an jene Stelle, an der das Feature definiert ist. Unten rechts befindet sich das Context-Fenster (4). Es bietet durch die unteren Karteireiter einige interessante Ansichten des Projekts, etwa Diagramme oder eine Übersicht über die Klassen.
Mit dem Assistenten »Tools | New Class…« erstellen Sie die Klasse »HELLO« (Listing 1). Obwohl die Groß- und Kleinschreibung beim Eiffel-Code keine Rolle spielt, gibt es einige Konventionen, an die Sie sich halten sollten (siehe Kasten “Groß- und Kleinschreibung”).
|
Listing 1: Hello World, |
|---|
01 indexing
02 description: "Simple 'Hello, World!' class"
03
04 class
05 HELLO
06
07 create
08 make
09
10 feature {NONE} -- Initialization
11
12 make is
13 -- Creation procedure.
14 do
15 set_name ("anonymous Eiffel Programmer")
16 end
17
18 feature -- Access
19
20 name: STRING
21 -- Name used for greeting.
22
23 greeting: STRING is
24 -- Printable representation of `HELLO' objects.
25 do
26 Result := "Hello, " + name + "!"
27 end
28
29 feature -- Element change
30
31 set_name (a_name: STRING) is
32 -- Set `name' with `a_name'.
33 require
34 name_exists: a_name /= Void
35 name_valid: not a_name.is_empty
36 do
37 name := a_name
38 ensure
39 name_set: name = a_name
40 end
41
42 invariant
43 name_exists: name /= Void
44 name_valid: not name.is_empty
45
46 end -- class HELLO
|
|
Groß- und |
|---|
|
Von Eiffel-Programmierern verwendete Konventionen zur Groß- und Kleinschreibung:
|
Der Eintrag »indexing« in Zeile 1 startet den fakultativen Indexierungs-Abschnitt, der der Dokumentation dient. Jeder Eintrag im »indexing«-Abschnitt besteht aus zwei durch Doppelpunkte getrennten Teilen: Kennzeichnung und Text. Weitere gebräuchliche Kennzeichnungen neben »description« sind »author«, »date«, »note« und »revision«.
Die eigentliche Klasse beginnt in Zeile 4 mit dem Schlüsselwort »class« gefolgt von ihrem Namen. Mit »feature« führen Sie eine Menge von Features ein. Die mit »–« gekennzeichneten Kommentare sind optional, Eiffelstudio verwendet sie aber zur Gruppierung. Das »end« in Zeile 46 beendet die Klassendefinition.
Mit »create« (Zeile 7) definieren Sie die Namen der Creation-Prozeduren (Konstruktoren) der Klasse. Im Gegensatz zu Java oder C++ besitzen Konstruktoren in Eiffel keine vordefinierten Namen. Verfügt eine Klasse über mehrere Creation-Prozeduren, werden die Namen mit Kommas voneinander getrennt.
Es ist auch möglich, dass die Klasse keine »create«-Anweisung hat. In diesem Fall erbt sie den Standardkonstruktor »default_create« von der Klasse »ANY«, von der alle Eiffel-Klassen implizit erben (Abbildung 3). In der Klasse »HELLO« gibt es nur eine Creation-Prozedur, die den Namen »make« trägt (Zeile 8).
Eiffel erlaubt es, jeden Aspekt der Datenkapselung zu kontrollieren. So folgt dem Schlüsselwort »feature« in Zeile 10 der Klassename »NONE« in geschweiften Klammern. Das bedeutet, dass Eiffel Features nach dieser Anweisung und bis zum nächsten »feature«-Schlüsselwort nur für die Klasse »NONE« exportiert. »NONE« ist eine virtuelle Klasse, die von allen Klassen erbt, sie steht am Ende der Vererbungshierarchie (Abbildung 3).
Ein Feature, das nach »NONE« exportiert wird, kann von keinem Verwender (Client) der Klasse aufgerufen werden. Nur die Klasse selbst und ihre Erben dürfen es benutzen. »feature {NONE}« ist äquivalent zu »feature {}«. Dieses Konzept entspricht in etwa dem Protected in Java oder C++.
Zwischen den geschweiften Klammern darf jede Klasse stehen. Kommas trennen mehrere voneinander. Damit können Sie das Exportieren von Features sehr fein steuern. Standardmäßig exportiert Eiffel alle Features nach »ANY«, »feature« ist also äquivalent zu »feature {ANY}«. Das bedeutet, dass jeder Client die Features aufrufen darf.
Obwohl der Konstruktor nach »NONE« exportiert wurde, können Sie »HELLO« instanziieren. Das Exportieren einer Creation-Prozedur nach »NONE« bedeutet, dass die Prozedur sich auf das Erstellen eines Objekts beschränkt. Der Client darf sie nicht als normale Prozedur aufrufen.
Design by Contract
Eines der zentralen Konzepte von Eiffel heißt Design by Contract. Der so genannte Vertrag (Contract) beschreibt die Zusammenarbeit der einzelnen Softwarekomponenten eines Systems mit Hilfe präziser Zusicherungen, also semantischer Bedingungen.
Wie im echten Leben hat auch beim Design by Contract der Anbieter Verpflichtungen gegenüber dem Kunden und umgekehrt. Nur sind eben in der Softwarewelt Anbieter und Kunden Softwarekomponenten. Vorteile von Design by Contract sind:
- Es hilft während der Implementation, weil die
Zusicherungen gleichzeitig als Spezifikation der Software
dienen. - Verträge unterstützen die Entwicklung korrekter
Software. Mit Design by Contract denkt der Programmierer über
die Anforderungen jeder Routine nach und setzt diese in
Programmcode um. - Verträge dienen als Basis für die Dokumentation.
Eiffelstudio zum Beispiel generiert die Dokumentation automatisch
aus den Verträgen (»Project | Generate
Documentation«). - Mit Verträgen ist es einfacher, Fehler in einem Programm
zu finden, weil die Ausführung an der Fehlerstelle stoppt.
Autotest [10] erstellt aus den Verträgen automatisch
Tests.
In Eiffel gibt es drei Hauptkategorien von Vertragsbedingungen: Klasseninvarianten (»invariant«), Vorbedingungen (»require«) und Nachbedingungen (»ensure«). Weitere Vertragsbedingungen sind Check-Instruktionen, Schleifeninvarianten und -varianten.
Bei allen Vertragsbedingungen bestehen die Zusicherungen aus einer Kennzeichnung (Tag) und einem booleschen Ausdruck der Form Kennzeichnung:Boolescher_Ausdruck. Das Tag ist optional, aber zur Dokumentation und zum Debuggen sehr nützlich.
Normalerweise prüft Eiffelstudio nur Vorbedingungen. Um auch die anderen Zusicherungen zu kontrollieren, müssen Sie sie unter »Project | Project Settings« und »Default assertions« aktivieren.
Klasseninvarianten müssen gelten, sobald eine Instanz der Klasse erzeugt ist und anschließend vor und nach der Ausführung von beliebigen Features der Klasse. Diese Regel gilt nur für qualifizierte Aufrufe der Form Objekt.Routine. Unqualifizierte Aufrufe der Form Routine und Aufrufe von nicht exportierten Features müssen die Klasseninvariante nicht bewahren. Die Klasse »HELLO« besitzt die Invariante, dass das Attribut »name« nie auf »Void« zeigen (Listing 1, Zeile 43) und nie leer sein darf (Zeile 44).
Bedingungen in Routinen
Vor- und Nachbedingungen kommen in Routinen zum Einsatz, die folgende Struktur besitzen:
name (argument_1: TYP_1; argument_2: TYP_2):RUECKGABE_TYP is
-- Kommentar
do
... -- Implementation
end
Nach dem Namen der Routine steht in Klammern eine optionale Liste von Argumenten. Falls es sich bei der Routine um eine Funktion handelt, folgt nach einem Doppelpunkt der Typ des Rückgabewerts und schließlich das Schlüsselwort »is«. Der Kommentar einer Routine ist zwar optional, ihn einfach wegzulassen gilt aber in der Eiffel-Welt als äußerst schlechter Stil.
Die Implementation der Routine beginnt mit dem Schlüsselwort »do« und endet mit »end«. Startet sie mit »once« statt mit »do«, läuft die Routine während der Ausführung des Systems nur beim ersten Aufruf tatsächlich ab – unabhängig davon, wer der Aufrufer war. Spätere Aufrufe vom selben oder einem anderen Aufrufer zeigen keinen Effekt. Ist die Routine eine Funktion, gibt sie immer das Resultat zurück, das sie beim ersten Aufruf berechnet hat. Zudem gibt es noch die optionalen Blöcke »local«, »require« und »ensure«.
Die Zeile 12 von Listing 1 definiert die Routine »make«, die ohne Argumente und Rückgabewert auskommt. Die Prozedur ruft das Feature »set_name« mit einer Zeichenkette als Argument auf (Zeile 15). Die Routine »greeting« (Zeile 23) gibt einen »STRING« zurück. Die Spezialvariable »Result« referenziert den Rückgabewert einer Funktion. Handelt es sich dabei um einen erweiterten Typ, enthält »Result« direkt das Objekt.
»Result« wird beim Eintritt in die Funktion mit dem Standardwert des Rückgabetyps (Tabelle 1) initialisiert. Zeile 26 weist »Result« die Zusammensetzung aus »”Hello, “«, dem Rückgabewert der Query »name« und »”!”« zu. Die Syntax einer Zuweisung in Eiffel ist Variable := Wert. Das Gleichheitszeichen prüft, ob zwei Referenzen identisch sind.
|
Tabelle 1: |
|
|---|---|
|
Typ |
Initialisierungswert |
|
»INTEGER«, »REAL«, |
»0« |
|
»BOOLEAN« |
»False« |
|
»CHARACTER« |
»’%U’« (NUL) |
|
Referenztypen wie »STRING« und |
»Void« |
Verpflichtung für den Client
Zeile 31 definiert die Routine »set_name«, die ein Argument vom Typ »STRING« besitzt. Die Anweisung »require« (Zeile 33) beginnt einen Block mit Vorbedingungen; das sind Bedingungen, unter denen eine Routine wie geplant ablaufen kann. Der Client muss sie erfüllen, bevor er die Routine aufruft. Vorbedingungen sind Verpflichtungen für den Klienten und ein Nutzen für den Anbieter, der sich auf sie verlassen kann. Die Verletzung einer Vorbedingung gilt als Fehler des Klienten. Die Prozedur »set_name« darf keine Referenz auf »Void« als Argument übergeben bekommen (Listing 1, Zeile 34). Zudem darf die als Argument übergebene Zeichenkette nicht leer sein (Zeile 35). Zeile 37 weist dem Attribut »name« den Argumentwert »a_name« zu.
»ensure« (Zeile 38) leitet einen Block mit Nachbedingungen ein. Das sind Eigenschaften, die nach der Ausführung der Routine erfüllt sein müssen. Nachbedingungen unterstützen den Client und nehmen den Anbieter in die Pflicht.
Die Verletzung der Nachbedingung ist also ein Fehler des Anbieters, der nicht einhält, was er dem Klienten zugesichert hat. Die hier verwendete Bedingung soll sicherstellen, dass »name« wirklich denselben Wert wie »a_name« hat (Zeile 39), das heißt, dass beide dasselbe Objekt referenzieren.
Attribute
Zeile 20 in Listing 1 definiert das Attribut »name« vom Typ »STRING«. Der Typ eines Attributs steht durch einen Doppelpunkt getrennt hinter dem Namen. Das Schlüsselwort »is« definiert konstante Attribute:
The_answer: INTEGER is 42
-- The Answer to the Ultimate Question
Mögliche Typen sind: »INTEGER«, »REAL« (auch »DOUBLE«), »BOOLEAN« (mit den Werten »True« und »False«), »CHARACTER« (die Zeichen in einfachen Anführungszeichen: »\’X\’«) und »STRING« (Zeichenketten in Anführungszeichen: »”The Answer is 42″«).
Um die eben erstellte Klasse »HELLO« zu benutzen, modifizieren Sie die Root-Creation-Prozedur der Root-Klasse »ROOT_CLASS« entsprechend Listing 2. Das Schlüsselwort »local« beginnt einen Abschnitt, der lokale Variablen für die Routine definiert. Dabei steht der Typ der Variablen wie bei den Attributen durch einen Doppelpunkt getrennt hinter dem Namen. Zeile 15 definiert eine lokale Variable vom Typ »HELLO«.
|
Listing 2: Hello World, |
|---|
01 indexing
02 description: "System's root class"
03
04 class
05 ROOT_CLASS
06
07 create
08 make
09
10 feature -- Initialization
11
12 make is
13 -- Creation procedure.
14 local
15 hello: HELLO
16 do
17 create hello.make
18 io.put_string ("Please enter your name: ")
19 io.read_line
20 hello.set_name (io.last_string)
21 io.put_string (hello.greeting + "%N")
22 end
23
24 end -- class ROOT_CLASS
|
In den Zeilen 17 bis 21 folgt eine Sequenz von Instruktionen, die üblicherweise durch einen Zeilenumbruch voneinander getrennt sind. Sie können aber auch Semikolons verwenden, womit der Zeilenumbruch optional wird. Um auf die Features der Klasse »HELLO« von »ROOT_CLASS« aus zuzugreifen, erzeugen Sie eine Instanz der Klasse »HELLO« (Zeile 17). Die lokale Variable »hello« ist zunächst an kein Objekt gebunden. Ihr Wert ist eine Referenz auf »Void«. Mit »create hello.make« erzeugen Sie ein »HELLO«-Objekt im Speicher, verbinden die Entität »hello« mit der Adresse des Objekts und rufen dann dessen Creation-Prozedur auf.
Ein- und Ausgabe
Wie beschrieben erbt jede Klasse in Eiffel von »ANY« (Abbildung 3). »ANY« besitzt eine Funktion »io«, die eine Referenz auf ein Objekt der Klasse »STD_FILES« zurückgibt, die wiederum Features für Ein- und Ausgabe definiert. Zeile 18 (Listing 2) gibt mit dem Feature »put_string« eine Zeichenkette aus. In Zeile 19 liest »io.read_line« eine Zeile und speichert sie in »io.last_string«.
Das Feature »set_name« von »HELLO« bekommt in Zeile 20 die zuletzt gelesene Zeichenkette »io.last_string« als Argument übergeben. Zeile 21 benutzt »put_string«, um den Rückgabewert der Funktion »greeting« der Klasse »HELLO« auszugeben. »%N« ist das Spezialzeichen für einen Zeilenumbruch. Weitere Spezialzeichen sind zum Beispiel »%B« (Backspace), »%T« (Tabulator), »%%« (Prozentzeichen), »%\’« (Single Quote) und »%”« (Double Quote).
»STD_FILES« definiert neben »put_string«, »read_line« und »last_string« weitere Features zur Ein- und Ausgabe. Welche das sind, finden Sie am einfachsten mit der in Eiffelstudio verfügbaren Codekomplettierung heraus. Dazu öffnen Sie »ROOT_CLASS« im Editor und fügen am Ende von »make« eine neue, zunächst leere Zeile ein. Dort geben Sie anschließend »io.« ein und drücken [Strg]+[Leertaste]. Dann klappt ein Menü mit allen von »STD_FILES« exportierten Features auf.
Eine andere Möglichkeit ist Pick&Drop. Dazu klicken Sie mit der rechten Maustaste auf »io« (Pick), ziehen den Cursor etwas zur Seite und drücken ein weiteres Mal die rechte Maustaste, während sie sich im Editorfenster befindet (Drop). Dann zeigt Eiffelstudio den Klassentext von »ANY« an der Stelle der Definition des Features »io«. Dort können Sie mit dem Rückgabetyp von »io« genau gleich verfahren und landen schließlich beim Klassentext von »STD_FILE«.
Verletzter Vertrag
Mit [F7] oder einem Klick auf das in Abbildung 4 grün umrandete Icon kompilieren Sie das System. Wenn Sie es mit [Strg]+[F5] oder einem Klick auf das in der Abbildung rot umrandete Icon ausf&;uuml;hren, erzeugt es nach der Eingabe von »Zaphod Beeblebrox« folgende Ausgabe auf der Konsole:
Please enter your name: Zaphod Beeblebrox Hello, Zaphod Beeblebrox!
Drücken Sie auf die Frage nach Ihrem Namen nur die Eingabetaste, wird das Feature »set_name« mit einer leeren Zeichenkette als Argument aufgerufen. Die Vorbedingung des Features »set_name« der Klasse »HELLO« ist also verletzt. Klienten einer Klasse müssen Benutzereingaben prüfen, bevor sie sie verwenden. In diesem Fall soll das eine bedingte Anweisung übernehmen.
Zeile 9 von Listing 3 prüft, ob der Benutzer eine leere Zeile eingegeben hat. Falls ja, macht das Programm mit einer Fehlermeldung darauf aufmerksam (Zeile 10). Sonst setzt es die Ausführung in Zeile 11 wie in der ursprünglichen Version (also wie in Listing 2) fort. Um jedoch auf der Eingabe einer gültigen Zeichenkette zu bestehen, könnten Sie die Abfrage in eine Schleife verpacken. Eleganter ist es jedoch, die Tatsache auszunutzen, dass eine Vertragsverletzung eine Ausnahme auslöst.
|
Listing 3: |
|---|
01 make is
02 -- Creation procedure.
03 local
04 hello: HELLO
05 do
06 create hello.make
07 io.put_string ("Please enter your name: ")
08 io.read_line
09 if io.last_string.is_empty then
10 io.put_string ("Error: You entered an empty name! %N")
11 else
12 hello.set_name (io.last_string)
13 io.put_string (hello.greeting + "%N")
14 end
15 end
|
Wenn eine Routine Ausnahmen nicht abfängt, bricht sie die Ausführung ab. Behandeln kann sie eine Ausnahme in einem fakultativen »rescue«-Abschnitt, der das aktuelle Objekte in einen Zustand überführt, der die Klasseninvariante erfüllt. Der »rescue«-Abschnitt kann die »retry«-Instruktion aufrufen, die die Routine noch einmal neu startet und versucht den Vertrag zu erfüllen.
Wenn der »rescue«-Abschnitt nicht mit »retry« endet, scheitert die Ausführung der Routine und im Aufrufer tritt eine Exception auf. Danach wird der »rescue«-Abschnitt des Aufrufers genau nach den gleichen Regeln ausgeführt. Normalerweise definieren nur wenige Routinen einen »rescue«-Abschnitt. Wenn eine Routine über keinen eigenen verfügt, greift sie auf den impliziten zurück, der die Routine »default_rescue« der Klasse »ANY« aufruft.
Nun können Sie die Ausnahmebehandlung in der Routine »make« verwenden (Listing 4). Sie ist beinahe identisch zur ursprünglichen Version (Listing 2), nur der »rescue«-Abschnitt kommt hinzu (Zeile 11). Falls eine Ausnahme auftritt, gibt das Programm eine Fehlermeldung aus (Zeile 12) und führt dann mit »retry« die Routine weiter aus (Zeile 13), bis der Benutzer des Programms einen gültigen Namen eingibt.
|
Listing 4: |
|---|
01 make is
02 -- Creation procedure.
03 local
04 hello: HELLO
05 do
06 create hello.make
07 io.put_string ("Please enter your name: ")
08 io.read_line
09 hello.set_name (io.last_string)
10 io.put_string (hello.greeting + "%N")
11 rescue
12 io.put_string ("An error occured. Please try again. %N")
13 retry
14 end
|
Wenn Sie das Programm erneut in Eiffelstudio starten und dann, statt einen Namen einzugeben, direkt [Enter] drücken, verletzt das immer noch die Vorbedingung »name_valid« der Prozedur »set_name« der Klasse »HELLO«. Das geschieht, weil Sie das Programm im Debug-Modus ausführen. Starten Sie von der Kommandozeile die ausführbare Datei »hello« (im Unterordner »EIFGEN/W_code« des Projektordners) funktioniert die Ausnahmebehandlung problemlos.
Ausblick
Diese Einführung vermag natürlich kein Eiffel-Buch zu ersetzen, wohl aber anhand eines Beispiels die wichtigsten Konstrukte und Prinzipien von Eiffel zu vermitteln. Darüber hinaus haben Sie damit bereits die ersten Schritte mit der integrierten Entwicklungsumgebung Eiffelstudio absolviert.
Eiffel ist zwar eine recht übersichtliche Sprache, dennoch hat diese Einführung einige Sprachkonstrukte und Konzepte ausgelassen. So fehlen etwa Vererbung, Polymorphismus, generische Klassen und Agenten. Diese und weitere Eigenschaften wird eine künftige Ausgabe des Linux-Magazins vorstellen. (ofr)
|
Infos |
|---|
|
[1] Bertrand Meyer: [http://se.ethz.ch/~meyer] [2] ECMA-Standard 367, Eiffel Analysis, Design and Implementation Language: [http://www.ecma-international.org/publications/standards/Ecma-367.htm] [3] Eiffelstudio: [http://www.eiffel.com/downloads] [4] Eiffel Software Wiki: [http://eiffelsoftware.origo.ethz.ch] [5] Gobo Eiffel Compiler: [http://gobo-eiffel.sourceforge.net/gec/index.html] [6] Smart Eiffel: [http://smarteiffel.loria.fr] [7] Visual Eiffel: [http://www.visual-eiffel.org] [8] EDT (Eclipse Eiffel Development Tools): [http://sourceforge.net/projects/eedt/] [9] GNU Emacs oder XEmacs mit dem Eiffel-Modus: [http://www.berenddeboer.net/eiffel/eiffel_and_emacs.html] [10] Autotest: [http://se.inf.ethz.ch/people/leitner/auto_test/] |
|
Der Autor |
|---|
|
Thomas Weibel studiert Informatik und wohnt in der Schweiz. In seiner Freizeit ist er häufig auf dem Mountainbike anzutreffen, hört Musik, geht Skifahren oder Tauchen und beschäftigt sich mit Themen rund um Software Engineering. |









