Afficher des diagrammes de classes avec PlantUML

Quand nous utilisons de la programmation orientée objet, c'est utile de pouvoir voir le diagramme de classes. Dans cet article, je vais vous montrer comment utiliser PlantUML pour générer des diagrammes de classes et un exemple de script pour trouver la hiérarchie de classes d'un projet et générer son diagramme de classes. Je fais ça sur Linux.

PlantUML

PlantUML est un outil pour générer des diagrammes. Cela utilise un code lisible par des êtres humains pour spécifier les diagrammes ce qui rend cela pratique pour en générer via des scripts.

Par exemple pour afficher le diagramme de classes avec ChildClass (classe fille) héritant de MotherClass (classe mère), nous pouvons utiliser le morceau de code suivant.

@startuml
MotherClass <|-- ChildClass
@enduml

PlantUML génère cette image.

Diagramme de classes du code précédent.
Diagramme de classes du code précédent.

PlantUML peut être utilisé pour générer d'autres types de diagramme comme des diagrammes de séquences, des diagrammes d'activité... Regardez leur site et leur documentation pour plus d'information.

Laissez-moi vous montrer comment intégrer PlantUML dans un script pour facilement découvrir la hiérarchie de classes utilisée dans hxWidgets.

Afficher le diagramme de classes de la bibliothèque hxWidgets

hxWidgets est un port de wxWidgets en Haxe. hxWidgets a été développé par l'équipe derrière HaxeUI.

Si vous n'avez rien compris à ce que je viens de dire: Haxe est un langage de programmation orienté objet, le code source peut être compilé en d'autres langages (JavaScript, C++, PHP...) et donc peut être utilisé pour faire des applications web mais aussi des logiciels natifs. wxWidgets est une bibliothèque C++ pour programmer des interfaces comme le sont GTK et QT. hxWidgets est un port de wxWidgets en Haxe et peut être utilisé à l'intérieur de HaxeUI pour faire des interfaces natives (sinon des moteurs de jeux vidéo sont utilisés) en Haxe.

Récupérer le code source de hxWidgets

Clonez simplement le dépôt officiel.

git clone https://github.com/haxeui/hxWidgets

Trouver les définitions de classe

En Haxe, les classes sont définis de cette manière.

class ChildClass extends MotherClass implements Interface {
    // Contenu de la classe
}

Utilisons la commande grep pour trouver les définitions de classe.

La première chose que l'on veut faire est d'être sûrs que nous recherchons le mot entier class et non la chaîne de caractères class. Pour cela nous utilisons les atomes d'expressions rationnelles (regex atoms) \< qui indique le début d'un mot et \> qui indique la fin d'un mot. Si vous êtes un utilisateur de Vim, quand vous utilisez en normal mode la commande *, Vim utilise ces atomes (regardez en bas de l'écran ou vérifier l'historique de recherche avec la commande :history search). Ici un exemple.

$ cat file.txt
xxxxxxxxxxxxxxxxx
xxxxx class xxxxx
xxxxxxclassxxxxxx
xxxxxxxxxxxxxxxxx
$ grep --extended-regexp "class" file.txt
xxxxx class xxxxx
xxxxxxclassxxxxxx
$ grep --extended-regexp "\<class\>" file.txt
xxxxx class xxxxx

Chercher pour ce pattern dans le dépôt de code source nous renvoie ce résultat. Je vous montre seulement les parties intéressantes.

$ grep --recursive \
    --no-filename \
    --extended-regexp \
    "\<class\>" hxWidgets/src/hx/widgets/

class OwnerDrawnPanel extends Panel {
class ClientDC extends WindowDC {

class ColourData {

class ArrayString { //extends WxArray<String> {

class MouseEvent extends Event implements MouseState {
class BookCtrlBase extends Control implements WithImages {

        return autoConvert(win); // lets auto convert the class so it can be used with casts
            list.push(autoConvert(win)); // lets auto convert the class so it can be used with casts
        return autoConvert(win); // lets auto convert the class so it can be used with casts

Nous avons encore trop d'informations (voir les lignes surlignées). Nous devrions supprimer les sections commentées dans la définition de classe et exclure les lignes où le mot class apparaît seulement en commentaire.

Améliorons notre expression rationnelle en utilisant ^\<class\>[^\{]+\{ qui signifie que nous voulons uniquement les lignes commençant par le mot class et que nous prenons jusqu'au premier { qui apparaît. Nous utilisons l'option --only-matching pour avoir seulement la partie capturée. La commande est maintenant:

grep --recursive \
    --no-filename \
    --only-matching \
    --extended-regexp \
    "^\<class\>[^\{]+\{" hxWidgets/src/hx/widgets/

Convertir le résultat en code PlantUML

Pour convertir le résultat en code PlantUML, nous créons un script Python def2plantuml.py. J'aurais pu utiliser AWK ou faire un script shell utilisant sed mais la version en Python était plus rapide à écrire et plus simple à lire.

def2plantuml.py
import sys
import re
"""
Convertie la liste de définitions de classe en diagrammes de classes PlantUML.
"""
print("@startuml")
print("skinparam groupInheritance 2")
for line in sys.stdin:

    # Supprime les nouvelles lignes
    clean = line.strip()

    # Supprime le { à la fin
    clean = re.sub(r" {.*", "", clean)

    # Supprime le mot "class" au début de la ligne
    clean = re.sub(r"^class ", "", clean)

    # Nous ne sommes intéressés que par la hiérarchie de classes
    # donc nous supprimons la définition d'implémentation d'interface.
    clean = re.sub(r" implements.*", "", clean)

    # Si cela hérite d'une classe, afficher le lien
    split = clean.split(" extends ")
    if len(split) == 1:
        print("class " + split[0])
    else:
        print(split[1] + " <|-- " + split[0])

print("@enduml")

Tout regrouper ensemble

Nous créons ensuite un script shell nommé create-class-diagram et nous utilisons ce que nous avons fait précédemment.

create-class-diagram
#!/bin/sh
# Génère le diagramme de classes du paquet hx.widgets

grep --recursive \
    --no-filename \
    --only-matching \
    --extended-regexp \
    "^\<class\>[^\{]+\{" hxWidgets/src/hx/widgets/ |
    sort |
    python3 def2plantuml.py |
    plantuml -pipe -tsvg > diagram.svg

# Ici utilise Inkscape 0.92.5 où l'option "--pipe" n'est pas présente
inkscape --export-pdf="diagram.pdf" diagram.svg
rm diagram.svg

Dans ce script nous listons les définitions de classe avec notre commande grep et nous mettons tout dans l'ordre alphabétique. Nous envoyons le résultat à notre script de conversion. Nous envoyons ensuite le résultat à plantuml pour qu'il puisse exporter ce résultat dans un fichier SVG nommé diagram.svg.

Nous utilisons Inkscape pour créer un fichier PDF pour qu'il soit plus simple de rechercher une classe. Si vous utilisez une version supérieure à 0.92.5 peut-être que vous avec l'option --pipe et que vous n'avez pas besoin de créer diagram.svg. Nous sauvegardons le tout dans diagram.pdf et voici le résultat.

Le diagramme de classes généré.
Le diagramme de classes généré pour hxWidgets.

Ce n'est peut-être pas le plus beau diagramme mais au moins ça fait le boulot.