Aus Linux-Magazin 09/2006

Workshop: Eiffel programmieren mit Eiffelstudio

© photocase.com

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:

  • Der Gobo Eiffel Compiler [5] befindet sich allerdings noch in
    einem sehr frühen Entwicklungsstadium.
  • Das Entwicklungsteam von Smart Eiffel [6] akzeptiert den
    ECMA-Standard nicht und entwickelt eine eigene Eiffel-Version.
  • Visual Eiffel [7] wurde Anfang 2005 unter der GPL freigegeben,
    entwickelt sich aber seither kaum weiter.

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.

Abbildung 1: Kategorisierung von Features nach Aufgabe und Implementation.

Abbildung 1: Kategorisierung von Features nach Aufgabe und Implementation.

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.

Abbildung 2: Die vier Hauptbereiche der Benutzeroberfläche von Eiffelstudio.

Abbildung 2: Die vier Hauptbereiche der Benutzeroberfläche von Eiffelstudio.

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,
»HELLO«

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
Kleinschreibung

Von Eiffel-Programmierern verwendete Konventionen zur Groß- und Kleinschreibung:

  • Klassennamen stehen in Großbuchstaben:
    »HELLO«, »LINKED_LIST« …
  • Namen von Attributen, Routinen und so weiter in
    Kleinbuchstaben: »greeting«, »set_name«,
    »i« …
  • Konstante Attribute beginnen mit einem Großbuchstaben,
    danach folgen Kleinbuchstaben: »Welcome_message: STRING is
    “Hello, World!”«
  • Einige Schlüsselwörter haben ebenfalls einen
    Großbuchstaben am Anfang, da sie ähnlich wie Konstanten
    sind: »Current«, »Result«,
    »Precursor«, »True«,
    »False«

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).

Abbildung 3: An der Spitze der Vererbungshierarchie von Eiffel steht die Klasse »ANY«.

Abbildung 3: An der Spitze der Vererbungshierarchie von Eiffel steht die Klasse »ANY«.

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:
Standardwerte für die Initialisierung von Typen

 

Typ

Initialisierungswert

»INTEGER«, »REAL«,
»DOUBLE«

»0«

»BOOLEAN«

»False«

»CHARACTER«

»’%U’« (NUL)

Referenztypen wie »STRING« und
»HELLO«

»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,
»ROOT_CLASS«

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«.

Gustave Eiffel


Der Name der Programmiersprache ist eine Reverenz an Gustave Eiffel, der unter anderem das Metallgerüst der Freiheitsstatue in New York und den Eiffelturm in Paris gebaut hat. Das Pariser Wahrzeichen wurde zur Weltausstellung 1889 fertig gestellt. Es besteht aus über 18000 Einzelmodulen, die Gustave Eiffel in seiner Fabrik vorbereiten ließ. Arbeiter setzten aus diesen Elementen den Turm vor Ort zusammen. Der Eiffelturm ist laut Bertrand Meyer das beste Symbol für das, was man mit Eiffel in der Softwarewelt erreichen kann.

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:
»ROOT_CLASS« mit bedingter Anweisung

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:
»ROOT_CLASS« mit Ausnahmebehandlung

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.

LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben