UML-Klassendiagramme mit PlantUML zeichnen
https://bildung.social/@oerinformatik/110480281247180661
https://oer-informatik.de/uml-klassendiagramm-plantuml
tl/dr; (ca. 13 min Lesezeit): Das Tool PlantUML ist hervorragend geeignet, um Klassendiagramme direkt in Dokumentationen wie Readme.md-Dateien des Repositorys oder in Kommentaren zu integrieren, sie aus Programmcode direkt zu generieren und sie in einem Versionskontrollsystem zu versionieren. In diesem Infotext stelle ich vor, wie mit PlantUML die unterschiedlichen Notationsmittel von Klassendiagrammen genutzt werden können, mit welchen Einstellungen sie nahe am UML-Standard bleiben und aufgehübscht sind. (Zuletzt geändert am 04.06.2023)
Dieser Text ist ein Teil der Infotext-Serie zu UML-Klassendiagrammen:
Das Tool PlantUML
PlantUML ist ein Tool, um alle möglichen (UML-)Diagramme aus deklarativen Beschreibungen (PlantUML-Quelltext) zu generieren. Ein mit PlantUML erstelltes Klassendiagramm sieht beispielsweise so aus:

Da es aus Quelltext Diagramme erzeugt wird PlantUML häufig mit dem Buzzword Diagram as Code bezeichnet. Die Diagramme können per Webservice erstellt (z.B. planttext.com oder plantuml.com), als Code direkt in Markdown-Quelltexten eingebettet oder mithilfe einer lokalen Installation generiert werden.
Eine Dokumentation des Tools findet sich auf der Internetseite des Projekts.
PlantUML erstellt sehr schnell und einfach Diagramme, ist aber relativ unflexibel, was die Positionierung von Elementen angeht und weicht in der Darstellung oft vom UML-Standard ab.
Über ein paar Umwege kann man die Erstellung jedoch in Aussehen und Struktur beeinflussen. Neben der grundlegenden Syntax von PlantUML stelle ich diese Umwege hier vor.
Ein erstes UML-Klassendiagramm erstellen

PlantUML-Quellcode ist eingefasst zwischen @startuml
und @enduml
- die Notation selbst ist angelehnt an gängige Java-Notationen (class ... {}
) und UML-Eigenschaften (-, +, #, {abstract}
):
@startuml
class Konto{
-iban: String
-kontostand: double
#{static} kontenListe: Konto[0..*]
+Konto(iban: String)
+einzahlen(betrag:double):void
+auszahlen(betrag:double):boolean
+getKontostand():double
+kontoStatus():String
#{static} listeStatusAllerKonten():String
}
@enduml
Editieren und darstellen kann man die Diagramme am schnellsten über einen Webservice, das obige Beispiel findet sich unter dieser URL. Dabei wird der Quelltext als Hashwert übergeben - so lassen sich bearbeitbare Diagramme anlegen und verlinken. Dieser Hashwert kann auch genutzt werden, um Links zum PNG-Bild oder zu einem SVG-Bild zu erhalten. Als Besonderheit generiert PlanUML auch ASCII-Art-Diagramme. Für das obige Beispiel sieht das dann so aus:
,-----------------------------------------.
|Konto |
|-----------------------------------------|
|-iban: String |
|-kontostand: double |
|#{static} kontenListe: Konto[0..*] |
|+Konto(iban: String) |
|+einzahlen(betrag:double):void |
|+auszahlen(betrag:double):boolean |
|+getKontostand():double |
|+kontoStatus():String |
|#{static} listeStatusAllerKonten():String|
`-----------------------------------------'
Für jedes Diagramm in diesem Artikel lässt sich der Quelltext schnell nachschlagen: ich habe unter identischem Pfad wie die Bilddatei (*.png
) eine Datei mit dem PlantUML-Quelltext (*.plantuml
) hinterlegt. Für das folgende Bild findet sich der Quelltext beispielsweise hier.
Ich mag die bunten Symbole nicht, die einige UML-Tools in Klassendiagramme setzen. Sie sind nicht UML-konform und nicht selbsterklärend: Kreise, Rechtecke, Rauten.
Daher empfehle ich, diese mit dem hide circle
und skinparam
-Befehl zu deaktivieren. Ein PlantUML-Skript für Klassendiagramme sollte also in jedem Fall folgenden Aufbau haben:
@startuml
hide circle
skinparam classAttributeIconSize 0
' Hier werden die eigentlichen UML-Deklarationen ergänzt.
' Kommentare können wie hier hinter dem Tickzeichen angefügt werden.
@enduml

Eine Klasse mit Member notieren
In der einfachsten Form besteht eine Klasse nur aus dem Klassennamen. In PlantUML notiert man:
class Konto{
}

Die Klasse kann um Member (Attribute und Methoden) ergänzt werden. PlantUML erkennt dabei selbständig, ob es sich um eine Methode handelt (anhand der Klammer). Klassenattribute und -methoden werden mit {static}
notiert. Die Deklaration hat den folgenden Aufbau:
Instanz-Attribute:
attributname: Datentyp
(konkret:iban: String
)Instanz-Methoden:
methodenname(Parametername: Datentyp): RückgabeDatentyp
(konkret:auszahlen(betrag:double):boolean
)Klassenattribute:
{static} klassenAttribut: Datentyp
(konkret:{static} kontenListe: Konto[0..*]
)Klassenmethoden:
{static} klassenMethode(...): RückgabeDatentyp
(konkret:{static} listeStatusAllerKonten():String
)
Eine einfache Kontoklasse würde mit PlantUML folgendermaßen notiert:
class Konto{
iban: String
kontostand: double
{static} kontenListe: Konto[0..*]
Konto(iban: String)
einzahlen(betrag:double):void
auszahlen(betrag:double):boolean
getKontostand():double
kontoStatus():String
{static} listeStatusAllerKonten():String
}

Sichtbarkeitsmodifizierer
In PlantUML werden die Sichtbarkeitsmodifizierer in gleicher Weise und mit identischer Symbolik den Membern der Klasse vorangestellt wie in der UML.
public / sichtbar:
+
(im Diagramm unten z.B.+getIban():String
),private / versteckt:
-
(das gekapselte Attribut-iban: String
),protected / klassensichtbar:
#
(das Attribut#kontostand: double
),package / paketsichtbar:
~
(der Konstruktor:~Konto(iban:String)
)
Wichtig ist, dass im Kopf des PlantUML-Quelltexts die Option skinparam classAttributeIconSize 0
steht, da sonst andere Symbole (Quadrat statt -
, Raute statt #
, Kreis statt +
, Dreieck statt ~
) für die Sichtbarkeitsmodifizierer angezeigt werden.
In unserem Beispiel:
class Konto{
-iban: String
#kontostand: double
+getIban():String
+setIban(iban:String):void
~Konto(iban:String)
}

kontostand
Vererbung
Vererbungsbeziehungen werden in PlantUML mit stilisierten Pfeilen mit geschlossener Pfeilspitze notiert: -|>
Sie können ohne Richtungsangabe (default: nach rechts) oder gezielt per up
, down
, left
, right
ausgerichtet werden:
Investment <|- Konto
PrivatKonto -left-|> Konto
GeschaeftsKonto -up-|> Konto
VereinsKonto -down-|> Konto
PrivatKonto <|-right- SparKonto

Assoziation
Assoziationen werden als Linie zwischen zwei Klassen dargestellt. In PlantUML werden diese durch zwei Bindestriche notiert:
Kunde -- Rechnung

Assoziationen können mit Namen und Leserichtungen versehen werden. Der Name wird nach dem Doppelpunkt angegeben, die Leserichtung als stilisierter Pfeil vor (<
) oder nach (>
) dem Namen.
Kunde -- Rechnung: bezahlt >
Kunde -- Rechnung: < wird bezahlt von


Multiplizitäten
Multiplizitäten werden an den Enden in Anführungszeichen angegeben: Mitarbeiter "1..*" -- "0..*" Bank
Mitunter kann es sinnvoll sein, einen leeren Assoziationsnamen zu vergeben, da sonst die Klassen von PlantUML so eng nebeneinander gezeichnet werden, dass eine Zuordnung der Multiplititäten schwierig ist (siehe hinter Räder:“…”):
Fahrzeug "1"-"1..*" Räder: " "

Assoziationen lassen sich - analog zu den Vererbungen und ebenso wie alle Kanten im UML-Klassendiagramm - in jede Richtung legen:
Rechnung "*"--right--"1" Lieferanschrift: " "
Rechnung "1"--left--"1..*" Rechnungsposition: " "
Rechnung "*"-up-"0..1" Rechnungsanschrift
Rechnung "*"-down-"1..2" Steuersatz

Navigierbarkeit von Assoziationen
Wenn nicht durch ein offenes Kantenende unbestimmt bleiben soll, in welche Richtung eine Assoziation navigiert werden kann, so kann die Richtung in PlantUML per stilisiertem Pfeil >
explizit erlaubt oder per stilisiertem Kreuz x
explizit verboten werden:
Kunde x-> Rechnung : bezahlt >

Komposition und Aggregation
Die engeren Objektbeziehungen Aggregation (“ist Teil von”) und Komposition (“ist existenzabhängiges Teil von”) werden ebenso an den Kantenenden notiert:
Bei einer Aggregation wird ein kleines o
auf der Seite des Ganzen eingefügt:
Kunde o- Konto

Bei einer Komposition ersetzt ein Asterisk (*
) die ausgefüllte Raute aufseiten des Ganzen.
Rechnung *- Rechnungsposition

Diese Elemente lassen sich natürlich auch mit Assoziationsnamen, Leserichtung, Multiplizitäten usw. verbinden:

Abstrakte Klassen und Interfaces
Die Notation von Interfaces und abstrakter Klassen erfolgt analog der bisher dargestellten Logik:
Interfaces
Interfaces werden zwar mit dem Schlüsselwort Interface
in PlantUML bereits als solche gekennzeichnet, jedoch sieht man diese Kennzeichnung nur, wenn der hide circle
-Befehl nicht genutzt wird. Daher muss zusätzlich das Stereotyp <<Interface>>
in Guillemets (Doppelte spitze Klammern) angegeben werden:
interface Verzinsbar <<Interface>>

Die Implementierungspfeilspitze ist in der UML die gleiche wie bei der Vererbung. Entsprechend muss in PlantUML nur der Linientyp angepasst werden (gestrichelt), in dem statt der Bindestriche Punkte angegeben werden.
Verzinsbar <|.. Sparbuch

Abstrakte Methoden (also Methodenköpfe ohne Implementierung) lassen sich in PlantUML über das Schlüsselwort {abstract}
definieren (in geschweiften Klammern als Constraint formuliert). Sie werden dann UML-konform (wie oben) kursiv dargestellt.
{abstract} verrechneJahreszinsen()
Abstrakte Klassen
Klassen, die nicht instanziiert werden können (weil sie abstrakte Methoden enthalten) oder nicht instanziiert werden sollen, werden ebenso mit dem Schlüsselwort abstract
gekennzeichnet, hier aber nicht als Constraint, also ohne Klammern:
abstract class Investment{
{abstract} verrechneJahreszinsen()
}
PlantUML stellt abstrakte Strukturen dann kursiv dar:

Da die kursive Schreibweise leicht übersehen werden kann, ist zu empfehlen, zusätzlich den constraint {abstract}
in den Klassenkopf zu schreiben. Man muss hierzu bei PlantUML tricksen: Der Name wird um den constraint erweitert und die Klasse fortan über den Alias angesprochen:
abstract class "Investment\n{abstract}" as Investment{
{abstract} verrechneJahreszinsen()
}

Interfaces in Lollipop-Notation
Bei loser Kopplung wird die Objektbeziehung nicht über eine konkrete Referenz (also eine konkrete Klasse) hergestellt, sondern über eine Abstraktion - in der Regel ein Interface. Im UML-Diagramm lässt sich das wie folgt darstellen:

interface Buchbar <<Interface>>{
+{abstract} abbuchen(betrag:double): void
+{abstract} einzahlen(betrag: double): void
}
class Konto{...}
class Kunde{...}
Kunde .down.> Buchbar : <<use>>
Buchbar <|.down. Konto : " "
Kunde nutzt ein Objekt vom Typ Buchbar, um einen Artikel zu bezahlen (siehe Auszug aus der Methode bezahlen()
).
Wenn der Aufbau des Interfaces nicht so wichtig ist, wird häufig die Lollipop-Notation gewählt, die nur die Schnittstelle benennt, die Member des Interface aber nicht zeigt.

Damit PlantUML das Interface in Lollipop-Notation darstellt, muss das Interface selbst mit dem Schlüsselwort circle
gekennzeichnet werden (anstelle interface
im Beispiel oben). Die Abhängigkeit der Interface-Nutzung wird als stilisierte Buchse dargestellt: -(
.
class Konto
class Kunde
circle Buchbar
Kunde -( Buchbar
Buchbar - Konto
Die nutzende und die implementierende Klasse können auch jeweils einzeln mit dem Interface dargestellt werden. Die implementierende Klasse bietet die Schnittstelle an, was als “Stecker” / Lollipop symbolisiert wird -()
. Die nutzende Klasse bietet die passende Buchse -(
. Das Interface selbst muss wieder als circle
definiert werden.
circle Buchbar
Kunde -( Buchbar
Konto -() Buchbar

Leider stellt PlantUML auch bei der Klasse, die das Interface nutzt (hier: Kunde) den Kreis dar. Im UML-Standard würde hier nur die Buchse (socket) dargestellt und das Interface benannt. Eine wirklich minimale Abweichung von der Standard-Notation.
Aufzähungen: ENUMS
Aufzählungen werden in PlantUML als Klasse umgesetzt, die mit dem Stereotyp <<enumeration>>
versehen werden:
class Wochentage <<enumeration>>{
MONTAG
DIENSTAG
MITTWOCH
DONNERSTAG
FREITAG
SAMSTAG
SONNTAG
}

Generics
Mit PlantUML lassen sich auch parametrisierte Klassen darstellen:
class Optional<T> {
}
Die Typvariable wird in einfache spitze Klammern eingetragen (<T>
) und in der Klasse als Datentyp weiterverwendet.

Für den Typparameter können - wie in vielen Programmiersprachen üblich - auch Grenzen angegeben werden:
class Foo<? extends Element> {
int size()
}
Foo *- Element

Notizen anordnen
In PlantUML können Notizen in allen vier Richtungen um eine Klasse herum angeordnet werden:

Wenn mehrere Notizen in die gleiche Richtung gehen, werden sie leider etwas unpassend angeordnet. Der PlantUML-Quelltext sieht folgendermaßen aus:
class Konto{ }
note top of Konto
oberhalb
end note
note bottom of Konto
unterhalb
end note
note right of Konto
nochmal rechts
end note
note right of Konto
rechts
end note
note left of Konto
links
end note
Die Notiz kann auch direkt auf einen Member zeigen.

class Konto{
-iban: String
~Konto(iban:String)
}
note left of Konto::iban
"-": <i><b>private</b></i>
Member nur innerhalb der Instanz zugreifbar
end note
note right of Konto::Konto
"\~": <i><b>package</b></i>
Member nur innerhalb Instanzen der Klassen
dieses Packages zugreifbar
end note
Eine Notiz kann auch direkt auf mehrere Member zeigen, dann werden Notiz und Member wie Assoziationen/Dependencies verknüpft.

note as n_abstr
Für diese Methoden
gilt diese Notiz
end note
note as n_prot
Für diese Attribute gilt
diese Notiz
end note
note top of Konto
Diese Notiz gilt für die ganze Klasse
end note
n_abstr ..left.. Konto::getIban
n_abstr ..left.. Konto::setIban
n_prot ..right.. Konto::iban
n_prot ..right.. Konto::kontostand
Anordnung von Klassen und Umgruppierungen
PlantUML ordnet alle Klassen, Interfaces und Notizen in einem Grid an. Wenn man sich überlegt, an welcher Position eines regelmäßigen Grids eine Klasse stehen soll, so gelingt es gut, auf die Positionierung Einfluss zu nehmen.

Man erkennt hier an dem Diagramm schön die regelmäßige Anordnung in einem 3x3 Raster - in der Mitte ist derzeit kein Element angeordnet.
Man kann die Position über die Richtung der Kante, die diese Klasse mit einer anderen Klasse verbindet, festlegen (
-up-
,-down-
,-left-
,-right-
bzw..up.
,.down.
,.left.
,.right.
).Darüber hinaus kann man auch die Position über die länge der Kanten festlegen (
--
/-up-
/..
/.up.
springt eine Position im Grid,---
springt zwei Positionen----
drei, usw.)
In folgendem Diagramm habe ich an den Kanten notiert, mit welchem Quelltext sie erzeugt wurden

Auch hier lässt sich das Grid deutlich erkennen. Die unterschiedlichen Positionen wurde im Wesentichen über die Pfeillängen bestimmt:
Hund . Katze: " . "
Maus .left. Katze: " .left. "
Wolf <|-down- Hund: " <|-down- "
Maus .right. Hase: " .right. "
Kojote - Fuchs : " - "
Fuchs --Waschbär: " -- "
Fuchs --- Dachs: " --- "
Fuchs ----Hyäne: " ---- "
Kojote ----left---- Hamster: "- -left--"
Kojote --down-- Hase: "- - down --"
Säugetier <|--down-- Wolf: "- - down --"
Tigerkatze --down-|> Katze: " --down-|>"
Anordnung über versteckte Pfeile
PlantUML versucht die einzelnen Klassen möglichst kompakt und ohne überschneidende Kanten darzustellen. Oft will man aber eine bestimmte Gruppierung von Klassen erreichen. Da hierfür PlantUML nicht ausgelegt ist, muss man ein paar schmutzige Tricks anwenden, um das gewünschte Layout zu erreichen:
Ausgeblendete Kanten: Wir können Klassen miteinander in Bezug bringen und diese Bezüge nicht anzeigen, in dem wir die Kanten mit [hidden]
notieren. Diese Klassen werden so in der Nähe dargestellt:
Pferd -right[hidden]-> Hund
Hund -right[hidden]-> Katze
Katze -down[hidden]-> Maus

Beziehungen bei Anordnung ignorieren
Wenn zwei Klassen zwar in einer Beziehung stehen, diese aber keinen Einfluss auf die Positionierung haben soll, so kann dies über [norank]
dargestellt werden:
Pferd .right.> Hund
Hund .right.> Katze
Katze .down.> Maus
Maus .[norank].> Pferd

Ohne [norank]
würde die Maus zum Pferd rücken, so bleibt sie aber am rechten Rand.
Anordnung über ausgeblendete Klassen
Über Ausblendungen lassen sich eine ganze Reihe Positionierungen umsetzen. Im folgenden Beispiel ist die obige Klasse Katze per hide
ausgeblendet worden:
Pferd .right.> Hund
Hund .right.> Katze
Katze .down.> Maus
Maus .[norank].> Pferd
hide Katze

hide
Denselben Effekt kann man erzielen, wenn man per Stereotyp ein transparentes Element erzeugt. Allerdings muss hier darauf geachtet werden, dass per skinparam
-Befehl der Schatten und die Klassen-Kreise ausgeschaltet sind (was im Beispiel einmal bewusst nicht gemacht wurde - das korrekte Ergebnis sieht exakt wie oben aus!).
hide circle
skinparam shadowing false
skinparam class<<invisible>> {
borderColor Transparent
backgroundColor Transparent
fontColor Transparent
stereotypeFontColor Transparent
}
class Katze<<invisible>>{
}
Pferd .right.> Hund
Hund .right[hidden].> Katze
Katze .down[hidden].> Maus
Maus .[norank].> Pferd

hide cicle
und skinparam shadowing false
Gruppierung per together {}
Wenn Klassen zusammen stehen sollen, lässt sich das darüber hinaus über together {...}
realisieren (Freizeichen vor der Klammer beachten!). Wieder am selben Beispiel demonstriert:
together {
class Hund
class Maus
}
together {
class Pferd
class Katze
}
Pferd .right.> Hund
Hund .right[hidden].> Katze
Katze .down[hidden].> Maus
Maus .[norank].> Pferd

Aufhübschen von UML-Klassendiagrammen
Über Skins bietet PlantUML die Möglichkeit, die Diagramme in Schriftart, Farben und Aussehen individuell anzupassen. An drei Beispielen soll hier kurz gezeigt werden, wie die Konfiguration mit skinparam
das Aussehen der Klassendiagramme individualisieren kann.
Darstellung ohne zusätzliche skinparam
-Befehle
Beispielhaft sie ein Klassendiagramm gänzlich ohne Anpassungen des Skins mit PlanUML folgendermaßen aus:

Skin mit angepassten Farben und näher am UML-Standard
Im folgenden Beispiel (das für die Diagramme oben verwendet wurde) wurden Farben und Schriftart anpasst, aber zusätzlich auch die oben genannten Tipps verwendet, um das Diagramm UML-konformer erscheinen zu lassen:
@startuml
hide circle
skinparam classAttributeIconSize 0
skinparam DefaultFontName "Lucida Sans Typewriter"
skinparam Class{
BorderColor DarkSlateBlue
BackgroundColor whitesmoke
}
skinparam Interface{
BorderColor DarkSlateBlue
BackgroundColor whitesmoke
}
skinparam Note{
BorderColor DarkSlateBlue
BackgroundColor LightYellow
}
skinparam ArrowColor DarkSlateBlue
@enduml

Klassendiagramme per Formatierung als Entwurf kennzeichnen
Um den Entwurfscharakter eines Klassendiagramms zu unterstützen, kann ein Handschrift-Layout verwendet werden. Besonders wirkungsvoll ist das, wenn auch die Schrift authentisch handschriftlich aussieht. Dies ist z.B. bei der Schriftart “FG Virgil” der Fall. 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.
skinparam style strictuml
skinparam DefaultFontName "FG Virgil"
skinparam handwritten true
skinparam monochrome true
skinparam packageStyle rect
skinparam shadowing false

Diagramme vereinheitlichen und Formatierung auslagern
Damit nicht alle PlantUML-Diagramme dieselben 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/UMLCLASS_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!
Fazit
Der große Vorteil von PlantUML ist, dass Diagramme direkt in die Dokumentation eingebunden werden können und jederzeit änderbar sind. Diesen Vorteil erkauft man sich mit der etwas unflexibleren Positionierung. Mit etwas Übung und rechtzeitiger Toleranz bei suboptimaler Aufteilung der Klassen lassen sich Projekte so schnell dokumentieren. Bei großen Diagrammen besteht allerdings die Gefahr sich zu verzetteln, wenn man versucht, die Anordnung nach eigenen Vorstellungen zu beeinflussen.
Links
Es finden sich zahlreiche Quellen und Dokumentationen zu PlantUML im Netz, erwähnenswert sind u.a.:
Hinweis zur Nachnutzung als Open Educational Resource (OER)
Dieser Artikel und seine Texte, Bilder, Grafiken, Code und sonstiger Inhalt sind - sofern nicht anders angegeben - lizenziert unter CC BY 4.0. Nennung gemäß TULLU-Regel bitte wie folgt: “UML-Klassendiagramme mit PlantUML zeichnen” von oer-informatik.de (H. Stein), Lizenz: CC BY 4.0. Der Artikel wurde unter https://oer-informatik.de/uml-klassendiagramm-plantuml veröffentlicht, die Quelltexte sind in weiterverarbeitbarer Form verfügbar im Repository unter https://gitlab.com/oer-informatik/uml/umlklasse. Stand: 04.06.2023.
[Kommentare zum Artikel lesen, schreiben] / [Artikel teilen] / [gitlab-Issue zum Artikel schreiben]