28.10.2013 / by
UML-Diagramme mit Graphviz
Häufig benötigt man mehr oder weniger einfache UML-Klassendiagramm, um in Vorlesungen, Klausuren oder Aufgabenblättern Sachverhalte übersichtlich darzustellen. Besonders praktisch ist, wenn man die Diagramme generieren kann, anstatt sie zu zeichnen.
In meinen Vorlesungen zur Programmierung benötige ich häufig UML-Diagramme. Den größten Anteil haben hier die Klassendiagramme. Mit gängigen Grafikprogrammen oder auch spezialisierten UML-Editoren lassen sich diese Diagramme schnell zeichnen und in XML oder anderen, proprietären Formaten ablegen. Mein Ziel ist schon seit längerem die vollständige Versionierung meiner Vorlesungsinhalte per GIT und die Erzeugung aller benötigten Artefakte (Folien, Übungsblätter etc.) mit einem einzigen make all
. Für die Übungsblätter ist mit LaTeX eine gute Lösung zur Hand, für die Folien habe ich einen eigenen Workflow auf Basis von Markdown implementiert, die Grafiken sind aber noch immer ein Problem. Eine generelle Lösung für die Grafiken ist zwar noch nicht in Sicht und wahrscheinlich auch nur schwer zu finden, aber zumindest für die UML-Klassendigramme bietet Graphviz einige interessante Möglichkeiten.
Das folgende UML-Diagramm ist mit Graphviz erstellt.
Der Quelltext sieht dann wie folgt aus.
digraph G {
fontname = "Bitstream Vera Sans"
fontsize = 8
rankdir = BT
node [
fontname = "Bitstream Vera Sans"
fontsize = 8
shape = "record"
]
Vogel [ label=<{<b><i>Vogel</i></b><br/>\{abstract\}|<i>+fliegen()</i><br/>}> ]
Ente [ label=<{<b>Ente</b><br/>|+fliegen()<br/>+schwimmen()<br/>+eierlegen()<br/>}> ]
Flugzeug [ label=<{<b>Flugzeug</b><br/>|+fliegen()<br/>}> ]
Wasserflugzeug [ label=<{<b>Wasserflugzeug</b><br/>|+fliegen()<br/>+schwimmen()<br/>}> ]
Flieger [ label=<{«interface»<br/><b><i>Flieger</i></b><br/>|+fliegen()<br/>}> ]
Schwimmer [ label=<{«interface»<br/><b><i>Schwimmer</i></b><br/>|+schwimmen()<br/>}> ]
edge [ arrowhead = "empty"; style = "dotted" ]
Ente -> Flieger
Ente -> Schwimmer
Flugzeug -> Flieger
Wasserflugzeug -> Schwimmer
edge [
fontname = "Bitstream Vera Sans"
fontsize = 8
arrowhead = "empty"
]
Wasserflugzeug -> Flugzeug
Ente -> Vogel
}
Man sieht aber auf den ersten Blick, dass der Quelltext für Graphviz mit Boilerplate-Code überfrachtet ist und ebenfalls wieder schwer zu versionieren und zu editieren ist, da sich alle Methodendeklarationen und Klassenbeschreibungen in einer einzigen Zeile befinden.
Daher habe ich mir eine einfache domänenspezifische Sprache überlegt, aus der ich die Eingabe für Graphviz erzeuge. Diese lässt sich viel besser mit einer Versionsverwaltung Verwaltung und unterstützt das DRY-Prinzip deutlich besser.
Class { Vogel {abstract}
+fliegen() {abstract}
}
Class { Ente
+fliegen()
+schwimmen()
+eierlegen()
}
Class { Flugzeug
+fliegen()
}
Class { Wasserflugzeug
+fliegen()
+schwimmen()
}
Interface { Flieger
+fliegen()
}
Interface { Schwimmer
+schwimmen()
}
Inheritance {
Wasserflugzeug -> Flugzeug
Ente -> Vogel
}
Implementation {
Ente -> Flieger
Ente -> Schwimmer
Flugzeug -> Flieger
Wasserflugzeug -> Schwimmer
}
Mit einem kleinen Makefile kann man nun die UML-Diagramme in Grafiken übersetzen.
OUTPUT_DIR = 00_Output
COMPILE = ./compile.sh
DOT = dot -T pdf
all: $(OUTPUT_DIR) \
$(OUTPUT_DIR)/beispiel.pdf
clean:
rm -rf $(OUTPUT_DIR)
$(OUTPUT_DIR)/beispiel.dot: beispiel.uml
$(COMPILE) $< $@
$(OUTPUT_DIR)/beispiel.pdf: $(OUTPUT_DIR)/beispiel.dot
$(DOT) $< > $@
$(OUTPUT_DIR):
mkdir $(OUTPUT_DIR)
.PHONY: clean all
Die aktuelle Implementierung der DSL ist in Java, schöner wäre eine Skriptsprache wie Ruby oder Perl für die man keine VM benötigt.