Verhaltensdiagramm

Sequenzdiagramm

In welcher Reihenfolge tauschen Objekte Nachrichten aus?

Eine Aufgabe beschreibt, wie eine Bestellung durch mehrere Objekte wandert, und du sollst zeigen, wer wen in welcher Reihenfolge aufruft. Genau dafür ist das Sequenzdiagramm da: Jeder Beteiligte bekommt eine senkrechte Lebenslinie, die Zeit läuft von oben nach unten. Nachrichten erscheinen als Pfeile zwischen Lebenslinien oder als Schleifen bei Selbstaufrufen. In der IHK-Prüfung ist es die übliche Antwort, wenn ein Ablauf zwischen Objekten mit Aufrufen und Antworten beschrieben wird.

Bestellung aufgeben · Sequenzdiagramm UML 2.5.1
Sequenzdiagramm: Kunde ruft bestellen am Shop auf, Shop prüft Lager, legt Bestellung an und antwortet Kunde shop : Shop lager : Lager bestellen(warenkorb) verfuegbar(artikel) verfuegbar(): true anlegen() bestellen(): bestellung

Nachrichten im Vergleich.

Synchron a : Client b : Server anfrage(id) anfrage(): ergebnis
Asynchron a : Client b : Server benachrichtigen(ereignis)
Antwort a : Client b : Server saldo() saldo(): betrag
Selbstaufruf a : Client b : Server speichern() validieren()
Erzeugen und zerstören a : Shop b : Warenkorb «create» leeren() «destroy»
Fragmente alt a : Client b : Server [saldo >= betrag][else] abbuchen(betrag) ablehnen()

Durchgezogene Linie mit gefüllter Spitze.

Ein Objekt braucht ein Ergebnis von einem anderen und kann bis dahin nicht weiterarbeiten. Genau das ist eine synchrone Nachricht: Der Sender ruft eine Operation auf und steht still, bis sie beendet ist. Du zeichnest eine durchgezogene Linie mit gefüllter Spitze und schreibst den Operationsnamen mit Klammern daran. Beim Empfänger läuft für die Dauer des Aufrufs ein Ausführungsbalken, also das schmale Rechteck auf der Lebenslinie.

Durchgezogene Linie mit offener Spitze.

Manchmal soll ein Objekt nur Bescheid geben und sofort weiterarbeiten, etwa wenn eine Benachrichtigung verschickt wird. Dafür gibt es die asynchrone Nachricht: Der Sender übergibt sie und wartet nicht auf ein Ergebnis. Gezeichnet wird sie als durchgezogene Linie mit offener, also nicht ausgefüllter Spitze. Eine Antwort gehört nicht dazu; kommt später doch etwas zurück, ist das eine eigene Nachricht.

Gestrichelte Linie mit offener Spitze.

Ist ein synchroner Aufruf fertig, gibt der Empfänger die Kontrolle zurück, oft zusammen mit einem Wert. Diese Rückgabe zeichnest du als gestrichelte Linie mit offener Spitze zurück zum Sender. Die Beschriftung darf den Operationsnamen und den Rückgabewert nach einem Doppelpunkt enthalten, etwa saldo(): betrag. Auch eine gefüllte Pfeilspitze ist nach UML zulässig. UML erlaubt, die Antwort wegzulassen, wenn das Diagramm auch ohne sie eindeutig bleibt; in Prüfungslösungen steht sie meistens dabei. Gestrichelt heißt hier immer Antwort, mit einer Ausnahme: dem Pfeil auf den Kopf einer neu erzeugten Lebenslinie.

Nachricht an die eigene Lebenslinie.

Ein Objekt erledigt einen Teilschritt selbst, zum Beispiel die Prüfung der Eingabe, bevor es weitergibt. Dann schickt es eine Nachricht an die eigene Lebenslinie. Die Linie verlässt den Ausführungsbalken nach rechts und kehrt kurz darunter auf dieselbe Lebenslinie zurück. Für die innere Ausführung zeichnest du einen zweiten Balken, versetzt auf dem ersten, damit sichtbar bleibt, dass der äußere Aufruf noch läuft.

Gestrichelter Pfeil auf den Kopf, Kreuz am Ende.

Nicht jedes Objekt existiert von Anfang an; eine Bestellung entsteht erst mitten im Ablauf. Die Erzeugungsnachricht ist gestrichelt und zeigt waagerecht auf den Kopf der neuen Lebenslinie, der deshalb tiefer sitzt als die übrigen Köpfe. Endet die Lebensdauer noch im Diagramm durch eine Lösch-Nachricht, muss deren Pfeil direkt am Kreuz enden. Als Beschriftung ist zum Beispiel «destroy» möglich. Unterhalb des Kreuzes darf das Objekt keine Nachricht mehr bekommen.

Rahmen mit Operator und Bedingungen in eckigen Klammern.

Sobald ein Ablauf eine Bedingung, eine Wiederholung oder Nebenläufigkeit enthält, reicht die reine Folge von Pfeilen nicht mehr aus. Dafür gibt es kombinierte Fragmente: einen Rahmen um die betroffenen Nachrichten, mit dem Operator oben links in der Ecke. alt steht für Alternativen, von denen höchstens eine ausgeführt wird, opt für einen Block, der nur unter einer Bedingung läuft, loop für Wiederholung und par für nebenläufige Teile. Die Bedingung heißt Guard und steht in eckigen Klammern am Anfang des jeweiligen Operanden.

Symbole und Notation.

15 Elemente

Lebenslinie shop : Shop : Lager

Lebenslinie

Kopf mit name : Klasse, gestrichelte Linie

Zeigt einen Beteiligten über die gesamte Zeit des Ablaufs, im Kopf beschriftet mit objekt : Klasse. Steht dort nur : Lager, ist irgendein Objekt dieser Klasse gemeint. UML 2.5.1 unterstreicht den Kopf nicht, viele Lehrbücher und Prüfungslösungen tun es trotzdem; die hier gezeigte Notation folgt UML 2.5.1.

Akteur Kunde

Akteur

Strichmännchen als Lebenslinie

Ein Beteiligter außerhalb des Systems, meist ein Mensch, der den Ablauf anstößt. Sein Kopf ist das Strichmännchen, darunter läuft die Lebenslinie wie bei jedem anderen Beteiligten. Üblicherweise steht er ganz links, weil die erste Nachricht von ihm ausgeht.

Ausführungsbalken a : Shop

Ausführungsbalken

Schmales Rechteck auf der Lebenslinie

Zeigt die Dauer einer Ausführung, einschließlich möglicher Wartezeiten auf weitere Aufrufe. Für eine aufgerufene Operation reicht der Balken typischerweise vom Empfang bis zur Antwort. Die Darstellung der Balken ist optional, sofern die Aufgabe sie nicht verlangt.

Synchrone Nachricht a : A b : B berechnen()

Synchrone Nachricht

Durchgezogen, gefüllte Spitze

Ein Operationsaufruf, bei dem der Sender wartet, bis der Empfänger fertig ist. Beschriftet wird sie mit dem Operationsnamen und den Klammern, bei Bedarf mit Parametern. Das Ergebnis kommt als gestrichelte Antwort zurück.

Asynchrone Nachricht a : A b : B melden(ereignis)

Asynchrone Nachricht

Durchgezogen, offene Spitze

Ein Signal oder Ereignis, nach dem der Sender sofort weiterarbeitet. Es gibt keine Antwort, auf die er wartet. Typisch für Benachrichtigungen und Meldungen an ein anderes System.

Antwort a : A b : B berechnen(): ergebnis

Antwort

Gestrichelt, offene Spitze

Die Rückgabe eines synchronen Aufrufs, gezeichnet zurück zum ursprünglichen Sender. Die Beschriftung kann den Operationsnamen und nach einem Doppelpunkt den Rückgabewert enthalten, etwa berechnen(): ergebnis. UML erlaubt auch eine gefüllte Spitze. Sie darf entfallen, wenn der Ablauf auch ohne sie eindeutig bleibt.

Selbstaufruf a : Shop pruefen()

Selbstaufruf

Schleife an der eigenen Lebenslinie

Ein Objekt ruft eine eigene Operation auf, zum Beispiel eine interne Prüfung. Die Nachricht geht von der Lebenslinie ab und kehrt kurz darunter auf dieselbe Linie zurück. Für die innere Ausführung kommt ein zweiter Balken versetzt auf den ersten.

Erzeugung a : Shop b : Bestellung «create»

Erzeugung

Gestrichelter Pfeil auf den Kopf

Das Objekt entsteht erst an dieser Stelle des Ablaufs. Deshalb sitzt sein Kopf nicht oben, sondern auf Höhe der Nachricht. Der Pfeil ist gestrichelt und zeigt waagerecht auf den Kopf.

Zerstörung a : Shop b : Warenkorb «destroy»

Zerstörung

Kreuz am Ende der Lebenslinie

Das Kreuz beendet die Lebenslinie: Danach existiert das Objekt nicht mehr und kann keine Nachricht mehr empfangen. Eine Lösch-Nachricht muss direkt am Kreuz enden; ihre Beschriftung kann etwa «destroy» lauten.

alt alt a : A b : B [ok][else] bestaetigen() ablehnen()

alt

Rahmen mit Operanden und [Guards]

Der Rahmen enthält mehrere durch eine gestrichelte Linie getrennte Abschnitte, die Operanden. Ausgeführt wird höchstens ein Operand, dessen Bedingung wahr ist. Treffen mehrere Guards zu, legt UML keine Priorität fest. [else] gilt, wenn alle anderen Guards falsch sind.

opt opt a : A b : B [gutschein] rabatt()

opt

Rahmen mit einem [Guard]

Ein Block mit nur einem Operanden, der ausgeführt wird, wenn die Bedingung gilt, und sonst übersprungen wird. Das entspricht einem alt mit einem leeren zweiten Operanden.

loop loop a : A b : B [für jede Position] preis(pos)

loop

Rahmen mit [Fortsetzungsbedingung]

Der Inhalt wird wiederholt, solange die Bedingung in den eckigen Klammern gilt; sie ist also eine Fortsetzungsbedingung, keine Abbruchbedingung. Mit loop(min, max) legst du zusätzlich die Zahl der Durchläufe fest, loop(1, 5) also mindestens einmal und höchstens fünfmal.

par par a : A b : B mail() sms()

par

Rahmen mit nebenläufigen Operanden

Die Operanden laufen nebenläufig, ihre Nachrichten können sich also zeitlich mischen. Innerhalb eines Operanden bleibt die Reihenfolge erhalten, zwischen den Operanden ist sie offen. Guards können die Operanden einschränken; ohne Guard gilt ein Operand als freigegeben.

ref a : A b : B ref Anmeldung

ref

Rahmen mit Namen eines anderen Diagramms

Verweist auf eine Interaktion, die in einem eigenen Sequenzdiagramm steht. So bleibt ein großer Ablauf lesbar, weil ein bekannter Teilablauf nur mit Namen auftaucht statt noch einmal vollständig gezeichnet zu werden.

break break a : A b : B [fehler] fehler

break

Rahmen, der den Rest abbricht

Gilt die Bedingung, wird der Inhalt des Rahmens ausgeführt und der umgebende Ablauf danach abgebrochen. Der Rest des umgebenden Fragments läuft dann nicht mehr. Passt für Abbrüche im Fehlerfall.

So liest und zeichnest du ein Sequenzdiagramm.

Wozu das Diagramm da ist

In der Prüfung steht ein Ablauf als Fließtext da: Ein Kunde gibt eine Bestellung auf, das System prüft die Verfügbarkeit beim Lager, legt eine Bestellung an und lässt bezahlen. Aus dem Text geht nicht auf einen Blick hervor, wer wen aufruft und was in welcher Reihenfolge passiert. Das Sequenzdiagramm macht genau das sichtbar: Jeder Beteiligte bekommt eine senkrechte Lebenslinie, jede Nachricht einen waagerechten Pfeil, und die Reihenfolge ergibt sich aus der Höhe der Pfeile.

Es beschreibt mögliche Abläufe eines Szenarios. Mit Alternativen, Schleifen und Nebenläufigkeit kann ein Diagramm mehrere mögliche Durchläufe festlegen. Wenn du wissen willst, welche Klassen es überhaupt gibt, ist das Klassendiagramm zuständig; das Sequenzdiagramm beantwortet nur die Frage nach dem zeitlichen Zusammenspiel.

Woher die Notation kommt

UML wird von der Object Management Group (OMG) standardisiert, einem Konsortium, das Modellierungssprachen pflegt. Diese Seite folgt UML 2.5.1, Kapitel 17 „Interactions“. Die Spezifikation legt fest, welche Symbole es gibt und was sie bedeuten, also zum Beispiel dass eine gefüllte Pfeilspitze einen synchronen Aufruf meint. Sie schreibt nicht vor, wie das Blatt aussieht: Layout, Farben, Schriftgröße und Werkzeug sind frei.

An einer Stelle weichen Prüfungslösungen sichtbar von der Spezifikation ab. UML 2.5.1 unterstreicht den Kopf einer Lebenslinie nicht, viele Lehrbücher und IHK-Lösungen tun es trotzdem. Hier verwenden wir die nicht unterstrichene Notation nach UML 2.5.1. Beachte zusätzlich die Vorgaben deiner Aufgabe.

So liest du es

Fang oben links an. In der Kopfzeile stehen die Beteiligten mit "objekt : Klasse", darunter läuft ihre Lebenslinie nach unten. Auf jeder Lebenslinie liest du die Ereignisse von oben nach unten. Zwischen unterschiedlichen Lebenslinien ergibt die Höhe allein keine globale Reihenfolge; Nachrichten und Fragmente legen weitere Abhängigkeiten fest. Absolute Abstände bedeuten nichts, ein doppelt so langer Abschnitt dauert nicht doppelt so lange.

Dann folgst du den Pfeilen von oben nach unten. Am Pfeil steht die Nachricht, bei einem Aufruf der Operationsname mit Klammern, bei einer gestrichelten Antwort zum Beispiel der Operationsname mit dem Rückgabewert nach einem Doppelpunkt. Die Spitze sagt dir, ob der Sender wartet: gefüllt heißt bei einem durchgezogenen Aufruf synchron und wartend, offen heißt dort asynchron. Antworten sind gestrichelt und dürfen eine offene oder gefüllte Spitze haben. Der Ausführungsbalken, also das schmale Rechteck auf der Lebenslinie, zeigt die Dauer einer Ausführung, einschließlich möglicher Wartezeiten. Umschließt ein Rahmen mehrere Nachrichten, liest du zuerst den Operator oben links und den Guard in eckigen Klammern: Er sagt, unter welcher Bedingung oder wie oft der Inhalt überhaupt abläuft.

So zeichnest du es in der Prüfung

  1. 1Unterstreiche in der Aufgabenstellung alles, was handelt oder angesprochen wird: den Auslöser, die beteiligten Klassen und fremde Systeme. Daraus werden deine Lebenslinien. Was nur ein Wert oder Parameter ist, etwa der Warenkorb als Übergabe, bekommt keine eigene Linie.
  2. 2Ordne sie von links nach rechts in der Reihenfolge, in der sie im Ablauf zum ersten Mal vorkommen. Der Auslöser steht ganz links, oft als Akteur mit Strichmännchen. So kreuzen sich später möglichst wenige Pfeile.
  3. 3Zeichne die Köpfe mit "objekt : Klasse" und darunter die gestrichelten Lebenslinien. Objekte, die im Ablauf erst entstehen, lässt du zunächst weg; ihr Kopf kommt später auf die Höhe der Erzeugung.
  4. 4Schreibe die Sätze der Aufgabe als Nachrichtenfolge untereinander, in der Reihenfolge des Textes. Meistens steht jeder Aufruf schon als Verb da: "prüft die Verfügbarkeit" wird zu verfuegbar(artikel). Bei geschachtelten Aufrufen gilt: Was ein Objekt braucht, bevor es antworten kann, steht darüber.
  5. 5Zeichne die Pfeile mit der passenden Spitze: gefüllt, wenn der Sender auf ein Ergebnis wartet, offen, wenn er sofort weiterarbeitet. Ergänze Ausführungsbalken und gestrichelte Antworten, wenn sie den Ablauf verdeutlichen oder die Aufgabe sie verlangt.
  6. 6Ziehe Rahmen um alles, was bedingt, wiederholt oder nebenläufig abläuft: opt für einen Block mit einer Bedingung, alt mit else für Alternativen, loop für Wiederholungen, par für Nebenläufigkeit. Der Guard gehört in eckige Klammern in den Operanden, nicht an den Pfeil.
  7. 7Ergänze zum Schluss Erzeugung und Zerstörung: den gestrichelten Pfeil auf den tiefer sitzenden Kopf, und dort, wo ein Objekt endet, die Lösch-Nachricht direkt auf das Kreuz am Ende der Lebenslinie.

Prüfe dich selbst: Ein Shop meldet dem Versanddienst, dass ein Paket abgeholt werden soll, und macht sofort mit der Rechnung weiter. Zeichnest du diese Nachricht mit gefüllter oder mit offener Spitze, und braucht sie eine Antwort?

Typische Prüfungsfehler.

6 Paare

Antwort mit durchgezogener Linie.

falsch Falsch: Antwort mit durchgezogener Linie. a : Shop b : Lager bestand() bestand(): 12
richtig Richtig: Antwort mit durchgezogener Linie. a : Shop b : Lager bestand() bestand(): 12

Wer den Rückweg als normalen Pfeil zeichnet, denkt an den Datenfluss und nicht an die Notation. Im Diagramm entsteht dadurch aber ein neuer synchroner Aufruf in Gegenrichtung, das Lager würde also den Shop aufrufen. Zeichne die Antwort gestrichelt, hier mit offener Spitze, und beschrifte sie etwa mit bestand(): 12. UML erlaubt für Antworten auch eine gefüllte Spitze.

Synchroner Aufruf mit offener Spitze.

falsch Falsch: Synchroner Aufruf mit offener Spitze. a : Shop b : Lager bestand() bestand(): 12
richtig Richtig: Synchroner Aufruf mit offener Spitze. a : Shop b : Lager bestand() bestand(): 12

Die offene Spitze ist schneller gezeichnet, und der Unterschied fällt beim Skizzieren kaum auf. In UML trägt die Spitze aber die Bedeutung: offen heißt asynchron, der Sender arbeitet sofort weiter. Wenn der Sender bis zum Ende des Aufrufs wartet, gehört die gefüllte Spitze hin. Eine spätere eigenständige Rückmeldung ist auch bei asynchroner Kommunikation möglich.

Bedingung ohne Fragmentrahmen.

falsch Falsch: Bedingung ohne Fragmentrahmen. a : Shop b : Lager wenn ok: reservieren()
richtig Richtig: Bedingung ohne Fragmentrahmen. opt a : Shop b : Lager [ok] reservieren()

Aus Pseudocode und Struktogrammen ist man gewohnt, die Bedingung direkt an den Schritt zu schreiben. Im Sequenzdiagramm gibt es dafür keine Notation, der Text am Pfeil ist nur die Nachricht selbst. Bedingte Abläufe kommen in ein opt- oder alt-Fragment, die Bedingung steht als Guard in eckigen Klammern oben im Operanden.

Erzeugung als normale Nachricht.

falsch Falsch: Erzeugung als normale Nachricht. a : Shop b : Bestellung new Bestellung()
richtig Richtig: Erzeugung als normale Nachricht. a : Shop b : Bestellung «create»

Weil alle anderen Köpfe oben stehen, setzt man auch den neuen dorthin und schreibt den Konstruktoraufruf an den Pfeil. Dann sagt das Diagramm aber, dass das Objekt von Anfang an existiert hat. Setze den Kopf auf die Höhe der Erzeugung und zeichne den Pfeil gestrichelt darauf.

Aufgerufene Ausführung beginnt vor dem Empfang.

falsch Falsch: Aufgerufene Ausführung beginnt vor dem Empfang. a : Shop b : Lager bestand() bestand(): 12
richtig Richtig: Aufgerufene Ausführung beginnt vor dem Empfang. a : Shop b : Lager bestand() bestand(): 12

Die hier gezeigte Ausführung von bestand() wird erst durch den Aufruf gestartet. Ihr Balken darf deshalb nicht vor dessen Empfang beginnen. Zeichne ihn vom Empfang bis zur Antwort. Ausführungsbalken grundsätzlich wegzulassen, ist dagegen kein UML-Fehler.

Objekt zerstört, Lebenslinie läuft weiter.

falsch Falsch: Objekt zerstört, Lebenslinie läuft weiter. a : Shop b : Warenkorb «destroy»
richtig Richtig: Objekt zerstört, Lebenslinie läuft weiter. a : Shop b : Warenkorb «destroy»

Die Lebenslinie wird meist in einem Zug bis zum unteren Rand durchgezogen, bevor die Nachrichten dazukommen. Läuft sie nach «destroy» weiter, sagt das Diagramm, dass das Objekt noch da ist und weitere Nachrichten empfangen könnte. Beende sie stattdessen auf Höhe der Zerstörung mit einem Kreuz.

Zwei vollständige Beispiele.

Bestellung: loop über die Positionen, alt mit else, Erzeugung, Fremdsystem.

Lies von oben nach unten: Der Kunde ruft bestellen auf, danach fragt der Shop im loop-Rahmen für jede Position beim Lager die Verfügbarkeit ab. Sind alle Artikel da, gilt der obere Operand des alt-Rahmens, der Shop erzeugt die Bestellung und lässt den Zahlungsdienst abbuchen und bestätigt dem Kunden die Bestellung. Der else-Operand darunter behandelt fehlenden Bestand: Dann wird keine Bestellung erzeugt und der Kunde erhält einen Fehler. Der Ausschnitt setzt eine erfolgreiche Zahlung voraus; Zahlungsablehnungen und konkurrierende Bestandsänderungen sind hier nicht modelliert.

Bestellung aufgeben · Sequenzdiagramm UML 2.5.1
Sequenzdiagramm einer Bestellung mit loop, alt, create loop alt Kunde shop : Shop lager : Lager b : Bestellung : Zahlungsdienst [für jede Position][alle verfügbar][else] bestellen(warenkorb) verfuegbar(artikel, menge) verfuegbar(): verfügbar summe() «create» positionen(warenkorb) zahlen(summe) zahlen(): bestaetigung bestellen(): b bestellen(): fehler

Anmeldung: Selbstaufruf, par innerhalb von alt, asynchrone Benachrichtigung.

Der AuthService lädt den Nutzer und prüft die Zugangsdaten in einem Selbstaufruf. Diese Prüfung liefert auch bei einem unbekannten Nutzer false. Nur bei gültigem Zugang erzeugt er ein Token. Im par-Rahmen verschickt er unabhängig voneinander den Login-Hinweis und den Protokollierungsauftrag als asynchrone Nachrichten. Erst nach beiden Sendeereignissen gibt er das Token zurück; auf die Verarbeitung durch Mailer und Datenbank wartet er nicht. Der else-Operand liefert einen Fehler. Die LoginSeite zeigt anschließend das jeweilige Ergebnis an.

Anmeldung · Sequenzdiagramm UML 2.5.1
Sequenzdiagramm einer Anmeldung mit alt, par und Selbstaufruf alt par Nutzer : LoginSeite : AuthService : NutzerDB : Mailer [zugang gültig][else] anmelden(name, pw) pruefen(name, pw) lade(name) lade(): nutzer zugangOK(nutzer, pw) tokenErstellen(nutzer) loginHinweis(nutzer) loginLog(nutzer) pruefen(): token pruefen(): fehler anmelden(): anzeige