Klassendefinitionen
Die Ausgabe des Liedes „Auf einem Baum ein Kuckuck saß“ kann in zwei Routinen aufgeteilt werden: Die erste Routine "Refrain ausgeben" ist für die Ausgabe des Refrains zuständig und die zweite Routine "Lied ausgeben" ist für die Ausgabe des gesamten Liedes zuständig. Dabei benutzt die zweite Routine die erste.
Die Routinen-Sammlung „Lieder ausgeben“ enthält Routinen zur Ausgabe der Anfänge zweier Lieder, nämlich des Liedes „Auf einem Baum ein Kuckuck saß“ und des Liedes „Ein Vogel wollte Hochzeit machen“.
Lieder ausgeben [Struktogramm DIN 66261 - A]Kuckuck-Refrain ausgeben
.--------------------------------------------------------------------------------.
| Zeile "Sim sa la dim, bam ba," ausgeben |
|--------------------------------------------------------------------------------|
| Zeile "Sa la du, sa la dim -" ausgeben |
'--------------------------------------------------------------------------------'
Kuckuck-Lied ausgeben
.--------------------------------------------------------------------------------.
| |
| .----------------------------------------------------------------------------. |
| | Zeile "Auf einem Baum ein Kuckuck, -" ausgeben | |
| |----------------------------------------------------------------------------| |
| | Block "Kuckuck-Refrain ausgeben" aufrufen | |
| |----------------------------------------------------------------------------| |
| | Zeile "Auf einem Baum ein Kuckuck sass." ausgeben | |
| '----------------------------------------------------------------------------' |
|--------------------------------------------------------------------------------|
| |
| .----------------------------------------------------------------------------. |
| | Zeile "Da kam ein junger Jaeger, -" ausgeben | |
| |----------------------------------------------------------------------------| |
| | Block "Kuckuck-Refrain ausgeben" aufrufen | |
| |----------------------------------------------------------------------------| |
| | Zeile "Da kam ein junger Jaegersmann." ausgeben | |
| '----------------------------------------------------------------------------' |
|--------------------------------------------------------------------------------|
| |
| .----------------------------------------------------------------------------. |
| | Zeile "Der schoss den armen Kuckuck, -" ausgeben | |
| |----------------------------------------------------------------------------| |
| | Block "Kuckuck-Refrain ausgeben" aufrufen | |
| |----------------------------------------------------------------------------| |
| | Zeile "Der schoss den armen Kuckuck tot." ausgeben | |
| '----------------------------------------------------------------------------' |
'--------------------------------------------------------------------------------'
Hochzeit-Refrain ausgeben
.--------------------------------------------------------------------------------.
| Zeile "Fi-di-ra-la-la, fi-di-ra-la-la, fi-di-ra-la-la-la-la!" ausgeben |
'--------------------------------------------------------------------------------'
Hochzeit-Lied ausgeben
.--------------------------------------------------------------------------------.
| |
| .----------------------------------------------------------------------------. |
| | Zeile "Ein Vogel wollte Hochzeit machen, in dem gruenen Walde." ausgeben | |
| |----------------------------------------------------------------------------| |
| | Block "Hochzeit-Refrain ausgeben" aufrufen | |
| '----------------------------------------------------------------------------' |
|--------------------------------------------------------------------------------|
| |
| .----------------------------------------------------------------------------. |
| | Zeile "Die Drossel war der Braeutigam, die Amsel war die Braute." ausgeben | |
| |----------------------------------------------------------------------------| |
| | Block "Hochzeit-Refrain ausgeben" aufrufen | |
| '----------------------------------------------------------------------------' |
|--------------------------------------------------------------------------------|
| |
| .----------------------------------------------------------------------------. |
| | Zeile "Die Lerche, die Lerche, die fuehrt die Braut zur Kerche." ausgeben | |
| |----------------------------------------------------------------------------| |
| | Block "Hochzeit-Refrain ausgeben" aufrufen | |
| '----------------------------------------------------------------------------' |
'--------------------------------------------------------------------------------'
Die Routinen zur Ausgabe des Refrains mußten diesmal allerdings etwas umständlicher benannt werden, denn sie dürfen nicht beide denselben Namen "Refrain ausgeben" haben.
Ein gut strukturierte Darstellung sollte schon an ihrer Oberfläche die inhaltliche Struktur erkennen lassen. Die vier Routinen stehen aber gleichberechtigt nebeneinandern, ohne daß es erkennbar wird, daß jeweils zwei von Ihnen zusammengehören.
Um die Zusammengehörigkeit je zweier Routinen besser zum Ausdruck zu bringen, können sie zu einer Klasse zusammgefaßt werden.
Eine Klasse enthält zusammengehörige Routinen.
So entsteht die Klasse "Auf_einem_Baum_ein_Kuckuck_sass" mit den beiden Routinen, die zu dem Lied „Auf einem Baum ein Kuckuck saß“ gehören. Sie enthält also die Routine "Kuckuck_Refrain_ausgeben" und die Routine "Kuckuck_ausgeben". Diese beiden Routinen werden als Elemente dieser Klasse angesehen und ihre Namen sind Einträge (relative Namen) in der Klasse. Die Klasse ist also ein Namensraum für ihre Elemente.
Eine Klasse ist auch ein Beispiel für einen Verbund : Darunter versteht man eine Zusammenfassung von Entitäten zu einer neuen Entität. Die Teile eines Verbundes sind meistens durch ihre Position in einer Reihenfolge, durch ihre Funktion oder durch einen Namen unterscheidbar. Die Bestandteile einer Klasse nennt man manchmal auch ihre Elemente (member ). Die Elemente einer Klassen haben in der Regel einen Namen.
Solche Routinen in einer Klasse werden auch als Methoden oder Operationen bezeichnet, wobei mit „Methode“ die Schnittstelle und Implementation gemeint ist und mit „Operation“ nur die Schnittstelle der Routine.
Auf einem Baum ein Kuckuck sass [UML class diagram].--------------------------------------------------------.
| |
| Auf_einem_Baum_ein_Kuckuck_sass |
| |
|--------------------------------------------------------|
|--------------------------------------------------------|
| |
| - Kuckuck_Refrain_ausgeben(): void |
| ---------------------------------- |
| |
| + Kuckuck_ausgeben(): void |
| -------------------------- |
| |
'--------------------------------------------------------'
Die Routine "Hochzeit_Refrain_ausgeben" und die Routine "Hochzeit_ausgeben" werden als Methoden der Klasse "Ein_Vogel_wollte_Hochzeit_machen" zusammengefaßt.
Ein Vogel wollte Hochzeit machen [UML class diagram].--------------------------------------------------------.
| |
| Ein_Vogel_wollte_Hochzeit_machen |
| |
|--------------------------------------------------------|
|--------------------------------------------------------|
| |
| - Hochzeit_Refrain_ausgeben(): void |
| ----------------------------------- |
| |
| + Hochzeit_ausgeben(): void |
| --------------------------- |
| |
'--------------------------------------------------------'
Das Vorzeichen einer Methode im UML -Klassendiagramm beschreibt dessen Sichtbarkeit. Ein negatives Vorzeichen "-" kennzeichnet einen privaten Eintrag, also einen, der nur innerhalb der Klasse sichtbar (d.h. verwendbar) ist. Ein positives Vorzeichen kennezeichnet einen öffentliche Eintrag, also einen, der auch außerhalb der Klasse sichtbar ist.
Da eine Klasse gleichzeitig ein Namensraum ist, ist es bei Verwendung von Klassen nicht mehr nötig zu verhindern, daß Namen von Klassenelementen sich von den Namen der Elemente anderer Klassen unterscheiden. Daher kann die lästige Erweiterung der Routinennamen aufgegeben werden. Das zugehörige Lied wird ja bereits durch die Klasse bestimmt. Die Namen der Methoden werden nun mit einem kleinen Anfangsbuchstaben geschrieben, um sie von den mit einem großen Anfangsbuchstaben geschriebenen Klassennamen zu unterscheiden.
Auf einem Baum ein Kuckuck sass 1 [UML class diagram].--------------------------------------------------------.
| |
| Auf_einem_Baum_ein_Kuckuck_sass_1 |
| |
|--------------------------------------------------------|
|--------------------------------------------------------|
| |
| - refrain_ausgeben(): void |
| -------------------------- |
| |
| + ausgeben(): void |
| ----------------------- |
| |
'--------------------------------------------------------'Ein Vogel wollte Hochzeit machen 1 [UML class diagram].--------------------------------------------------------.
| |
| Ein_Vogel_wollte_Hochzeit_machen_1 |
| |
|--------------------------------------------------------|
|--------------------------------------------------------|
| |
| - refrain_ausgeben(): void |
| -------------------------- |
| |
| + ausgeben(): void |
| ------------------ |
| |
'--------------------------------------------------------'
Wenn in der Methode "ausgeben" der Klasse "Ein_Vogel_wollte_Hochzeit_machen_1" die Methode "refrain_ausgeben" ohne weitere Angaben zur Klasse aufgerufen wird, dann wird dadurch natürlich die Methode "refrain_ausgeben" derselben Klasse "Ein_Vogel_wollte_Hochzeit_machen_1" aufgerufen, obwohl es in anderen Klassen auch eine Methode mit dem selben Namen geben kann.
Es fällt nun noch auf, daß die beiden Klassendiagramme sich nur noch in ihrem Namen unterscheiden, aber hinsichtlich der Einträge gleichen: Beide Klassen haben denselben Schnittstellentyp. Solche Klassen können in vielen Fällen leicht gegeneinander ausgetauscht werden: Sie verstehen sozusagen dieselbe Sprache.
Der Aufruf einer Methode einer Klasse wird in manchen Zusammenhängen auch beschrieben, indem man sagt, daß man der Klasse eine Nachricht schicke. Die Klasse entscheidet dabei, wie sie auf die Nachricht reagiert oder antwortet. Hier wird angenommen, daß der Empfang einer Nachricht dem Aufruf der Methode mit gleichem Namen und passendem Typ entspricht. Der Versand der Nachricht "ausgeben()" an eine Klasse ist also wie der Aufruf der Methode "ausgeben" der Klasse, die die Nachricht erhält.
Die Nachricht "ausgeben()" kann somit an eine der beiden Klassen geschickt werden, die dann die Nachricht jeweils als Aufruf der gleichnamigen Methode verstehen. Da diese Methode bei beiden Klassen zwar den gleichen Schnittstellentyp hat, aber unterschiedlich definiert ist, ergibt sich dann auch ein unterschiedliches Verhalten, je nachdem, welcher Klasse die Nachricht geschickt wird.
Dieselbe Nachricht kann unterschiedlich interpretiert werden, je nachdem, von welchem Empfänger sie interpretiert wird.
Wird die Nachricht "ausgeben()" an die Klasse "Auf_einem_Baum_ein_Kuckuck_sass_1" geschickt, so wird das Lied „Auf einem Baum ein Kuckuck saß“ ausgegeben. (Hier wird tatsächlich nur der Anfang ausgegeben.)
[UML collaboration diagram]|
| |
| | ausgeben()
| |
| V
|
.--------------------------------------.
| |
| Auf_einem_Baum_ein_Kuckuck_sass_1 |
| |
'--------------------------------------'AusgabeAuf einem Baum ein Kuckuck, -
Sim sa la dim, bam ba,
Sa la du, sa la dim -
Auf einem Baum ein Kuckuck sass.
Da kam ein junger Jaeger, -
Sim sa la dim, bam ba,
Sa la du, sa la dim -
Da kam ein junger Jaegersmann.
Der schoss den armen Kuckuck, -
Sim sa la dim, bam ba,
Sa la du, sa la dim -
Der schoss den armen Kuckuck tot.
Wird die Nachricht "ausgeben()" an die Klasse "Ein_Vogel_wollte_Hochzeit_machen_1" geschickt, so wird das Lied „Ein Vogel wollte Hochzeit machen“ ausgegeben (Hier wird tatsächlich nur der Anfang ausgegeben).
[UML collaboration diagram]|
| |
| | ausgeben()
| |
| V
|
.--------------------------------------.
| |
| Ein_Vogel_wollte_Hochzeit_machen_1 |
| |
'--------------------------------------'AusgabeEin Vogel wollte Hochzeit machen, in dem grünen Walde.
Fi-di-ra-la-la, fi-di-ra-la-la, fi-di-ra-la-la-la-la!
Die Drossel war der Bräutigam, die Amsel war die Braute.
Fi-di-ra-la-la, fi-di-ra-la-la, fi-di-ra-la-la-la-la!
Die Lerche, die Lerche, die führt die Braut zur Kerche.
Fi-di-ra-la-la, fi-di-ra-la-la, fi-di-ra-la-la-la-la!
Eine Nachricht kann man auch mit einem Kommando oder einen Befehl vergleichen. Auch aus dem Alltag ist es ja bekannt, daß verschiedenen Personen unterschiedlich reagieren, wenn man ihnen das gleiche sagt.
Die beiden vorgestellten Klassen haben den gleichen Schnittstellentyp, der hier "Ausgebbar" genannt wird. Dieser Schnittstellentyp kann dadurch beschrieben werden, daß er die Nachricht "ausgeben()" interpretieren kann. Der Schnittstellentyp einer Klasse gibt an, welche Nachrichten die Klasse interpretieren kann, also welche Operationen sie bietet. Die Schnittstelle (interface ) bestimmt darüber hinausgehend noch jeweils eine bestimmten Bedeutung für die Nachrichten, enthält also auch eine Beschreibung der Operationen. Ein Schnittstellentyp wird aber meistens auch nur kurz als „Schnittstelle“ bezeichnet. Die Schnittstelle der hier vorgestellten Klassen legt noch fest, daß der Empfang der Nachricht "ausgeben()" zur Ausgabe eines Liedtextes führt, während der Schnittstellentyp nur festlegt, daß diese Nachricht empfangen werden kann, aber nicht, welche Bedeutung der Empfang haben soll.
Für Klassen, welche die Nachricht "ausgeben()" verstehen und darauf mit einer Ausgabe ihres jeweiligen (Lied-)Textes reagieren, wird die Schnittstelle "Ausgebbar" eingeführt. Die Klasse "Auf_einem_Baum_ein_Kuckuck_sass_1" und die Klasse "Ein_Vogel_wollte_Hochzeit_machen_1" bieten beide diesen Schnittstelle "Ausgebbar". Man sagt auch, sie implementieren oder realisieren diese Schnittstelle.
In UML kann die Beziehung zwischen einer Klasse und ihrer Schnittstelle durch einen gestrichelten Generalisierungspfeil mit einer nicht ausgefüllten dreieckigen Spitze dargestellt werden. Dieser kennzeichnet dann, daß die Klasse die Schnittstelle realisiert. Die Angabe des sogenannten Stereotyps "<<realize>>", also einer Kennzeichnung mit der Bedeutung des Pfeiles, ist bei diesem Pfeil nicht nötig, weil dieser Stereotyp durch die Darstellung des Pfeiles bereits festgelegt ist.
Eine Schnittstelle kann aber auch durch eine in einem kleinen Kreis endende Linie dargestellt werden, die ein Klassendiagramm verläßt.
In beiden Fällen wird ausgesagt, daß die Klasse alle Operationen der Schnittstelle implementiert (und möglicherweise noch weitere Operationen).
Lied-Schnittstelle [UML class diagram].---------------------------------------------.
| |
| <<interface>> |
| Ausgebbar |
| |
|---------------------------------------------|
| | < <<use>>
| ausgeben(): void |< - - - - - - - - .
| ---------------- | |
| |
'---------------------------------------------' |
^
/ \ .-------------.
--- | |
| ^ <<realize>> | <Klient> |
| |
| '-------------'
|
. - - - - - - - - - - - - - - '
|
|
.---------------------------------------------. V <<use>> |
| | |
| Auf_einem_Baum_ein_Kuckuck_sass_1 | |
| | |
|---------------------------------------------| |
| |---------------------------------------------| Ausgebbar
' - - -| |-----------o < - -'
| | - refrain_ausgeben(): void |
| -------------------------- |
| | |
| + ausgeben(): void |
| | ------------------ |
| |
| '---------------------------------------------'
.---------------------------------------------.
| | |
| Ein_Vogel_wollte_Hochzeit_machen_1 |
| | |
|---------------------------------------------|
| |---------------------------------------------| Ausgebbar
' - - -| |-----------o
| - refrain_ausgeben(): void |
| -------------------------- |
| |
| + ausgeben(): void |
| ------------------ |
| |
'---------------------------------------------'
Die Schnittstelle "Ausgebbar" enthält nur die Operation "ausgeben" und nicht die private Methode "refrain ausgeben", weil private Methoden nicht zur Schnittstelle gehören. Dadurch ist es möglich, daß bestimmte Aspekte der Implementation (des internen Aufbaus) der Klasse verändert werden könnten, ohne daß sich dabei auch die Schnittstelle ändern muß. Beispielsweise könnte darauf verzichtet werden, den Refrain mit einer eigens dafür vorgesehenen Routine auszugeben und die Ausgabe des Refrains könnte direkt in der Methode "ausgeben" erledigt werden. Da ein Klient (Benutzer) der Klasse aber nur an die Schnittstelle koppelt, merkt er nichts von dieser internen Änderung. Das bedeutet, daß der Klient durch solche schnittstellenwahrenden Änderungen nicht beschädigt wird (er muß also nicht angepaßt werden, sondern kann unverändert weiterarbeiten). Solche Änderungen haben also nicht die Notwendigkeit von anderen Änderungen bei Klienten zur Folge und sind dadurch einfacher zu bewerkstelligen. Deswegen gibt es auch die Möglichkeit, bestimmte Methoden, auf die ein Klient nicht zugreifen können soll, als „privat“ zu kennzeichnen: Sie können später beispielsweise auch wieder entfernt werden, ohne daß befürchtet werden muß, Klienten dadurch zu beschädigen—denn die Klienten können von ihnen gar nichts wissen, weil sie nicht auf privaten Methoden zugreifen können.
Eine empfehlenswerte Richtlinie zur Gestaltung von Programmen ist es, mit Klassen Gegenstände oder Begriffe aus dem Anwendungsgebiet des Programmes nachzugestalten. In jedem Anwendungsgebiet verwendet man bestimmte Modelle, die Begriffe und ihre Beziehungen festlegen. Diese Begriffe sind dann oft die Grundlage für Klassendefinition im Rahmen der objektorientierten Gestaltung. Bei den hier gegebenen Beispielklassen, repräsentiert eine Klasse jeweils ein Lied.
In der objektorientierten Programmierung baut man Modelle von Systemen. Bei dem hier gegebenen Beispiel modelliert jede Klasse ein Lied und die gesamte Klassensammlung modelliert eine Liedersammlung. Ein Lied hat in diesem Modell, die Fähigkeit, seinen Text auszugeben. Diese Fähigkeit kann durch Senden einer Nachricht an das Objekt nutzen.