Grundlagen der Informatik I “Programmierung”
Grundlagen der Informatik I “Programmierung” Grundlagen der Informatik I “Programmierung”
Ist also p ein Personenobjekt und a ein Arbeitnehmerobjekt, so ist der Aufruf a.name, a.vornamen genauso erlaubt wie p.name, p.vornamen. Darüber hinaus kann man allerdings auch a.adresse und a.gehalt aufrufen, nicht jedoch p.adresse oder p.gehalt Dies gilt natürlich nur dann, wenn die geerbtes features nicht durch export-Klauseln von der Weiterverwendung ausgeschlossen werden. Diese Eigenschaft läßt sich als eine erste wichtige Regel des Typsystems von Eiffel beschreiben. Entwurfsprinzip 3.8.2 (Regel der Featureanwendung) Ist eine Größe entity vom Klassentyp K, so ist die Anwendung entity.f eines feature f nur dann zulässig, wenn f in einem Vorfahren von K definiert wurde. Man beachte, daß diese Regel “nur dann” und nicht etwa “genau dann” besagt. Die Voraussetzung, daß ein feature in einem Vorfahren von K (d.h. eventuell auch in K selbst) definiert wurde, ist notwendig für eine Anwendung, reicht aber nicht immer aus – zum Beispiel, weil das feature durch die Klasse K nicht oder nur selektiv exportiert wurde. Die Interpretation des selektiven Exports von features wird allerdings durch die Vererbung etwas aufgeweicht. Da Erben alle Rechte ihrer Vorfahren besitzten, haben sie natürlich auch alle Zugriffsrechte. Das bedeutet, daß ein selektiv an eine Klasse exportiertes feature auch innerhalb aller Erbenklassen zur Verfügung steht – selbst, wenn diese nicht explizit in der export- bzw. feature-Klausel genannt sind. Entwurfsprinzip 3.8.3 (Regel des selektiven Exports) Ein an eine Klasse K selektiv exportiertes feature f darf in allen Nachkommen von K benutzt werden. In den folgenden Abschnitten werden wir weitere Regeln des Typsystems von Eiffel vorstellen. All diese Regeln sind statischer Natur, d.h. sie können ausschließlich auf der Basis des Programmtextes überprüft werden 23 . Der Compiler wird Klassen abweisen, die eine dieser Regeln verletzen. Auf diese Art wird sichergestellt, daß es während der Laufzeit eines Systems nicht mehr zu Typfehlern kommen kann. 3.8.3 Redefinition So wie es Gründe dafür gibt, geerbte features selektiv an verschiedene Klassen zu exportieren, gibt es auch Gründe, ihre Implementierung zu verändern. So wird zum Beispiel bei Universitätsangestellten das Gehalt nicht alleine aufgrund der Gehaltsklasse bestimmt, sondern hängt zusätzlich vom tatsächlichen Alter der Angestellten ab ab. Das bedeutet, daß die Funktion gehalt bei Universitätsangestellten nach einem anderen Verfahren berechnet werden sollte als im Allgemeinfall. Um dies zu ermöglichen, erlaubt Eiffel, geerbte features neu zu definieren. Hierzu wird in einer redefine Klausel angegeben, welche features verändert werden. Natürlich muß jetzt jedes dieser features erneut als feature der Klasse deklariert und implementiert werden. Abbildung 3.32 enthält eine Deklaration der Klasse aller Universitätsangestellten, in der das feature gehalt neudefiniert wurde. Ohne die redefine-Klausel wäre die erneute Deklaration von gehalt als Merkmal von UNI-ANGESTELLTE ein Fehler, da gehalt bereits aus der Klasse ARBEITNEHMER übernommen wird. Die redefine-Klausel teilt mit, welche features einer Elternklasse in einer Erbenklasse neudefiniert werden 24 . Hierdurch wird gewährleistet, daß derselbe Name sich auf verschiedene aktuelle features beziehen kann. Abhängig vom Typ des Objektes, auf das es angewandt wird, erhält man eine andere Implementierung. 23 Für fast alle Regeln gibt es im Referenzbuch [Meyer, 1992] verfeinerte Formen. Diese behandeln jedoch auch alle Spezialfälle, die sich aus Kombinationen diverser Möglichkeiten ergeben und sind zur Erklärung des eigentlichen Prinzip ungeeignet. 24 Der Compiler benötigt die redefine-Klausel eigentlich nicht, weil jede erneute Deklaration eines ererbten features ja nur eine Redefinition sein kann, sofern kein Fehler des Programmierers vorliegt. Da aber irrtümliche Redefinitionen bei umfangreichen Ahnenklassen nicht unbedingt ausgeschlossen werden können, muß Redefinition ausdrücklich als solche gekennzeichnet werden. Dies erhöht die Sicherheit und Lesbarkeit der Programme. Es sei an dieser Stelle auch erwähnt, daß ein feature durch das Schlüsselwort frozen gegen Redefinition sperren kann, wenn man darauf Wert legt, allen Benutzen eine eine unveränderliche Implementierung bereitzustellen.
class UNI-ANGESTELLTE inherit ARBEITNEHMER redefine gehalt export {ARBEITGEBER} name, vorname, geburtsjahr, adresse, gehaltsklasse; {FINANZAMT} all end -- feature-Anpassung von ARBEITNEHMER feature universität: STRING; fachbereich, raum, telephon: INTEGER; gehalt: REAL is -- Gehalt berechnen do Result := Gehalt nach Alter und gehaltsklasse end end -- class UNI-ANGESTELLTE Abbildung 3.32: Vererbung mit Redefinition Natürlich kann man diese nicht beliebig abändern. Von außen betrachtet muß ein feature im wesentlichen unverändert, d.h. auf die gleiche Art aufrufbar und veränderbar bleiben. Kurzum, die Vereinbarungen der Elternklasse müssen weiterhin Gültigkeit haben – ansonsten sollte man besser ein neues feature deklarieren. Zu den Vereinbarungen, die von der Erbenklasse übernommen werden müssen, gehören insbesondere die Anzahl der Argumente und die Typen der features. Routinen müssen Routinen bleiben und Attribute Attribute. Die einzigen erlaubten Ausnahmen sind ein Wechsel von parameterlosen Funktionen zu Attributen – weil hier der Kunde den Unterschied nicht sieht – und ein Übergang zu Typen, die zum ursprünglichen Datentyp passen (“konform sind”). Entwurfsprinzip 3.8.4 (Regel der Redefinition) Ein in einer Klasse deklariertes Attribut, Funktionsergebnis oder formales Routinenargument darf in einer Erbenklasse mit einem neuen Typ redeklariert werden, wenn der neue Typ konform zum ursprünglichen ist. Das Attribut oder die zugehörige Routine gilt als redefiniert. Der Rumpf einer Routine kann redefiniert werden, solange die obige Typeinschränkung nicht verletzt wird. Parameterlose Funktionen dürfen dabei als Attribute redefiniert werden. Redefinierte features, die nicht ursprünglich deferred (siehe Abschnitt 3.8.6) waren, müssen in einer entsprechenden redefine Klausel aufgeführt werden. Die Idee hinter dieser Regelung ist, daß eine Klasse immer eine speziellere Version eines in einer Ahnenklasse deklarierten Elementes anbieten kann. Deshalb ist auch eine Redefinition eines Attributes als parameterlose Funktion nicht erlaubt, weil hierdurch die Zuweisung von Werten an das Attribut unmöglich gemacht wird 25 . Die Redefinitionsregel verwendet den Begriff der Konformität zwischen zwei Datentypen. “B ist konform zu A” bedeutet in erster Näherung, daß B ein Nachkomme von A sein muß. Dies gilt in ähnlicher Form auch für den Fall, daß A und B generische Parameter enthalten. Alle in B vorkommenden aktuellen generischen Parameter müssen Nachkommen der entsprechenden Parameter in A sein. So kann zum Beispiel B die Klasse TWO WAY LIST[ENTLEIHER] sein und A die Klasse LINKED LIST[PERSON], denn die generische Klasse TWO WAY LIST ist Nachkomme von LINKED LIST und der aktuelle Parameter ENTLEIHER ein Nachkomme von PERSON. Die präzise Definition ist etwas komplizierter, da B auch in der Vererbungsklausel eine Klasse mit generischen Parametern nennen kann, die A als Vorfahren besitzt, das Konzept der Deklaration durch Assoziation hinzukommt, das wir erst in Abschnitt 3.8.5 besprechen werden, und Konformität transitiv ist. 25 Bei einer Redefinition eines Attributes als parameterlose Funktion dürfte auch die Ahnenklasse keine Zuweisung an das Attribut mehr enthalten, da Größen dieser Klasse ja auch Objekte einer Nachkommenklasse bezeichnen können – Personengrößen dürfen zum Beispiel auch auf spezielle Personen, nämlich Entleiher zeigen. Damit würde die Redefinition aber einen Eingriff in die Elternklasse mit sich bringen, was völlig gegen den Sinn der Strukturierung eines Systems in unabhängige Klassen ist.
- 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 82 und 83: Viel sinnvoller ist es, bei der Bes
- 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 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
- Seite 132 und 133: Prinzipiell wäre es sogar möglich
- Seite 134 und 135: Das bedeutet also, daß die Erbenkl
- Seite 136 und 137: Die eigentliche Montage eines Syste
- Seite 138 und 139: Verständlichkeit der Module: Die L
- Seite 140 und 141: dynamische Semantik: Die dynamische
- Seite 142 und 143: Constant ::= Manifest constant | Id
- Seite 145 und 146: Kapitel 4 Systematische Entwicklung
- Seite 147 und 148: 4.1.2 Grundideen des objektorientie
- Seite 149 und 150: jedoch dringend zu empfehlen, jegli
- Seite 151 und 152: • Ausleihe - Bücher werden nach
- Seite 153 und 154: Der Anwender ist kein echter Klient
- Seite 155 und 156: Es sei an dieser Stelle angemerkt,
- Seite 157 und 158: einen Rechner überprüft werden ka
- Seite 159 und 160: Definition 4.2.3 (Korrektheit von R
- Seite 161 und 162: Programmkonstruktion keine Rolle, d
- Seite 163 und 164: Eine Wertzuweisung entity := Ausdru
- Seite 165 und 166: 4.3.2.1 Die Rolle formaler Paramete
- Seite 167 und 168: Für eine korrekt implementierte Pr
Ist also p ein Personenobjekt und a ein Arbeitnehmerobjekt, so ist <strong>der</strong> Aufruf a.name, a.vornamen<br />
genauso erlaubt wie p.name, p.vornamen. Darüber hinaus kann man allerdings auch a.adresse<br />
und a.gehalt aufrufen, nicht jedoch p.adresse o<strong>der</strong> p.gehalt<br />
Dies gilt natürlich nur dann, wenn die geerbtes features nicht durch export-Klauseln von <strong>der</strong><br />
Weiterverwendung ausgeschlossen werden.<br />
Diese Eigenschaft läßt sich als eine erste wichtige Regel des Typsystems von Eiffel beschreiben.<br />
Entwurfsprinzip 3.8.2 (Regel <strong>der</strong> Featureanwendung)<br />
Ist eine Größe entity vom Klassentyp K, so ist die Anwendung entity.f eines feature f nur dann<br />
zulässig, wenn f in einem Vorfahren von K definiert wurde.<br />
Man beachte, daß diese Regel “nur dann” und nicht etwa “genau dann” besagt. Die Voraussetzung, daß ein<br />
feature in einem Vorfahren von K (d.h. eventuell auch in K selbst) definiert wurde, ist notwendig für eine<br />
Anwendung, reicht aber nicht immer aus – zum Beispiel, weil das feature durch die Klasse K nicht o<strong>der</strong> nur<br />
selektiv exportiert wurde.<br />
Die Interpretation des selektiven Exports von features wird allerdings durch die Vererbung etwas aufgeweicht.<br />
Da Erben alle Rechte ihrer Vorfahren besitzten, haben sie natürlich auch alle Zugriffsrechte. Das bedeutet,<br />
daß ein selektiv an eine Klasse exportiertes feature auch innerhalb aller Erbenklassen zur Verfügung steht –<br />
selbst, wenn diese nicht explizit in <strong>der</strong> export- bzw. feature-Klausel genannt sind.<br />
Entwurfsprinzip 3.8.3 (Regel des selektiven Exports)<br />
Ein an eine Klasse K selektiv exportiertes feature f darf in allen Nachkommen von K benutzt werden.<br />
In den folgenden Abschnitten werden wir weitere Regeln des Typsystems von Eiffel vorstellen. All diese Regeln<br />
sind statischer Natur, d.h. sie können ausschließlich auf <strong>der</strong> Basis des Programmtextes überprüft werden 23 .<br />
Der Compiler wird Klassen abweisen, die eine dieser Regeln verletzen. Auf diese Art wird sichergestellt, daß<br />
es während <strong>der</strong> Laufzeit eines Systems nicht mehr zu Typfehlern kommen kann.<br />
3.8.3 Redefinition<br />
So wie es Gründe dafür gibt, geerbte features selektiv an verschiedene Klassen zu exportieren, gibt es auch<br />
Gründe, ihre Implementierung zu verän<strong>der</strong>n. So wird zum Beispiel bei Universitätsangestellten das Gehalt<br />
nicht alleine aufgrund <strong>der</strong> Gehaltsklasse bestimmt, son<strong>der</strong>n hängt zusätzlich vom tatsächlichen Alter <strong>der</strong><br />
Angestellten ab ab. Das bedeutet, daß die Funktion gehalt bei Universitätsangestellten nach einem an<strong>der</strong>en<br />
Verfahren berechnet werden sollte als im Allgemeinfall. Um dies zu ermöglichen, erlaubt Eiffel, geerbte features<br />
neu zu definieren. Hierzu wird in einer redefine Klausel angegeben, welche features verän<strong>der</strong>t werden. Natürlich<br />
muß jetzt jedes dieser features erneut als feature <strong>der</strong> Klasse deklariert und implementiert werden.<br />
Abbildung 3.32 enthält eine Deklaration <strong>der</strong> Klasse aller Universitätsangestellten, in <strong>der</strong> das feature gehalt<br />
neudefiniert wurde. Ohne die redefine-Klausel wäre die erneute Deklaration von gehalt als Merkmal von<br />
UNI-ANGESTELLTE ein Fehler, da gehalt bereits aus <strong>der</strong> Klasse ARBEITNEHMER übernommen wird.<br />
Die redefine-Klausel teilt mit, welche features einer Elternklasse in einer Erbenklasse neudefiniert werden 24 .<br />
Hierdurch wird gewährleistet, daß <strong>der</strong>selbe Name sich auf verschiedene aktuelle features beziehen kann.<br />
Abhängig vom Typ des Objektes, auf das es angewandt wird, erhält man eine an<strong>der</strong>e Implementierung.<br />
23 Für fast alle Regeln gibt es im Referenzbuch [Meyer, 1992] verfeinerte Formen. Diese behandeln jedoch auch alle Spezialfälle,<br />
die sich aus Kombinationen diverser Möglichkeiten ergeben und sind zur Erklärung des eigentlichen Prinzip ungeeignet.<br />
24 Der Compiler benötigt die redefine-Klausel eigentlich nicht, weil jede erneute Deklaration eines ererbten features ja nur eine<br />
Redefinition sein kann, sofern kein Fehler des Programmierers vorliegt. Da aber irrtümliche Redefinitionen bei umfangreichen<br />
Ahnenklassen nicht unbedingt ausgeschlossen werden können, muß Redefinition ausdrücklich als solche gekennzeichnet werden.<br />
Dies erhöht die Sicherheit und Lesbarkeit <strong>der</strong> Programme.<br />
Es sei an dieser Stelle auch erwähnt, daß ein feature durch das Schlüsselwort frozen gegen Redefinition sperren kann, wenn<br />
man darauf Wert legt, allen Benutzen eine eine unverän<strong>der</strong>liche Implementierung bereitzustellen.