Grundlagen der Informatik I “Programmierung”
Grundlagen der Informatik I “Programmierung” Grundlagen der Informatik I “Programmierung”
Viel sinnvoller ist es, bei der Beschreibung einer Klasse noch nicht an die konkrete Implementierung zu denken sondern eine möglichst abstrakte Beschreibung derjenigen Informationen zu geben, die wirklich von Interesse sind. Die Klasse der Autoren, zum Beispiel, soll uns Namen, Vornamen, Nationalität, Geburts- und Todesjahr – falls vorhanden – liefern und umgekehrt zulassen, daß das Todesjahr nachgetragen wird. Alle anderen Informationen dürfen dagegen nicht verändert werden – namensrechtliche Probleme bei Heirat seien außer Acht gelassen. Wir stehen also vor der Aufgabe, einerseits eine vollständige, genaue und eindeutige Beschreibung einer Klasse von Objekten zu geben, gleichzeitig aber eine Überspezifikation zu vermeiden. Die Antwort auf dieses Problem liegt in der Theorie der abstrakten Datentypen. 3.2.1 Abstrakte Datentypen Ausgangspunkt der Theorie der abstrakten Datentypen ist die Feststellung, daß für ein Softwaresystem, welches die Objekte einer bestimmten Klasse benutzt, weniger die konkrete Realisierung relevant ist, als die Art der Dienstleistungen, welche es bietet. So bietet zum Beispiel die Klasse der Autoren an, Namen, Vornamen, Nationalität, Geburts- und Todesjahr, und ggf. auch das Alter eines Autors abzufragen und das Todesjahr zu verändern. Wie diese Dienste realisiert werden – also ob z.B. das Alter des Autors berechnet oder gespeichert wird – ist ziemlich unbedeutend. Wichtig ist dagegen, daß diese Dienstleistungen gewisse Eigenschaften erfüllen, auf die sich das System verlassen darf, wie zum Beispiel, daß das Alter eines Autors eine ganze Zahl ist, die größer als Null ist (wahrscheinlich wäre 8 sinnvoller, aber man kann ja nie wissen). Eine abstrakte Datentypspezifikation beschreibt daher eine Klasse nicht durch die konkrete Realisierung sondern durch eine Liste von Dienstleistungen, die für die Außenwelt verfügbar sein sollen, und durch die Eigenschaften dieser Dienstleistungen. 4 Auf diese Art wird sichergestellt, daß die Außenwelt auf die Objekte einer Klasse nur mithilfe der bekanntgegebenen Dienstleistungen zugreifen kann und nicht etwa über die Implementierung(, welche sich ja möglicherweise ändern kann). Durch diese Form der Datenkapselung wird sichergestellt, daß sich jede Komponente eines Softwaresystems ausschließlich um ihre eigenen Geschäfte kümmert und in die Aufgaben der anderen nicht hineinpfuscht. Nur dieses Geheimnisprinzip garantiert die Integrität eines Moduls in einer Umwelt ständiger Veränderungen. Für eine vollständige formale Spezifikation eines abstrakten Datentyps sind vier Fragen zu beantworten: • Welche Typen werden eingeführt? • Welche Funktionen (Dienstleistungen) werden eingeführt? • Was sind die Vorbedingungen für die Anwendbarkeit der Funktionen? • Welche Axiome (Eigenschaften) erfüllen die Funktionen? Typen und Funktionen beschreiben die Syntax des abstrakten Datentyps, Vorbedingungen und Axiome seine Semantik. Diese Beschreibung wird in einer rein mathematischen Form gegeben, um die gewünschte Genauigkeit und Eindeutigkeit sicherzustellen. Wir wollen die Bedeutung dieser vier Teile am Beispiel der vollständigen Spezifikation endlicher Listen (vgl. Abschnitt 2.3.5) illustrieren. Eine Liste wird als Struktur betrachtet, die folgende Dienste zur Verfügung stellt: trage ein neues Element ein (cons), greife auf das erste Element zu (head), entferne das erste Element (tail), prüfe ob die Liste leer ist (empty), erzeuge eine neue Liste (new), usw. Listen und ihre Dienstleistungen werden in sehr vielen Anwendungen benötigt und können auf viele verschiedene Arten implementiert werden, je nachdem, ob über die obengenannte Dienstleistungen hinaus noch weitere Dienstleistungen wie z.B. das Zusammensetzen von Listen (& in Abschnitt 2.3) angeboten werden sollen oder nicht. 4 Anstelle von “Dienste” sagt man auch “Operationen” oder “Merkmale” (in Englisch “features”).
Die vollständige Spezifikation endlicher Listen ist in Abbildung 3.4 zusammengefaßt. Sie drückt alles aus, was für den Begriff der Liste von allgemeiner Bedeutung ist und läßt alles weg, was nur für bestimmte Repräsentationen von Listen gilt. TYPES: List[X] FUNCTIONS: empty: List[X] → BOOLEAN new: → List[X] cons: X×List[X] → List[X] head: List[X] → X tail: List[X] → List[X] PRECONDITIONS: pre head(L:List[X]) = (not empty(L)) AXIOMS: ∀x:X . ∀L:List[X] . empty(new()) not empty(cons(x,L)) head(cons(x,L)) = x tail(cons(x,L)) = L tail(new()) = new() Abbildung 3.4: Listen als abstrakter Datentyp • Die Namen der neuen Datentypen werden in einem Abschnitt TYPES der Spezifikation aufgelistet. Es kann sinnvoll sein, mehrere Datentypen zusammen zu spezifizieren. In unserem Fall kommt allerdings nur ein neuer Datentyp hinzu, die Liste. Dieser Typ wird eingeführt unter dem Namen “List[X]”. Das bedeutet, das es sich um einen parametrisierten oder generischen Datentyp handelt mit einem Parameter X, der einen beliebigen Datentyp darstellt, über dessen Elementen die Listen gebildet werden. 5 Die restliche Spezifikation beschreibt nun die Eigenschaften des Typs List[X], der mathematisch als Menge von Objekten (d.h. Listen) angesehen wird. • In einem Abschnitt FUNCTIONS sind die Namen der Dienstleistungen aufgeführt, die von den Exemplaren des soeben genannten Typs zur Verfügung gestellt werden. Diese werden einheitlich als mathematische Funktionen empty, new, cons, head, tail beschrieben. Zu jeder Funktion wird ihre Funktionalität (vgl. Abschnitt 2.1.3 – auch “Signatur” genannt) angegeben, in welcher der spezifizierte neue Datentyp mindestens einmal vorkommen muß. So wird zum Beispiel die Dienstleistung cons als Funktion spezifiziert, die ein Element von X und eine Liste aus List[X] in eine neue Liste abbildet: cons: X×List[X] → List[X] Die beabsichtigte Wirkung von cons, nämlich daß das Element vor die Liste gehängt werden soll, gehört zu den Eigenschaften des abstrakten Datentyps und wird im Abschnitt AXIOMS mathematisch beschrieben. Dabei interessiert sich die mathematische Beschreibung ausschließlich für das Ergebnis der Funktion, da nur auf diese Art eine präzise und überschaubare Formulierung von Eigenschaften der Dienstleistungen möglich ist. Eventuelle Seiteneffekte der Operation cons, wie zum Beispiel, daß cons in den meisten Implementierungen einem konkreten Objekt das berechnete Ergebnis zuweist, spielen hier keine Rolle. Sie sollten erst in der letzten Phase der Implementierung, nicht aber in der Spezifikation eingeführt werden. 5 Durch die Verwendung generischer Datentypen kann man sich ersparen, getrennte Spezifikationen für Listen von ganzen Zahlen, Buchstaben, oder komplexeren Datensätzen schreiben zu müssen, obwohl diese jeweils nur dieselben Dienstleistungen anbieten würden. Listen über ganzen Zahlen erhält man, indem man für X den Typ INTEGER einsetzt, also “List[INTEGER]” schreibt. Generische Datentypen werden wir in Abschnitt 3.6 ausführlicher besprechen.
- Seite 32 und 33: Rahmen zu weit führen. Auffällig
- Seite 34 und 35: Durch das Konzept des Übersetzers
- Seite 36 und 37: und “örtlich” durch Zerlegung
- Seite 38 und 39: 1.3.4 Prozeduralisierung Das Beispi
- Seite 40 und 41: Man könnte das Objektkonzept von d
- Seite 43 und 44: Kapitel 2 Logik und formale Sprachb
- Seite 45 und 46: 2.1 Formale Sprachbeschreibungen Di
- Seite 47 und 48: möglichen Alternativen auf der rec
- Seite 49 und 50: Wenn wir eine Menge von Sprachen be
- Seite 51 und 52: Da wir im folgenden den Begriff der
- Seite 53 und 54: Für die Zuordnung zwischen der Obj
- Seite 55 und 56: Diese Form der Definition findet ih
- Seite 57 und 58: K1: Kommutativ-Gesetz E1 ∧E2 ≡
- Seite 59 und 60: Axiomenschemata: L1 A ∨ ¬A L2 (A
- Seite 61 und 62: In dem Kapitel über die Programmve
- Seite 63 und 64: 2. Es werden Quantoren eingeführt,
- Seite 65 und 66: s (T, state) = wahr s (F, state) =
- Seite 67 und 68: • x ist gebunden in p(t1, ..., tn
- Seite 69 und 70: Der Wert einer Aussage mit mehreren
- Seite 71 und 72: Wichtig ist, daß die Auswahl des W
- Seite 73 und 74: Wir geben hier eine Funktion an, di
- Seite 75: wichtigste formale Sprache zur Besc
- Seite 78 und 79: Wir wollen jedoch deutlich darauf h
- Seite 80 und 81: Buch, sondern sollten besser als ei
- Seite 84 und 85: Entsprechend ihrer beabsichtigten B
- Seite 86 und 87: Klassen sind rein statische Beschre
- Seite 88 und 89: leeren Verweis (d.h. von machen Per
- Seite 90 und 91: haben wie z.B. “(jahr:INTEGER)”
- Seite 92 und 93: und diese erzeugten Objekte mit Ver
- Seite 94 und 95: Bei der Erzeugung von Objekten mitt
- Seite 96 und 97: über den Namen eines Attributs Wer
- Seite 98 und 99: • Die Veränderung und Auswertung
- Seite 100 und 101: 3.5 Copy- und Referenz-Semantik In
- Seite 102 und 103: 3.5.2 expanded: Klassen mit Copy-Se
- Seite 104 und 105: class LIST[X] creation new feature
- Seite 106 und 107: Dieses Problem wird durch das Konze
- Seite 108 und 109: 3.7.1 Zusicherungen Eiffel logische
- Seite 110 und 111: class ARRAY[X] creation make featur
- Seite 112 und 113: class ARRAY[X] creation make featur
- Seite 114 und 115: formulieren und zu überwachen. Eig
- Seite 116 und 117: 3.8.2 Export geerbter Features Norm
- Seite 118 und 119: Ist also p ein Personenobjekt und a
- Seite 120 und 121: Definition 3.8.5 (Konformität) Ein
- Seite 122 und 123: Um die Beschreibung des Typsystems
- Seite 124 und 125: Nachkommenklassen aber folgt entity
- Seite 126 und 127: deferred class LIST[X] feature leng
- Seite 128 und 129: class STUDENT feature universität:
- Seite 130 und 131: Das Problem ist nun, daß beide Elt
Die vollständige Spezifikation endlicher Listen ist in Abbildung 3.4 zusammengefaßt. Sie drückt alles aus,<br />
was für den Begriff <strong>der</strong> Liste von allgemeiner Bedeutung ist und läßt alles weg, was nur für bestimmte<br />
Repräsentationen von Listen gilt.<br />
TYPES: List[X]<br />
FUNCTIONS: empty: List[X] → BOOLEAN<br />
new: → List[X]<br />
cons: X×List[X] → List[X]<br />
head: List[X] → X<br />
tail: List[X] → List[X]<br />
PRECONDITIONS: pre head(L:List[X]) = (not empty(L))<br />
AXIOMS: ∀x:X . ∀L:List[X] .<br />
empty(new())<br />
not empty(cons(x,L))<br />
head(cons(x,L)) = x<br />
tail(cons(x,L)) = L<br />
tail(new()) = new()<br />
Abbildung 3.4: Listen als abstrakter Datentyp<br />
• Die Namen <strong>der</strong> neuen Datentypen werden in einem Abschnitt TYPES <strong>der</strong> Spezifikation aufgelistet. Es<br />
kann sinnvoll sein, mehrere Datentypen zusammen zu spezifizieren. In unserem Fall kommt allerdings<br />
nur ein neuer Datentyp hinzu, die Liste. Dieser Typ wird eingeführt unter dem Namen “List[X]”. Das<br />
bedeutet, das es sich um einen parametrisierten o<strong>der</strong> generischen Datentyp handelt mit einem Parameter<br />
X, <strong>der</strong> einen beliebigen Datentyp darstellt, über dessen Elementen die Listen gebildet werden. 5<br />
Die restliche Spezifikation beschreibt nun die Eigenschaften des Typs List[X], <strong>der</strong> mathematisch als<br />
Menge von Objekten (d.h. Listen) angesehen wird.<br />
• In einem Abschnitt FUNCTIONS sind die Namen <strong>der</strong> Dienstleistungen aufgeführt, die von den Exemplaren<br />
des soeben genannten Typs zur Verfügung gestellt werden. Diese werden einheitlich als mathematische<br />
Funktionen empty, new, cons, head, tail beschrieben. Zu je<strong>der</strong> Funktion wird ihre Funktionalität<br />
(vgl. Abschnitt 2.1.3 – auch “Signatur” genannt) angegeben, in welcher <strong>der</strong> spezifizierte neue<br />
Datentyp mindestens einmal vorkommen muß.<br />
So wird zum Beispiel die Dienstleistung cons als Funktion spezifiziert, die ein Element von X und eine<br />
Liste aus List[X] in eine neue Liste abbildet:<br />
cons: X×List[X] → List[X]<br />
Die beabsichtigte Wirkung von cons, nämlich daß das Element vor die Liste gehängt werden soll,<br />
gehört zu den Eigenschaften des abstrakten Datentyps und wird im Abschnitt AXIOMS mathematisch<br />
beschrieben. Dabei interessiert sich die mathematische Beschreibung ausschließlich für das Ergebnis <strong>der</strong><br />
Funktion, da nur auf diese Art eine präzise und überschaubare Formulierung von Eigenschaften <strong>der</strong><br />
Dienstleistungen möglich ist. Eventuelle Seiteneffekte <strong>der</strong> Operation cons, wie zum Beispiel, daß cons<br />
in den meisten Implementierungen einem konkreten Objekt das berechnete Ergebnis zuweist, spielen hier<br />
keine Rolle. Sie sollten erst in <strong>der</strong> letzten Phase <strong>der</strong> Implementierung, nicht aber in <strong>der</strong> Spezifikation<br />
eingeführt werden.<br />
5 Durch die Verwendung generischer Datentypen kann man sich ersparen, getrennte Spezifikationen für Listen von ganzen<br />
Zahlen, Buchstaben, o<strong>der</strong> komplexeren Datensätzen schreiben zu müssen, obwohl diese jeweils nur dieselben Dienstleistungen<br />
anbieten würden. Listen über ganzen Zahlen erhält man, indem man für X den Typ INTEGER einsetzt, also “List[INTEGER]”<br />
schreibt. Generische Datentypen werden wir in Abschnitt 3.6 ausführlicher besprechen.