Der moderne Softwareentwicklungsprozess mit UML


Kapitel 4: Das Klassendiagramm


Inhaltsverzeichnis

Dieses Buch ist unter einer Creative Commons-Lizenz lizensiert.


4.1 Allgemeines

Abhängigkeiten zwischen Klassen und Klasseninterna

Das Klassendiagramm ist das mit Abstand wichtigste Strukturdiagramm der UML, wenn nicht sogar der wichtigste Diagrammtyp überhaupt. Es stellt Zusammenhänge zwischen Klassen als auch den Aufbau von Klassen dar. Da Klassen das entscheidende Merkmal objektorientierter Programmiersprachen sind, können sich Programmierer, die bisher wenig mit Softwaremodellierung zu tun hatten, sehr schnell mit diesem Diagrammtyp anfreunden. Das, was man vom Quellcode her kennt, kommt dem, was das Klassendiagramm zeigt, am nächsten.


4.2 Grundlegende Bausteine eines Klassendiagramms

Ähnlichkeit mit Konzepten aus objektorientierten Programmiersprachen

Im Folgenden lernen Sie die grundlegenden Bausteine kennen, aus denen alle Klassendiagramme aufgebaut sind.

Sie lernen in diesem Buch drei Arten von Beziehungen in Klassendiagrammen kennen. Die Verbindungslinie zwischen Klasse A und B ist eine gewöhnliche Assoziation. Sie bedeutet, dass Klasse A auf Klasse B zugreifen kann. In der UML wird von Navigierbarkeit gesprochen. Die Pfeilrichtung gibt an, welche Klasse auf wen Zugriff hat. Sind an beiden Enden der Assoziation Pfeile angegeben, kann jede Klasse auf die andere zugreifen. Auch Assoziationen ohne Pfeile sind möglich: In diesem Fall ist die Navigierbarkeit undefiniert, weil sie der Modellierer im Diagramm zum Beispiel für unwichtig gehalten und deswegen nicht eingezeichnet hat.

Die Verbindungslinie zwischen Klasse C und D stellt eine Komposition dar - zu erkennen an der schwarzen Raute. Eine Komposition ist eine Klasse, die nicht ohne andere Klassen existieren kann. Typisches Beispiel ist eine Klasse Auto, die durch eine Komposition mit einer Klasse Motor verbunden ist. Würde die Beziehung zwischen den Klassen Auto und Motor aufgehoben werden, so ist das Auto kein Auto mehr - immerhin fehlt dann der wichtigste Bestandteil. Wenn eine Klasse also nicht ohne eine andere existieren bzw. sinnvoll funktionieren kann, kann diese hohe Abhängigkeit durch eine Komposition ausgedrückt werden. Die schwarze Raute befindet sich dabei an dem Ende der Verbindungslinie, an dem die abhängige Klasse steht. Im Beispiel oben hängt also Klasse C von Klasse D ab. Eine Komposition beinhaltet immer Navigierbarkeit - deswegen ist an der anderen Seite der Verbindungslinie ein Pfeil angebracht.

Das aus objektorientierten Programmiersprachen bekannte Konzept der Vererbung wird durch die Verbindungslinie dargestellt, die zwischen Klasse E und F aufgespannt ist. Die Pfeilspitze unterscheidet sich deutlich von der einer Assoziation. Sie zeigt bei einer Vererbung auf die Elternklasse. Das heißt, Klasse F ist Kindklasse von E und erbt Eigenschaften und Methoden der Elternklasse.

Assoziationen lassen sich verfeinern, indem man an navigierbaren Enden Multiplizitäten angibt. Eine Multiplizität gibt an, ob es sich zwischen zwei Klassen um eine 1:1-, 1:n- oder n:m-Beziehung handelt. Bei einer 1:1-Beziehung steht jedes Objekt einer Klasse mit genau einem Objekt der anderen Klasse in Verbindung. Bei einer 1:n-Beziehung hat ein Objekt einer Klasse Zugriff auf mehrere Objekte der anderen Klasse. Und bei einer n:m-Beziehung haben Objekte beider Klassen Zugriff auf mehrere Objekte der anderen Klasse.

Wird keine Angabe vorgenommen, ist eine Assoziation immer eine 1:1-Beziehung. Um eine 1:n-Beziehung auszudrücken, stellen Sie ein * an das navigierbare Ende. Im Beispiel oben besteht eine derartige 1:n-Beziehung zwischen Klasse A und B: Ein Objekt der Klasse A hat Zugriff auf beliebig viele Objekte der Klasse B.

Die Verbindungslinie zwischen C und D drückt aus, dass ein Objekt der Klasse C entweder auf kein oder genau ein Objekt der Klasse D Zugriff hat. Es ist möglich, Unter- und Obergrenzen festzulegen, indem zwischen den entsprechenden Zahlen einfach zwei Punkte gesetzt werden.

Neben der Angabe einer Multiplizität kann auch eine Rollenbezeichnung an das navigierbare Ende gestellt werden. Wenn Sie bei einer Rolle an die Besetzung von Rollen in einem Film denken, liegen Sie gar nicht falsch: Eine Klasse kann verschiedene Rollen spielen. So kann die im C++-Standard definierte Klasse std::string eingesetzt werden, um zum Beispiel Namen oder WWW-Adressen zu speichern. Die Aufgaben zweier solcher Strings unterscheiden sich, was durch eine Rollenbezeichnung ausgedrückt werden kann.

Neben der Darstellung von Zusammenhängen können im Klassendiagramm auch Klassen selbst beschrieben werden.

Klassen werden als Rechtecke dargestellt, die in drei Bereiche unterteilt werden: Im oberen Bereich steht der Name der Klasse. Im mittleren Bereich stehen Eigenschaften der Klasse, im unteren Bereich Methodendeklarationen. Eine Klasse in der UML ist nichts anderes als eine Klasse in einer objektorientierten Programmiersprache. So wie im Quellcode Klassen aus Eigenschaften und Methoden bestehen, gilt dies auch für die UML.

Es ist sogar möglich, den Zugriff auf Eigenschaften und Methoden einzuschränken. Man setzt vor die Eigenschaft oder Methodendeklaration ein einzelnes Zeichen, das die Sichtbarkeit angibt. Das Minuszeichen - steht für ein privates Klassenmerkmal, das Pluszeichen + für ein öffentliches Klassenmerkmal. Das Hash-Zeichen # ist gleichbedeutend mit dem aus Programmiersprachen bekannten Schlüsselwort protected, und das Tildezeichen ~ steht für das Schlüsselwort package.

Die Definition von Eigenschaften erfolgt, indem hinter dem Namen der Eigenschaft getrennt durch einen Doppelpunkt ein Datentyp angegeben wird. Methoden werden ähnlich deklariert: Hinter dem Methodenkopf wird ebenfalls getrennt durch einen Doppelpunkt der Datentyp des Rückgabewertes angegeben. Gibt eine Methode keinen Wert zurück, verwenden Sie das aus Programmiersprachen bekannte Schlüsselwort void.

Sie können auch aus Programmiersprachen bekannte statische Eigenschaften und Methoden definieren. Die entsprechenden Klassenmerkmale werden dann einfach unterstrichen.

Auch Arrays sind möglich: Geben Sie einfach hinter dem Datentyp in eckigen Klammern eine Multiplizität an.


4.3 Klassendiagramme in der Praxis

Quadratisch, praktisch, gut

Im Folgenden werden wir in verschiedenen Klassendiagrammen jeweils einen Teil der Software-Architektur unseres Online-Shops betrachten, bevor am Ende des Kapitels alle Klassen in einem großen Klassendiagramm übersichtlich zusammengestellt werden.

In unserem Online-Shop sollen verschiedene Artikel angeboten werden können. Wir fassen alle Artikel, die durch die gleichnamige Klasse repräsentiert werden, in einer Klasse Angebot zusammen. Dazu zeichnen wir zwischen der Klasse Angebot und der Klasse Artikel eine Assoziation ein. Die Assoziation ist wie oben erkennbar eine 1:n-Assoziation: Auf ein Objekt der Klasse Angebot kommen mehrere Objekte der Klasse Artikel. Genauer gesagt: Auf ein Objekt der Klasse Angebot kommt mindestens ein Objekt der Klasse Artikel. Das heißt, ein Angebot enthält immer mindestens einen Artikel. Etwas anderes soll in unserem Online-Shop nicht erlaubt sein. Ein Online-Shop ohne Artikel im Angebot würde auch nicht viel Sinn machen.

Beachten Sie, dass die Assoziation nicht nur als 1:n-Beziehung gekennzeichnet ist, sondern außerdem eine Pfeilspitze angegeben ist. Das heißt, die Klasse Artikel ist von der Klasse Angebot aus navigierbar. Ein Objekt der Klasse Angebot kann jederzeit auf seine Artikel zugreifen.

Obiges Klassendiagramm stellt einen Zusammenhang zwischen Angebot und Artikel her. Es wird im Folgenden erweitert, um zu beschreiben, was ein Artikel eigentlich genau ist.

Die Klasse Artikel besitzt nun vier Eigenschaften. Ein Artikel ist durch eine Artikelnummer, einen Namen und einen Preis gekennzeichnet. Außerdem kann für Artikel angegeben werden, ob sie verfügbar sind oder nicht. Diese Eigenschaften entsprechenden den Anforderungen unseres Online-Shops.

Alle vier Eigenschaften sind privat, wie am Minuszeichen vor den Namen erkannt werden kann. Der Datentyp jeder Eigenschaft ist hinter dem Namen angegeben, getrennt vom Namen durch einen Doppelpunkt.

Beachten Sie, dass Eigenschaften auch als Assoziationen dargestellt werden können. Das gilt auch umgekehrt: Eine Assoziation kann jederzeit als Eigenschaft dargestellt werden. Sehen Sie sich hierfür folgendes Klassendiagramm an, das von seiner Aussage her mit obigem Klassendiagramm völlig identisch ist.

Die Assoziation zwischen den Klassen Angebot und Artikel ist nun durch eine Eigenschaft Artikel in der Klasse Angebot ausgedrückt. Diese Eigenschaft ist vom Typ Artikel. Es ist außerdem angegeben, dass in dieser Eigenschaft mehrere Objekte gespeichert werden können, mindestens aber ein Objekt enthalten sein muss. Man gibt die Multiplizität hierzu in eckigen Klammern hinter dem Typ an.

Die vier Eigenschaften der Klasse Artikel sind nun als Assoziationen angegeben. Die bisherigen Namen der Eigenschaften sind jetzt Rollenbezeichner. Rollenbezeichner stehen immer am Ende einer Assoziation, und zwar an dem Ende, an dem sich die Klasse für die Rolle befindet. Obiges Diagramm drückt aus, dass die Klasse Artikel Zugriff auf die Klassen Integer, String, Double und Boolean hat. Die Beziehungen zwischen den Klassen sind alle 1:1-Beziehungen, da keine Multiplizitäten angegeben sind. Das heißt, es gibt jeweils Zugriff auf genau ein Objekt vom Typ Integer, String, Double und Boolean. Dieses eine Objekt, auf das jeweils Zugriff besteht, wird durch den Rollenbezeichner näher gekennzeichnet. So wird das Objekt der Klasse Integer verwendet, um eine Artikelnummer zu speichern. Die Minuszeichen vor den Rollenbezeichnern drücken wieder aus, dass alle Objekte private Eigenschaften der Klasse Artikel sind.

Das Klassendiagramm wird nun um eine Klasse Warenkorb erweitert. In einen Warenkorb können beliebig viele Artikel gelegt werden. Deswegen gibt es eine 1:n-Beziehung zwischen der Klasse Warenkorb und der Klasse Artikel. Die Klasse Artikel wurde außerdem um eine Eigenschaft Bestellanzahl erweitert, in der gespeichert wird, wie oft der Artikel in den Warenkorb gelegt wurde.

Die Klasse Warenkorb besitzt neben der Assoziation zur Klasse Artikel eine statische Eigenschaft KostenfreiAb. Sie erkennen statische Eigenschaften daran, dass sie unterstrichen werden. Die Eigenschaft KostenfreiAb ist statisch, weil sie für alle Warenkörbe gilt - völlig unabhängig davon, welche Artikel in ihnen liegen. Sie gibt den Euro-Betrag an, ab dem für die Lieferung der bestellten Ware keine Verpackungs- und Versandkosten in Rechnung gestellt werden.

Sehen Sie sich folgendes Klassendiagramm an, um zu verstehen, wie Methoden einer Klasse hinzugefügt werden.

Um Artikel in den Warenkorb legen oder auch wieder herausnehmen zu können, werden zwei Methoden deklariert: Die Methode Reinlegen() wird verwendet, um einen Artikel mit der angegebenen Artikelnummer in den Warenkorb zu legen. Die Methode Rausnehmen() wird verwendet, um einen Artikel, der sich im Warenkorb befindet, zu entfernen. Beide Methoden erwarten als zweiten Parameter eine Zahl, die angibt, wie viele Artikel in den Warenkorb gelegt bzw. wie viele Artikel aus dem Warenkorb herausgenommen werden sollen. Beide Methoden sind wie am Pluszeichen zu erkennen öffentlich.

Mit dem Warenkorb alleine ist eine Bestellung natürlich nicht vollständig. Wir benötigen weitere Angaben, um ausgewählte Artikel ausliefern zu können.

Im obigen Diagramm steht die Klasse Bestellung im Mittelpunkt. Sie hat Zugriff auf die bereits bekannte Klasse Warenkorb und auf eine Klasse namens Anschrift. Dieser Zugriff findet über eine private Eigenschaft Rechnungsanschrift statt, wie am Rollenbezeichner abgelesen werden kann. Außerdem besitzt die Klasse Bestellung eine private Eigenschaft namens Lieferanschrift, die ebenfalls vom Typ Anschrift ist. Während es aber genau eine Rechnungsanschrift gibt, kann an der Multiplizität abgelesen werden, dass die Angabe einer Lieferanschrift nicht zwingend ist. Ist keine Lieferanschrift angegeben, wird der Warenkorb an die Rechnungsanschrift ausgeliefert.

Bei den Verbindungslinien zu den beiden Klassen Warenkorb und Anschrift handelt es sich nicht um herkömmliche Assoziationen, sondern um Kompositionen. Sie erkennen das an der schwarzen Raute, die sich am Anfang der Verbindungslinien befindet. Damit soll ausgedrückt werden, dass eine Bestellung nichts anderes ist als ein Warenkorb und eine Rechnungsanschrift. Eine Bestellung kann ohne die anderen beiden Klassen nicht existieren - was sollte das für eine Bestellung sein, die keine Daten zu bestellten Waren noch eine Rechnungsanschrift enthält? Diese existentiellen Beziehungen zu den Klassen Warenkorb und Anschrift werden durch die Komposition ausgedrückt.

Die Klasse Anschrift enthält zahlreiche private Eigenschaften vom Typ String. Bis auf die Telefonnummer sind alle Angaben Pflicht.

Die Eigenschaft vom Typ Land ist als Assoziation ausgedrückt. Das Land wird also in der Klasse Anschrift nicht einfach nur als String gespeichert, sondern ist eine eigene Klasse. Denn wir müssen zu jedem Land mehr Daten speichern als den Namen. Wie oben im Klassendiagramm zu erkennen wird für jedes Land festgehalten, wie hoch die Versand- und Verpackungskosten sind und ob die Angabe einer Telefonnummer für Bestellungen aus diesem Land Pflicht ist.

Was fehlt, um eine Bestellung komplett zu machen, ist die Zahlungsmethode.

Die Klasse Zahlungsmethode ist ebenfalls durch eine Komposition mit der Klasse Bestellung verbunden. Da für eine Bestellung jedoch eine konkrete Zahlungsmethode ausgewählt werden muss, ist die Klasse Zahlungsmethode abstrakt. Sie erkennen das an der kursiven Schreibweise des Klassennamens. Es kann also kein Objekt der Klasse Zahlungsmethode erstellt werden - das hätte keinen Sinn, weil dies keine Auskunft geben würde, welche Zahlungsmethode denn genau vom Kunden gewählt wurde.

Die vom Online-Shop unterstützten Zahlungsmethoden werden als abgeleitete Klassen dargestellt. Sie erkennen das an den Verbindungslinien mit dem ausgeprägten dreieckigen Pfeil. Die Klassen Nachnahme, Vorauskasse und Bankeinzug sind Kindklassen der Elternklasse Zahlungsmethode.

Während die Klassen Nachnahme und Vorauskasse keine Eigenschaften definieren, ist die Klasse Bankeinzug durch die privaten Eigenschaften Kontonummer, Bankleitzahl und Bank charakterisiert. Alle drei Kindklassen erben jedoch von der Klasse Zahlungsmethode die Eigenschaft Gebühr. Das Hash-Zeichen vor dem Eigenschaftsnamen ist gleichbedeutend mit dem von einigen Programmiersprachen bekannten protected-Gültigkeitsbereich.

In der Eigenschaft Gebühr werden jeweils die Kosten der Zahlungsmethode für das Land gespeichert, in das die Auslieferung erfolgt. Erinnern Sie sich, dass wir unterschiedliche Kosten für Zahlungsmethoden haben - zum Beispiel ist die Nachnahme im Inland billiger als im Ausland. Da die Kosten für Zahlungsmethoden aber vom Land abhängen, müssen wir die Klasse Land um entsprechende Eigenschaften erweitern. Wir tun das im folgenden Beispieldiagramm, in dem die Klasse Land sowieso eingesetzt wird.

Was bisher in den Klassendiagrammen fehlte, war der Distributor, der per Benutzername und Kennwort auf den Online-Shop zugreifen möchte, um Bestell- und Kundendaten herunterzuladen. Im obigen Diagramm steht eine Klasse Distributor, die über Verbindungslinien mit den beiden bereits bekannten Klassen Bestellung und Land verknüpft ist. Diese Assoziationen drücken aus, dass der Distributor auf beliebig viele Bestellungen zugreifen kann und für mindestens ein Land verantwortlich ist, in das er Bestellungen auszuliefern hat.

Die Klasse Format, zu der ebenfalls eine Beziehung besteht, legt das Format fest, in dem Kunden- und Bestelldaten für den Distributor zur Verfügung gestellt werden sollen. Erinnern Sie sich, dass Distributoren mit unterschiedlichen Programmen arbeiten und für die Übernahme der Kunden- und Bestelldaten diese im richtigen Format vorliegen müssen.

Oben sehen Sie alle verwendeten Klassen in einer Gesamtübersicht. Da für diese Gesamtübersicht Eigenschaften und Methoden der Klassen nicht relevant sind, wurden sie ausgeblendet. Das Klassendiagramm stellt also lediglich die Zusammenhänge zwischen allen Klassen dar, die wir für die Entwicklung des Online-Shops benötigen.


4.4 Zusammenfassung

Das Allround-Diagramm der Strukturdiagramme

Wenn Sie verstanden haben, wie Klassendiagramme aufgebaut sind und funktionieren, können Sie den wahrscheinlich wichtigsten Diagrammtyp der UML einsetzen. Durch die sehr enge Verbindung mit Konzepten aus objektorientierten Programmiersprachen sind Klassendiagramme der Diagrammtyp, der Programmierer am ehesten anspricht und sie von den Vorteilen der Softwaremodellierung überzeugt. Dass die UML die gleichen Begriffe wie Klasse, Eigenschaft, Methode oder Vererbung verwendet, die bereits aus objektorientierten Programmiersprachen bekannt sind, ist ebenfalls von Vorteil.

Da ein Klassendiagramm scheinbar genau das abbildet, was man in einer objektorientierten Programmiersprache in Quellcode ausdrücken kann, kann ein Klassendiagramm recht einfach in Quellcode überführt werden. Das Klassendiagramm ist tatsächlich der Diagrammtyp, der am einfachsten in Quellcode übersetzt werden kann. Dies kann bereits von vielen UML-Werkzeugen automatisch auf Knopfdruck erledigt werden. Man spricht hierbei von Code-Generierung. Im Kapitel 5, Code-Generierung erfahren Sie mehr über das automatische Erzeugen von Quellcode aus Diagrammen.