Eiffel ist eine objektorientierte Programmiersprache von
Bertrand Meyer. Sie wurde in den 80ern entworfen - ist also relativ alt.
Features:
- multiple inheritance
- statisches typsystem
- garbage collection
- multi-platform inklusuve GUI-Library
- .NET-fähig
- Uniform access principle
- Exceptions (wenn auch ein bischen anders).
- Agenten (= mächtigere Delegaten)
- und das aller wichtigste: DESIGN BY CONTRACT
Fangen wir mal mit dem wichtigsten an: Design by Contract oder auch DbC. Design by Contract ermöglicht es einem in einer Klasse einen Contract zu definieren. Dieser Contract gibt an was für die Klasse unveränderlich ist (invarianten - müssen vor und nach einer Methode gelten) und für jede einzelne Methode was sie zusichert (postcondition) und was sie benötigt (precondition). Diese Kontrakte sind dabei Teil der Dokumentation.
Ein Beispiel (von mir):
Code:
indexing
description: "Objects that represents a die"
author: "Tobias Langner"
date: "$Date$"
revision: "$Revision$"
class
MULTI_DIE
create
make
feature -- Access
d(side : INTEGER) : INTEGER is
-- liest den letzten Wurf als d(side) aus
require
side_positive: side>0
do
Result:=(rng.real_item*(side)).floor+1
ensure
lower_bound: Result>=1
upper_bound: Result<=side
end
feature -- Basic operations
roll is
-- wirft den würfel
do
rng.forth
ensure
rng.item/=1
end
feature -- Creation
make is
-- initialisiert den Würfel
local
temp_date : DATE_TIME
do
create temp_date.make_now
create rng.set_seed(temp_date.seconds)
ensure
rng /= Void
end
feature {NONE} -- Implementation
rng : RANDOM
invariant
rng_not_void: rng /= Void
end
- indexing -> Beschreibung der Klasse
class -> Klassenname
- inherit (hier nicht vorhanden) -> Liste der Klassen von denen man erbt und welche Klassen Zugriff auf die geerbten "features" haben sollen
- create -> Liste der Konstruktoren. Dabei sind Konstruktoren normale Methoden die zur Erstellung verwendet werden können
- feature -> Methodenabschnitt. Dabei kann in {} angegeben werden welche Klassen Zugriff auf die Features haben sollen. Ohne bedeutet jeder (=public), {NONE} bedeutet alle die von {NONE} abgeleitet sind. Da NONE eine spezielle Klasse ist von der keiner abgeletet sein darf (sie ist die Klasse die von allen abgeleitet ist) bedeutet das private. Alle anderen Angaben wären dann friends.
- invariants -> Bedinungen die vor jedem und nach jedem Methodenaufruf gelten müssen.
- requrie -> Preconditions. Müssen bei jedem Aufruf der Methode gelten
- ensure -> Postconditions. Wird von der Methode zugesichert. Müssen gelten wenn die Preconditions erfüllt waren.
Im Beispiel - ein Würfel für Rollenspiele - muss der Methode "d" eine positive Zahl übergeben werden. Dies erwartet sie. Dafür sichert sie zu, dass das Würfelergebnis zwischen 1 und dem übergebenen Wert liegt. Für diese Pre/Postconditions und Invarianten kann man im Compiler einstellen ob sie überprüft werden sollen (Debug-Phase) oder nicht (Release). Für Librarys kann man sich überlegen ob man die Preconditions eincompiliert (geht sogar Klassenweise glaube ich) um den Aufrufer auf Probleme in seinen Aufrufen hinzuweisen. Eine Verletzung löst eine Exception aus.
Zum Thema Benutzerfreundlichkeit: Eiffel benötigt für sein DbC die sogenannte "Command-Query-Separation", d.h. eine Klasse darf entweder einen Wert abfragen (Query) oder aber den Zustand der Klasse verändern (Command). Bei mir oben wurde das so realisiert, dass "roll" den würfel rollt (Command) und damit den Status der Klasse ändert und "d" das Ergebnis abfragt (Query). Mehrfacher aufruf von "d" liefert immer das gleiche Ergebnis solange kein "roll" dazwischen kommt. Das ist gewöhnungsbedürftig. DbC selber ist ein sehr gutes Werkzeug zur Entwicklungszeit Fehler zu finden. Das wichtigste von DbC im vergleich zu den "low-level" Assertions ist - sie werden mit vererbt!!. Ich darf in einer überschriebenen Methode die Preconditions nur aufweichen und die Postconditions nur erhärten. Das ist logisch, da ich ja über die Vererbung verspreche, dass man die neue Klasse auch an der Stelle verwenden kann wo man die Vater-Klasse verwenden kann. Preconditions kann ich durch mehr Eingabechecks aufweichen - Postconditions kann ich nur erhärten dadurch das ich mehr garantiere.
Die Multiple-Inheritance in Eiffel ist sehr gut realisiert - da man featurs auch renamen kann und bestimmen kann welches feature bei dem Diamant Problem bei Multiple Inheritance aufgerufen wird. Generics sind auch klar - sehr wichtig für statische Typen. Die Agenten selber sind etwas mächtiger als Delegaten. Beispiel: ich hab ein Event das 2 Parameter übergibt (z.B. MausX und MausY). Das soll eine 3 Parametrige funktion aufrufen, der 3te Parameter fest. Dann übergebe ich einfach den agenten my_func(?,?,23) und schon werden die Parameter entsprechend eingesetzt und der letzte bleibt bei diesem Event auf 23.
Fazig: interessant auf jeden Fall - für jeden der ernsthaft sich mit den Konzepten der Objektorientierung auseinandersetzen will. Gute Mechanismen um Qualität zu sichern. Nichts für Rapid Prototyping (da dann eher so was wie Smalltalk nehmen). Und wer sich mal
"Object-Oriented Software Construction" anschaut (sehr gutes Buch zu den theoretischen Grundlagen der Objektorientierung - warum ist alles so wie es ist), der kommt um Eiffel nicht rum.