9. UML II – Klassen- und Sequenzdiagramm
Nach diesem Kapitel kannst du:
- ein Klassendiagramm mit Sichtbarkeit, Beziehungen und Multiplizitäten zeichnen und laut lesen.
- Aggregation und Komposition unterscheiden und deine Wahl begründen.
- ein Klassendiagramm in Code übersetzen und Multiplizitäten in Datenstrukturen übersetzen.
- zu einem Anwendungsfall ein Sequenzdiagramm mit
alt-Fragment zeichnen und es gegen das Klassendiagramm auf Widersprüche prüfen.
Im vorigen Kapitel hast du mit dem Use-Case-Diagramm die erste der drei Karten aus der Navi-App gezeichnet: wer mit dem System welches Ziel erreicht. Jetzt folgen der Stadtplan und die Routenanweisung, also Klassen- und Sequenzdiagramm der LibraryApp.
Schritt 2: Klassendiagramm - aus welchen Bausteinen besteht das System?
Abschnitt betitelt „Schritt 2: Klassendiagramm - aus welchen Bausteinen besteht das System?“Das Klassendiagramm beschreibt die statische Struktur: welche Klassen es gibt, was sie wissen, was sie können und wie sie zusammenhängen. Es ist das Diagramm, das dem Code am nächsten ist, und deshalb dasjenige, das du in der Diplomarbeit am häufigsten brauchst.
Aufbau einer Klasse
Abschnitt betitelt „Aufbau einer Klasse“Eine Klasse besteht aus bis zu drei Kompartimenten: Name, Attribute, Operationen. Jedes Attribut und jede Operation trägt eine Sichtbarkeit.
| Zeichen | Sichtbarkeit | Bedeutung |
|---|---|---|
+ | public | von überall zugreifbar |
- | private | nur innerhalb der Klasse |
# | protected | in der Klasse und in Unterklassen |
~ | package | im selben Paket oder Modul |
classDiagram
class Book {
-isbn : string
-title : string
-available : boolean
+isAvailable() boolean
+markBorrowed() void
+markReturned() void
}
Beachte, was fehlt: Getter und Setter für jedes Attribut. Ein Klassendiagramm zeigt die fachlich relevanten Operationen, nicht jede Zeile, die der Code später enthält. Wer jedes getTitle() einzeichnet, macht das Diagramm unlesbar und gewinnt nichts.
Beziehungen
Abschnitt betitelt „Beziehungen“Der Wert eines Klassendiagramms liegt weniger in den Kästchen als in den Linien dazwischen. Sechs Beziehungsarten decken praktisch alles ab:
| Beziehung | Notation (Mermaid) | Bedeutung | Prüffrage |
|---|---|---|---|
| Assoziation | A --> B | A kennt B und kann es verwenden | ”A hat eine Referenz auf B” |
| Aggregation | A o-- B | A besteht aus B, B kann aber auch ohne A existieren | schwache “Hat-ein”-Beziehung |
| Komposition | A *-- B | A besteht aus B, B lebt und stirbt mit A | starke “Hat-ein”-Beziehung |
| Vererbung | A <|-- B | B ist eine spezielle Art von A | ”B ist ein A” |
| Realisierung | A <|.. B | B setzt die Schnittstelle A um | ”B erfüllt den Vertrag von A” |
| Abhängigkeit | A ..> B | A verwendet B nur vorübergehend (Parameter, lokale Variable) | “Wenn B sich ändert, muss ich A prüfen” |
Zu jeder Assoziation gehört eine Multiplizität an beiden Enden: 1 genau eins, 0..1 höchstens eins, * beliebig viele, 1..* mindestens eins.
Das Beispiel: Klassen der LibraryApp
Abschnitt betitelt „Das Beispiel: Klassen der LibraryApp“classDiagram
class Member {
-id : string
-name : string
+canBorrow() boolean
}
class Book {
-isbn : string
-title : string
+isAvailable() boolean
}
class Loan {
-dueDate : Date
+isOverdue() boolean
+close() void
}
class Catalogue {
+search(term) Book[]
}
Member "1" --> "*" Loan : has
Loan "*" --> "1" Book : concerns
Catalogue "1" o-- "*" Book : lists
Lies ein solches Diagramm laut: “Ein Mitglied hat beliebig viele Ausleihen. Jede Ausleihe betrifft genau ein Buch. Ein Katalog listet beliebig viele Bücher.” Wenn sich dieser Satz fachlich falsch anhört, ist die Multiplizität falsch - und das fällt hier in Sekunden auf, im Code erst als Fehler im Betrieb.
Wo begegnet dir das? Wenn du in deinem Jahresprojekt Entitäten wie Task oder User anlegst und festlegst, dass ein User viele Tasks hat, zeichnest du genau diese Linie mit Multiplizität. Dieselben Zahlen bestimmen später, ob dein Feld eine Liste oder ein einzelner Wert ist.
Genauer erklärt: Multiplizitäten sind Programmcode
Abschnitt betitelt „Genauer erklärt: Multiplizitäten sind Programmcode“Die Zahlen an den Linienenden sind keine Dekoration, sondern bestimmen die Datenstruktur im Code:
1heißt: Das Feld darf nienullsein, der Konstruktor muss es verlangen.0..1heißt: Das Feld ist optional (Book | undefined), jede Verwendung muss den Fall “nicht vorhanden” behandeln.*heißt: Es gibt eine Sammlung (Loan[]), kein einzelnes Feld.
Dass Loan zwischen Member und Book steht und nicht einfach Member "*" -- "*" Book, ist eine bewusste Entwurfsentscheidung: Eine Viele-zu-viele-Beziehung, die selbst Eigenschaften hat (Fälligkeitsdatum, Status), wird zu einer eigenen Klasse. Dieselbe Regel kennst du aus dem Datenbankentwurf als Zwischentabelle.
Genauer erklärt: Komposition, Aggregation und der Lebenszyklus
Abschnitt betitelt „Genauer erklärt: Komposition, Aggregation und der Lebenszyklus“Der Unterschied zwischen den beiden “Hat-ein”-Beziehungen ist die Frage, was mit dem Teil passiert, wenn das Ganze verschwindet.
- Komposition: Eine Bestellung besteht aus Bestellpositionen. Wird die Bestellung gelöscht, ergeben die Positionen allein keinen Sinn mehr - sie werden mitgelöscht. Im Code erzeugt die Bestellung ihre Positionen selbst.
- Aggregation: Ein Katalog listet Bücher. Wird der Katalog aufgelöst, existieren die Bücher weiter - sie gehören dem Bestand, nicht dem Katalog.
In der Praxis verwenden viele erfahrene Entwicklerinnen und Entwickler die Aggregation kaum noch: Die UML-Spezifikation gibt ihr keine eigene Bedeutung über die Assoziation hinaus, und in Reviews entstehen mehr Diskussionen über den leeren Rhombus, als er Information liefert. Eine brauchbare Regel: Komposition, wenn der Lebenszyklus wirklich gekoppelt ist, sonst einfache Assoziation. In der Schularbeit musst du die Aggregation trotzdem erkennen und korrekt erklären können.
Auflösung
Das Haus mit seinen Räumen ist eine Komposition: Wird das Haus abgerissen, gibt es die Räume nicht mehr. Die Playlist mit ihren Songs ist eine Aggregation: Löschst du die Playlist, bleiben die Songs in der Bibliothek erhalten. Frage dich immer, ob der Teil ohne das Ganze weiterlebt.
Genauer erklärt: Vererbung ist der teuerste Pfeil
Abschnitt betitelt „Genauer erklärt: Vererbung ist der teuerste Pfeil“Der Pfeil mit dem leeren Dreieck sieht harmlos aus und ist die stärkste Kopplung, die UML kennt: Die Unterklasse erbt alles, auch alles, was später dazukommt. Prüfe vor dem Zeichnen zwei Dinge.
- “Ist-ein”-Test: Ist wirklich jedes B in jeder Situation ein A? Ein Student ist eine Person - aber ein Quadrat ist, sobald es veränderbare Seitenlängen hat, kein sauberes Rechteck mehr (Liskov’sches Substitutionsprinzip, vertieft in Kapitel 13).
- Alternative: Reicht es, dass B eine A-Fähigkeit hat? Dann ist Komposition oder eine Schnittstelle (
<<interface>>, Realisierung) die flexiblere Wahl.
Genau diese Abwägung - Vererbung gegen Komposition - kommt in den Entwurfsmustern der Kapitel 11 bis 18 immer wieder vor, und dort wird sie in Klassendiagrammen dargestellt, die du nach diesem Kapitel lesen und selbst zeichnen kannst.
Vom Diagramm zum Code
Abschnitt betitelt „Vom Diagramm zum Code“Ein Klassendiagramm ist dann ein Bauplan, wenn der Code daraus mechanisch ableitbar ist. Der Ausschnitt des obigen Beispiels:
class Book { constructor( private readonly isbn: string, private readonly title: string, private available = true, ) {}
isAvailable(): boolean { return this.available; }}
class Loan { constructor( private readonly book: Book, // multiplicity 1: never undefined private readonly dueDate: Date, ) {}
isOverdue(): boolean { return this.dueDate.getTime() < Date.now(); }}
class Member { private readonly loans: Loan[] = []; // multiplicity *: a collection}Beispielprojekt: 09_uml_klassen_sequenz_examples/ex01_uml_to_code
Dass das Attribut loans hier steht, ist die Umsetzung der Assoziation Member "1" --> "*" Loan. Im Diagramm steht dieselbe Information als Linie, nicht zusätzlich als Attribut - das ist einer der häufigsten Fehler (siehe Kapitel 10).
Schritt 3: Sequenzdiagramm - was passiert in welcher Reihenfolge?
Abschnitt betitelt „Schritt 3: Sequenzdiagramm - was passiert in welcher Reihenfolge?“Das Klassendiagramm zeigt, wer es gibt. Das Sequenzdiagramm zeigt, wer wann mit wem spricht, damit ein Anwendungsfall abläuft. Die Zeit läuft von oben nach unten, die beteiligten Objekte stehen nebeneinander.
Die Bausteine des Sequenzdiagramms
Abschnitt betitelt „Die Bausteine des Sequenzdiagramms“| Element | Notation | Bedeutung |
|---|---|---|
| Lebenslinie | Kasten mit senkrechter gestrichelter Linie | ein Beteiligter (Objekt, Komponente oder Akteur) über die Zeit |
| Synchrone Nachricht | durchgezogener Pfeil, gefüllte Spitze | Aufruf, bei dem der Sender auf die Antwort wartet |
| Antwort | gestrichelter Pfeil | Rückgabe an den Aufrufer |
| Asynchrone Nachricht | durchgezogener Pfeil, offene Spitze | Aufruf, bei dem der Sender weiterarbeitet (Event, Message Queue) |
| Aktivierung | schmaler Balken auf der Lebenslinie | der Beteiligte ist gerade aktiv |
| Fragment | Rahmen mit Operator | steuert den Ablauf: alt (Verzweigung), opt (optional), loop (Wiederholung) |
Das Beispiel: Buch ausleihen
Abschnitt betitelt „Das Beispiel: Buch ausleihen“Dies ist der Standardablauf des Anwendungsfalls Borrow book aus der Beschreibung in Kapitel 08, ergänzt um den Alternativablauf 2a.
sequenceDiagram
actor M as Member
participant L as LoanService
participant B as Book
participant D as Database
M->>L: borrow(isbn)
activate L
L->>B: isAvailable()
B-->>L: boolean
alt book available
L->>B: markBorrowed()
L->>D: save(loan)
D-->>L: ok
L-->>M: loan with due date
else book already borrowed
L-->>M: offer reservation
end
deactivate L
Lies auch dieses Diagramm laut: “Das Mitglied ruft borrow beim LoanService auf. Der Service fragt das Buch, ob es verfügbar ist. Falls ja, markiert er es, speichert die Ausleihe und bestätigt dem Mitglied. Falls nein, bietet er eine Vormerkung an.”
Wo begegnet dir das? Wenn die Dokumentation eines Anmeldeverfahrens wie OAuth 2.0 erklärt, wer wann wem welche Nachricht schickt, ist das fast immer ein Sequenzdiagramm. Auch wenn du im Netzwerk-Tab der Browser-Entwicklertools siehst, wie Browser und Server Anfragen und Antworten austauschen, beobachtest du genau so einen Ablauf.
Auflösung
Erstens fehlt in der Klasse Book die Operation markBorrowed(), die der LoanService aufruft (im kleinen Einzeldiagramm von Book steht sie, im Gesamtdiagramm nicht). Zweitens gibt es die Klasse LoanService im Klassendiagramm gar nicht, obwohl sie den Ablauf koordiniert. Bei save(loan) kommt zusätzlich die Frage dazu, ob die Datenbank im Klassendiagramm überhaupt auftauchen soll.
Genauer erklärt: Nachrichten sind Methodenaufrufe
Abschnitt betitelt „Genauer erklärt: Nachrichten sind Methodenaufrufe“Jeder Pfeil zwischen zwei Objekten ist eine Methode des Empfängers. Das hat eine unangenehme, aber nützliche Konsequenz: Jede Nachricht im Sequenzdiagramm muss als Operation im Klassendiagramm vorkommen. Im Beispiel ruft LoanService die Methoden isAvailable() und markBorrowed() von Book auf - beide müssen im Klassendiagramm von Book stehen. Fehlt eine, ist entweder das Sequenzdiagramm falsch oder das Klassendiagramm unvollständig. In beiden Fällen hast du einen Fehler gefunden, bevor er im Code steht. Genau dafür zeichnet man beide.
Umgekehrt entdeckst du dabei häufig, dass eine Klasse fehlt. Im Beispiel taucht LoanService im Sequenzdiagramm auf, im Klassendiagramm oben aber nicht - die Ausleihe selbst muss ja von jemandem koordiniert werden. Der Entwurf ist durch das Zeichnen des Ablaufs besser geworden, und das Klassendiagramm wird nachgezogen.
Grenzen des Sequenzdiagramms
Abschnitt betitelt „Grenzen des Sequenzdiagramms“- Es zeigt einen Ablauf, nicht alle. Das Beispiel zeigt Standardablauf und einen Alternativablauf. Wer jede denkbare Verzweigung einzeichnet, verliert den Überblick; mehr als zwei, drei
alt-Zweige sind ein Hinweis, dass der Ablauf eher als Aktivitäts- oder Zustandsdiagramm dargestellt gehört. - Es skaliert nicht in die Breite. Mit mehr als etwa fünf Lebenslinien wird es unleserlich. Zerlege dann in mehrere Diagramme oder fasse Beteiligte zu einer Komponente zusammen.
- Es ist kein Programmablaufplan. Algorithmen innerhalb einer Methode (Schleifen über Daten, Rechnungen) gehören nicht hinein; das Diagramm zeigt die Kommunikation, nicht die Berechnung.
Zusatzmaterialien
Abschnitt betitelt „Zusatzmaterialien“- Mermaid-Dokumentation - Syntax für textbasierte Diagramme, die direkt neben dem Code im Repository versioniert werden können.
- OMG: Unified Modeling Language - die verbindliche Spezifikation; zum Nachschlagen bei Streitfragen zur Notation, nicht zum Durchlesen.
Zum Anhören
Abschnitt betitelt „Zum Anhören“Du kannst dir diesen Text mit der Vorlesefunktion deines Browsers oder Betriebssystems kostenlos anhören, etwa mit “Laut vorlesen” in Microsoft Edge. Das eignet sich gut zum Wiederholen vor der Schularbeit.
Lernergebnisse: Was du nach diesem Kapitel können solltest
Abschnitt betitelt „Lernergebnisse: Was du nach diesem Kapitel können solltest“- Anwenden: zu einer Beschreibung ein Klassendiagramm mit Sichtbarkeit und Multiplizitäten und ein Sequenzdiagramm mit
alt-Fragment zeichnen. - Erklären: den Unterschied zwischen Aggregation und Komposition erklären.
- Anwenden: ein Klassendiagramm in Code überführen und Multiplizitäten in Datenstrukturen übersetzen.
- Analysieren: Sequenz- und Klassendiagramm gegeneinander auf Widersprüche prüfen.
Passende Übungen
Abschnitt betitelt „Passende Übungen“- Aufgabe 05 - Jahresprojekt Sprint 1 - Klassen und Abläufe: Kata: Klassendiagramm eines Schulradio-Sendeplans mit Multiplizitäten, Komposition und Beziehungsklasse (Wesentlich_überwiegend); Klassendiagramm der Kern-Entitäten gegen den Code, Sequenzdiagramm des Hauptablaufs mit Gegenprobe (Wesentlich_vollständig). PlusPlus: Multiplizitäten im Code umsetzen; MandalorianPlus: Vererbung mit dem Ist-ein-Test prüfen, zweiter Ablauf.
Autoren
Abschnitt betitelt „Autoren“GAM
CC-BY-SA