Grundlagen der Informatik I “Programmierung”

Grundlagen der Informatik I “Programmierung” Grundlagen der Informatik I “Programmierung”

22.08.2013 Aufrufe

leeren Verweis (d.h. von machen Personen sind die Eltern unbekannt), da im Rechner keine unendliche Menge von Objekten verarbeitet werden können. 3.3 Routinen In den bisher betrachteten Klassen haben wir nur Attribute definiert, welche die Struktur der Objekte erklären, die in dieser Klasse zusammengefaßt werden. Die Darstellung der Exemplare eines Typs ist aber nicht die einzige Dienstleistung, die ein abstrakter Datentyp bereitstellt. Neben der Struktur der Exemplare gehören zu einem abstrakten Datentyp auch Operationen auf diesen Exemplaren, die nach außen zur Verfügung gestellt werden. Diese Operationen werden in Programmiersprachen durch sogenannte Routinen beschrieben. Es gibt zwei Arten von Routinen. • Prozeduren können durch Ausführung einer Aktion den Zustand eines Objektes ändern. • Funktionen berechnen Werte, die sich aus dem Zustand der Objekte ergeben. Dabei verstehen wir unter dem Zustand eines Objektes zu einem beliebigen Zeitpunkt der Ausführung eines Softwaresystems die Gesamtheit der Werte seiner Komponenten zu diesem Zeitpunkt. Ein Prozeduraufruf kann also die Werte von einem oder mehreren Komponenten eines Objektes ändern, während ein Funktionsaufruf einen Wert liefert, der aus den Werten der Komponenten eines Objektes berechnet wurde. 3.3.1 Aufruf von Routinen Wir hatten bereits erwähnt, daß es – von außen betrachtet – keinen Unterschied macht, ob ein Wert direkt durch den Zugriff auf eine Komponente oder durch die Berechnung einer Funktion bestimmt wird. Aus diesem Grunde verwendet Eiffel für alle Operationen, seien es nun Zugriffe auf Komponenten, Aufrufe von Funktionen oder Aufrufe von Prozeduren die gleiche Syntax, nämlich eine Punktnotation. Beispiel 3.3.1 Nehmen wir an wir hätten für die Klasse PERSON die folgenden zusätzlichen features definiert: Die Funktion anzahl vornamen soll aus der Komponente vornamen die Anzahl der darin enthaltenen Vornamen bestimmen. Die Funktion alter soll bei Eingabe einer Jahreszahl das Alter der Person berechnen, sofern diese dann noch lebt. Die Prozedur setze todesjahr soll bei Eingabe einer Jahreszahl das Todesjahr einer Person auf das angegebene Jahr setzen. Es bezeichne p ein bereits existierendes Objekt 8 der Klasse PERSON. Dann liefert • p.vornamen die aktuelle Komponente von p, welche dem Attribut vornamen entspricht, • p.anzahl vornamen die aktuelle Anzahl der Vornamen des Objektes p, • p.alter(1993) das berechnete Alter der mit p bezeichneten Person im Jahre 1993, • p.setze todesjahr(1993) eine Veränderung des Zustandes von p: die Komponente, welche dem Attribut todesjahr entspricht wird auf 1993 gesetzt. Man beachte, daß in allen 4 Fällen die gleiche Notation entity.operation(argumente) . Sie bedeutet, daß die angegebene Operation mit den Argumenten auf das durch entity (Größe) bezeichnete Objekt angewandt wird. Es ist egal, ob diese Operation eine Funktion, eine Prozedur, oder etwa nur ein Komponentenzugriff ist – nach außen sieht es immer gleich aus. Dies entspricht einem der Grundgedanken der Datenkapselung: den Benutzer geht es nichts an, wie eine Klasse ihre Dienstleistungen ausführt – nicht einmal, ob dies die Lieferung einer gespeicherten Information oder eine Berechnung ist. 8 Um genau zu sein: wenn eine Variable p ein bereits existierendes Objekt bezeichnet, dann bedeutet das gemäß der in Abschnitt 3.2.3 besprochenen Denkweise natürlich, daß sie ein Verweis auf dieses Objekt ist, wenn der Typ dieser Variablen kein einfacher Datentyp ist. Hierauf kommen wir später noch im Detail zu sprechen.

Entwurfsprinzip 3.3.2 (Punktnotation) Alle Aufrufe von Funktionen, Prozeduren und Komponentenzugriffen auf ein Objekt werden in Eiffel einheitlich in der Notation entity.operation(argumente) ausgedrückt. Eine Operation entity.operation(argumente) greift auf ein Objekt über den Verweis zu, der durch entity bezeichnet wird. Damit dies fehlerfrei geschehen kann, muß das zugehörige Objekt natürlich existieren, d.h. der zu entity gehörende Verweis darf nicht leer sein. Dies gilt für alle Operationen, insbesondere auch für Zugriffe auf Komponenten. Der Versuch, auf ein Merkmal eines leeren Verweises zuzugreifen, ist einer der häufigsten Ursachen für Laufzeitfehler, die bei der Eiffel-Programmierung auftauchen. In “klassischen” Programmiersprachen wie Pascal wird anstelle von entity.operation(argumente) die Notation operation(objekt,argumente) benutzt. Bei einer ersten Betrachtung scheint diese Form symmetrischer zu sein als die von Eiffel. Jedoch benutzt Pascal beim Zugriff auf Komponenten eines Verbundes ebenfalls die Punktnotation und verlangt also vom Benutzer, die Unterscheidung zwischen Zugriff und Funktion vorzunehmen. Eiffel ist da einheitlicher. Der spezielle Grund für die Punktnotation, bei der das Objekt am Anfang jedes Aufrufs genannt wird, ist wiederum die objektorientierte Denkweise: es ist das wichtigste zu sagen, auf welchem Objekt eine Operation ausgeführt wird. Aus diesem Grunde soll das Objekt durch die Notation besonders hervorgehoben werden. 3.3.2 Definition von Routinen Nachdem wir gesehen haben, wie man Routinen aufruft, wollen wir nun zeigen, wie man sie definiert. Dazu muß als wichtigstes herausgehoben werden, daß Routinen prinzipiell nur als Dienstleistungen einer Klasse definiert werden können und nicht etwa als unabhängige “Unterprogramme”. 9 Routinen werden daher gleichberechtigt zu den Attributen einer Klasse als Features aufgeschrieben. Abbildung 3.10 zeigt eine abstrahierte Implementierung der in Beispiel 3.3.1 benutzten Routinen innerhalb der Klasse PERSON. class PERSON feature name, vornamen: STRING; geburtsjahr, todesjahr: INTEGER; nationalität: STRING; vater, mutter: PERSON; anzahl vornamen: INTEGER is -- Anzahl der Vornamen bestimmen do Result := Anzahl der Vornamen in vornamen end; -- anzahl vornamen alter(jahr:INTEGER): INTEGER is -- Alter im gegebenen Jahr bestimmen do Result := jahr - geburtsjahr end; -- alter setze todesjahr(jahr:INTEGER) is -- todesjahr auf jahr setzen do todesjahr := jahr end -- setze todesjahr end -- class PERSON Abbildung 3.10: Klassendefinition mit Routinen Neben den Attributen name, vornamen, geburtsjahr, todesjahr, nationalität, vater und mutter enthält die Klasse PERSON drei Routinen, die man an dem Vorhandensein der Schlüsselwortfolge is...do...end erkennt. Diese Folge begrenzt den Rumpf einer Routine. Eine Routine kann formale Argumente in Klammern 9 Die im Abschnitt 3.3.4 vorgestellten Operationen, die auf allen Klassen Gültigkeit haben, sind daher in Eiffel als Dienstleistungen einer Klasse ANY realisiert, die ihre Konzepte auf alle Klassen vererbt.

leeren Verweis (d.h. von machen Personen sind die Eltern unbekannt), da im Rechner keine unendliche Menge<br />

von Objekten verarbeitet werden können.<br />

3.3 Routinen<br />

In den bisher betrachteten Klassen haben wir nur Attribute definiert, welche die Struktur <strong>der</strong> Objekte erklären,<br />

die in dieser Klasse zusammengefaßt werden. Die Darstellung <strong>der</strong> Exemplare eines Typs ist aber nicht die<br />

einzige Dienstleistung, die ein abstrakter Datentyp bereitstellt. Neben <strong>der</strong> Struktur <strong>der</strong> Exemplare gehören zu<br />

einem abstrakten Datentyp auch Operationen auf diesen Exemplaren, die nach außen zur Verfügung gestellt<br />

werden. Diese Operationen werden in Programmiersprachen durch sogenannte Routinen beschrieben. Es gibt<br />

zwei Arten von Routinen.<br />

• Prozeduren können durch Ausführung einer Aktion den Zustand eines Objektes än<strong>der</strong>n.<br />

• Funktionen berechnen Werte, die sich aus dem Zustand <strong>der</strong> Objekte ergeben.<br />

Dabei verstehen wir unter dem Zustand eines Objektes zu einem beliebigen Zeitpunkt <strong>der</strong> Ausführung eines<br />

Softwaresystems die Gesamtheit <strong>der</strong> Werte seiner Komponenten zu diesem Zeitpunkt. Ein Prozeduraufruf kann<br />

also die Werte von einem o<strong>der</strong> mehreren Komponenten eines Objektes än<strong>der</strong>n, während ein Funktionsaufruf<br />

einen Wert liefert, <strong>der</strong> aus den Werten <strong>der</strong> Komponenten eines Objektes berechnet wurde.<br />

3.3.1 Aufruf von Routinen<br />

Wir hatten bereits erwähnt, daß es – von außen betrachtet – keinen Unterschied macht, ob ein Wert direkt<br />

durch den Zugriff auf eine Komponente o<strong>der</strong> durch die Berechnung einer Funktion bestimmt wird. Aus diesem<br />

Grunde verwendet Eiffel für alle Operationen, seien es nun Zugriffe auf Komponenten, Aufrufe von Funktionen<br />

o<strong>der</strong> Aufrufe von Prozeduren die gleiche Syntax, nämlich eine Punktnotation.<br />

Beispiel 3.3.1 Nehmen wir an wir hätten für die Klasse PERSON die folgenden zusätzlichen features definiert:<br />

Die Funktion anzahl vornamen soll aus <strong>der</strong> Komponente vornamen die Anzahl <strong>der</strong> darin enthaltenen Vornamen<br />

bestimmen. Die Funktion alter soll bei Eingabe einer Jahreszahl das Alter <strong>der</strong> Person berechnen,<br />

sofern diese dann noch lebt. Die Prozedur setze todesjahr soll bei Eingabe einer Jahreszahl das Todesjahr<br />

einer Person auf das angegebene Jahr setzen.<br />

Es bezeichne p ein bereits existierendes Objekt 8 <strong>der</strong> Klasse PERSON. Dann liefert<br />

• p.vornamen die aktuelle Komponente von p, welche dem Attribut vornamen entspricht,<br />

• p.anzahl vornamen die aktuelle Anzahl <strong>der</strong> Vornamen des Objektes p,<br />

• p.alter(1993) das berechnete Alter <strong>der</strong> mit p bezeichneten Person im Jahre 1993,<br />

• p.setze todesjahr(1993) eine Verän<strong>der</strong>ung des Zustandes von p: die Komponente, welche dem Attribut<br />

todesjahr entspricht wird auf 1993 gesetzt.<br />

Man beachte, daß in allen 4 Fällen die gleiche Notation entity.operation(argumente) . Sie bedeutet, daß<br />

die angegebene Operation mit den Argumenten auf das durch entity (Größe) bezeichnete Objekt angewandt<br />

wird. Es ist egal, ob diese Operation eine Funktion, eine Prozedur, o<strong>der</strong> etwa nur ein Komponentenzugriff<br />

ist – nach außen sieht es immer gleich aus. Dies entspricht einem <strong>der</strong> Grundgedanken <strong>der</strong> Datenkapselung:<br />

den Benutzer geht es nichts an, wie eine Klasse ihre Dienstleistungen ausführt – nicht einmal, ob dies die<br />

Lieferung einer gespeicherten Information o<strong>der</strong> eine Berechnung ist.<br />

8 Um genau zu sein: wenn eine Variable p ein bereits existierendes Objekt bezeichnet, dann bedeutet das gemäß <strong>der</strong> in Abschnitt<br />

3.2.3 besprochenen Denkweise natürlich, daß sie ein Verweis auf dieses Objekt ist, wenn <strong>der</strong> Typ dieser Variablen kein einfacher<br />

Datentyp ist. Hierauf kommen wir später noch im Detail zu sprechen.

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!