UML-Zustandsdiagramme mit PlantUML erstellen

PlantUML ist ein vielseitig nutzbares Tool, um testbasiert versionierbare (UML-)Diagramme zu erstellen. Aus einfachen Quelltexten lassen sich so UML-Diagramme als PNG, ASCII-Art oder SVG erstellen. PlantUML ist als Webservice verfügbar (z.B. https://www.planttext.com/), in vielen IDEs (als Plugin) integriert und wird von einigen Markdown-Interpretern direkt umgewandelt. Allgemeine weiter Infos zu PlantUML finden sich hier: https://www.planttext.com/

Da die Anleitung zu UML-Diagrammen nicht immer den aktuellen UML-Standard im Blick haben, versuche ich hier praktische Tipps zu geben, wie standardkonforme UML-Zustandsdiagramme mit PlantUML erstellt werden können.

Grundbausteine des UML-Zustandsdiagramms

UML-Zustandsdiagramme bestehen im Wesentlichen aus einem Startzustand, einzelnen Zuständen, einem Endzustand (optional) sowie Transitionen, die die Zustände verbinden. In PlantUML werden Zustände zunächst deklariet (state Zustand1 oder bei längerem Namen mit Alias state "Dargestellter Name des \nZustands 2" as Zustand2) und die verbindenden Transitionen in Zeilen mit stilisiertem ASCII-Pfeil beschrieben:

state hungrig
state "satt" as wohlgenährt
hungrig -> wohlgenährt
Die Zustände hungrig und satt
Die Zustände hungrig und satt

Start- und Endzustand werden in PlantUML mit [*] notiert. An Hand der Pfeilrichtung wird automatisch erkannt, ob es mit sich um einen Start- oder einen Endzustand handelt:

left to right direction
[*] --> Zustand1
Zustand1 --> [*]
Der Pseudozustand “Start” und der Endzustand
Der Pseudozustand “Start” und der Endzustand

Transitionen

An den Transitionen können - wie in der UML üblich - Event (Trigger), Guard (Bedingung) und Effekt (Aktion während der Transition) notiert werden (in der Form: startzustand -> zielzustand: Trigger [Guard] / Effekt). In PlantUML sieht es wie folgt aus:

state hungrig
state satt

hungrig -right-> satt : essen [wenn Essen da] /schmatzen()
Die Zustände hungrig und satt mit Trigger, Guard und Effekt an den Transitionen
Die Zustände hungrig und satt mit Trigger, Guard und Effekt an den Transitionen

Innere Aktionen der Zustände

Die Zustände können mit inneren Aktionen versehen werden, sie werden mit Hilfe des Zustandsnamens und folgendem Doppelpunkt notiert - entweder nach dem Muster

Event [Guard] / interneAktion

oder entsprechend der drei internen Events: entry, do, exit:

state schlafend
schlafend : entry/Augen zu
schlafend : do/träumen
schlafend : exit/Augen auf
schlafend : Vollmond [Werwolf == true]/Schlafwandeln


state wach
wach : entry/Kaffee trinken
wach : do/arbeiten
wach : exit/Zähneputzen


wach -right-> schlafend : [Müdigkeit > Wille] /Schlafanzug anziehen
schlafend -left-> wach : Weckerklingeln [zeit>=weckzeit] /aufstehen
schlafend -left-> schlafend : aufschrecken [zeit<weckzeit] /weiterschlafen
Innere Aktionen (entry, do, exit) in Zuständen
Innere Aktionen (entry, do, exit) in Zuständen

Verzweigungen (Choices)

Um dynamische Entscheidungen (Choices) in PlantUML einzubinden muss die Raute als Pseudozustand mit dem Stereotyp <<choice>> deklariert werden und danach wie jeder andere Zustand eingebunden werden:

state State1: entry/a=1

state entscheidung <<choice>>
state State2
state State3
State1 -> entscheidung : trigger /a++
entscheidung -> State2 :[a == 1]
entscheidung -> State3 :[else]

Wichtig: bei Choices wird der Guard erst nach dem Effekt ausgewertet. In folgendem Beispiel wird a (in State1: a = 1) also erst hochgezählt (a++, also a=2) und dann der Guard ausgewertet ((a == 1) = false) - es wird also immer die [else]-Transition genommen und State3 erreicht.

Innere Aktionen (entry, do, exit) in Zuständen
Innere Aktionen (entry, do, exit) in Zuständen

Kreuzungen (Junctions)

Sofern die Guards bereits vor den Effekten ausgewertet werden sollen (statische Entscheidungen / junctions), müssen im UML-Diagramm Knoten (ausgefüllte Kreise) notiert werden. In PlantUML ist das aber nicht möglich. Daher müssen diese zusammenhängenden Transitionen als einzelne Transitionen mit identischem Trigger und unterschiedlichen Guards modelliert werden:

state State1
State1 : entry/ a=1

state State2
state State3

State1 -right-> State2 : trigger[a == 1]/a++
State1 --right-> State3 :trigger[else]/a++
Kreuzungen (Junctions) müssen in Plantuml als einzelne Transitionen modelliert werden
Kreuzungen (Junctions) müssen in Plantuml als einzelne Transitionen modelliert werden

Zusammengesetzte Zustände (composed states)

Mit Hilfe von geschweiften Klammern können composed states beliebig verschachtelt werden:

state schlafend
state wach{
    state hungrig
    state satt
    hungrig -> satt : gegessen
    satt -> hungrig : verdaut
}

schlafend -> wach : Wecker
wach -> schlafend : einschlafen
Composed States
Composed States

Wenn Sie nur als Verweis referenziert werden sollen muss man mit PlantUML allerdings etwas zaubern, um das Symbol, das zwei Zustände zeigt in die Ecke zu schreiben (Workaround: Tab (Unicode #9) zur Einrückung und Unendlich-Symbol (Unicode #8734) zur Darstellung der Unterzustände):

@startuml
state "zusammengesetzter\nZustand"  as subState
subState: &#9;&#9;&#9;&#9;<b>&#8734;</b>
@enduml
Composed States
Composed States

Regionen in zusammengesetzten Zuständen

Mit Hilfe von zwei Bindestrichen lassen sich Regionen mit Unterzuständen definieren.

@startuml
left to right direction
state wach {
  [*] --> zufrieden
  zufrieden --> unzufrieden : Sonne
  unzufrieden --> zufrieden : Regen
  --
  [*] --> neugierig
  neugierig --> gelangweilt : warten
  gelangweilt --> neugierig : loslegen
}
@enduml
Substates in Regionen aufgeteilt
Substates in Regionen aufgeteilt

Improvisationen für nicht vorhandene Notationselemente (History / Terminator)

Nutzt man die erweiteten PlantUML-Funktionen, so lassen sich auch Notationselemente wie der Terminator (Beenden des Zustandsautomaten) und die Speicherfunktion eines Unterzustands (History) abbilden:

Über sprite lassen sich beliebige pngs als Base64url einfügen, die per java -jar plantuml.jar -encodesprite 16z test.png umgewandelt werden und per <$spriteName> angesprochen werden. Der Terminator wird als sprite in einen Zustand geschrieben, dessen Ränder und Schrift per Stereotyp auf weiß gestellt sind. Darüber hinaus wurden auch alle Notizenzettel auf weiß gestellt, so dass in diesen Boxen die Regionsnamen und das History-Objekt einigermaßen notationskonform dargestellt werden kann. Notizen sind so allerdings nicht mehr möglich.

@startuml
left to right direction
skinparam state {
  BackgroundColor<<terminate>> White
  FontColor<<terminate>> White
  BorderColor<<terminate>> White
}
skinparam  NoteBorderColor White
skinparam  NoteBackgroundColor White


skinparam stateShape start
skinparam shadowing false
sprite $history [30x30/16z] bTE54OKm50NHxM3xh_OBDfQTHoxEGQAyZLV2dfEmjIjzhP2iaApGBCluFDH7MUe9jWfIoevUsTVz_quredLkIKg77cpMOUGawpHYUxJFCsNE5m
sprite $terminator [30x30/16z] RSat0G0n5CMmp-3nHtjbygBqMDlEO_-8WJ4m5dOJmILoeH-nFx52h17ZK1oQGxCnDSR2s7ZaQWm4L_eO-neBm9An6993S0WEpIy

skinparam  NoteBorderColor White
skinparam  NoteBackgroundColor White
state wach {
  note left of zufrieden : [glücklich]
  [*] --> zufrieden
  zufrieden --> unzufrieden : Sonne
  unzufrieden --> zufrieden : Regen
  note right of unzufrieden : <$history>
  unzufrieden --> [*] : einschlafen
  --
  note left of neugierig : [interessiert]
  [*] --> neugierig
  neugierig --> gelangweilt : warten
  gelangweilt --> neugierig : loslegen
  gelangweilt -->end<<terminate>> :totlangweilen
 end : <$terminator>
}
@enduml

Geerbte Zustände

Auch bei Zuständen gibt es eine Erbfolge - von Elternzuständen geerbte Zustände werden mit gestrichelter Linie dargestelle - in PlantUML folgendermaßen:

@startuml
state gererbterZustand ##[dashed] {
}
@enduml

plantUML-Webservice zur Diagrammerzeugung nutzen

PlantUML bietet einen Webservice, der Diagramme unmittelbar online rendert.

  • Variante 1: Codierter Quelltext: PlantUML kodiert den Quelltext, um ihn so über den Webservice zugänglich (und bearbeitbar) zu machen. Die URL ist in diesem Fall wie folgt aufgebaut: http://www.plantuml.com/plantuml/AUSGABEFORMAT/CODIERTERQUELLTEXT wobei als AUSGABEFORMAT png, svg, eps, epstext und txt genutzt werden kann. Wird als AUSGABEFORMAT uml gewählt erhält man den bearbeitbaren Quelltext.

  • Variante 2: Editierbare Links lassen sich auch über den Service von planttext.com erstellen, sie nutzen die selbe Quelltextcodierung und URLs nach dem Muster: https://www.planttext.com/?text=CODIERTERQUELLTEXT Auch in den erzeugten PNG-Dateien wird der codierte Quelltext als Titel hinterlegt, so dass sie sich relativ einfach später weiterverarbeiten lassen.

  • Variante 3: Quelltext direkt online rendern: der PlantUML-Quelltext ist online als Resource verfügbar und soll gerendert ausgegeben werden. Hierzu muss eine URL nach dem Muster: http://www.plantuml.com/plantuml/proxy?fmt=AUSGABEFORMAT&src=https://URL_PLANTUMLSOURCE erstellt werden, wobei als AUSGABEFORMAT png, svg, eps, epstext und txt genutzt werden kann. Diese Funktion scheint derzeit deaktiviert zu sein oder nur Entwicklern vorbehalten.

plantUML-Formatierung: Aufhübschen von UML-Diagrammen

Über Skins bietet PlantUML die Möglichkeit, die Diagramme in Schriftart, Farben und Aussehen individuell anzupassen.

Ich nutze für meine Diagramme derzeit die folgenden Einstellungen:

skinparam style strictuml

skinparam shadowing true

skinparam DefaultFontName "Lucida Sans Typewriter"

skinparam State{
    BackgroundColor snow
    BorderColor DarkSlateBlue
    DiamondBackgroundColor ghostwhite
    DiamondBorderColor DarkSlateBlue
}
skinparam Activity{
    DiamondBackgroundColor ghostwhite
    DiamondBorderColor DarkSlateBlue
}
skinparam Note{
    BorderColor DarkSlateBlue
    BackgroundColor LightYellow
}

skinparam ArrowColor DarkSlateBlue
skinparam lineType ortho

Codebeispiel: ( online bearbeitbar )

UML-Zustandsdiagramme per Formatierung als Entwurf kennzeichnen

Um den Entwurfscharakter eines Diagramms zu unterstützen kann ein Layout verwendet werden, dass Handschrift ähnelt. Besonders wirkungsvoll ist das, wenn auch die Schrift authentisch handschriftlich aussieht. Dies ist z.B. bei der Schriftart “FG Virgil” der Fall (diese muss aber auf dem System installiert sein). Welche Schriften das jeweilige System bietet lässt sich mit dem PlantUML-Befehl listfonts anzeigen. Wichtig ist: sofern eine Vektorgrafik ausgegeben wird (svg, eps) muss die Schrift auf allen anzeigenden Computern vorhanden sein, andernfalls werden Ersatzschriftarten verwendet.

Die ensprechenden Skin-Parameter sind:

skinparam style strictuml
skinparam DefaultFontName "FG Virgil"
skinparam handwritten true
skinparam monochrome true
skinparam packageStyle rect
skinparam shadowing false

Das Ergebnis sieht dann etwa so aus:

Entwurfscharakter durch Handschriftart hervorherben
Entwurfscharakter durch Handschriftart hervorherben

Diagramme vereinheitlichen und Formatierung auslagern

Damit nicht alle PlantUML-Diagramme die selben skinparam-Sequenzen am Beginn der plantUML-Datei nennen müssen, kann eine zentrale Konfigurationsdatei eingebunden werden, die die Formatierungen enthält:

!includeurl https://PATH.TO/MY/UMLSEQUENCE_CONFIG.cfg

Alle Befehle in dieser Datei werden ausgeführt, als stünden sie in der umgebenden PlantUML-Datei. Um ein einheitliches Layout zu erreichen ist diese Funktion sehr praktisch!

Es finden sich zahlreiche Quellen und Dokumentationen zu PlantUML im Netz, erwähnenswert sind u.a.:

Quellen und offene Ressourcen (OER)

Die Ursprungstexte (als Markdown), Grafiken und zugrunde liegende Diagrammquelltexte finden sich (soweit möglich in weiterbearbeitbarer Form) in folgendem git-Repository:

https://gitlab.com/oer-informatik/uml/umlzustand.

Sofern nicht explizit anderweitig angegeben sind sie zur Nutzung als Open Education Resource (OER) unter Namensnennung (H. Stein, oer-informatik.de) freigegeben gemäß der Creative Commons Namensnennung 4.0 International Lizenz (CC BY 4.0).

Creative Commons Lizenzvertrag

Kommentare gerne per Mastodon, Verbesserungsvorschläge per gitlab issue (siehe oben). Beitrag teilen: