Beispiel der Plantuml Diagrammerstellung

Plantuml Diagramme sind rein textbasierte Diagramme welche über das Framework plantuml in Grafikformate gewandelt werden. Die textuale Beschreibung der Diagramme soll in diesem Beispiel nicht manuell sondern durch einen Builder erfolgen. Die Definition eines Plantumldiagram lässt sich in diverse Bereiche wie Header, Footer, Objektdefinitionen, Verbindungen zwischen Objekten etc. unterteilen.

Da die Bereiche in einer festgelegten Reihenfolge in der Diagrambeschreibung aufgeführt werden müssen, sollten sich Stage basierte Builder besonders gut eignen. Die zu bildende Instanz ist im Prinzip ein Text, also eine Instanz von String. Da bei der Programmierung der Konkatinierung von Strings lieber ein Stringbuilder vorgezogen werden sollte, werden wir auch in diesem Beispiel statt eines Strings einen StringBuilder als zu buildende Instanz verwenden.

Die verwendbaren fachlichen Methoden wie addHeader etc. werden wir in den Stages definieren und immer auf die append Methode des Stringbuilders abbilden. Es folgt eine Veranschaulichung eines Builders zum Erstellen eines minimalen Plantuml Diagrammes.

Als Ergebnis gibt der Builder im Test folgende Diagramm Definition zurück:

Generierte Plantuml Sourcen für ein Entity Diagram
@startuml
entity Mitarbeiter{
* id  varchar2(2000) <<PK>>
}
@enduml

Dieses kann online über folgenden URL ausprobiert und in eine Grafik umgewandelt werden: https://www.plantuml.com/plantuml

Die zugehörigen Quellen sehen wie folgt aus:

PlantumlEntityDiagramBuilder.java
public abstract class PlantumlEntityDiagramBuilder implements BuilderStates.NewState {

    private final StringBuilder builder;

    private PlantumlEntityDiagramBuilder() {
        this.builder = new StringBuilder();
    }

    public static NewState builder() {
        final PlantumlEntityDiagramBuilder builder = new PlantumlEntityDiagramBuilder() {
            // will be never called, because is internal overridden by lambda in return of builder() method
            public StringBuilder build() {
                throw new UnsupportedOperationException("Call of method forbidden");
            }
        };
        // create new interface instance
        return () -> builder.builder;
    }
}
BuilderStage.java
import org.apache.commons.lang3.StringUtils;

public interface BuilderStates {
    StringBuilder build();

    interface NewState extends BuilderStates {

        default UmlStartState createUmlHeader() {
            final StringBuilder builder = build();
            builder.append("@startuml");
            return this::build;
        }
    }

    interface UmlStartState extends BuilderStates {
        default EntityState createEntity(final String name) {
            final StringBuilder builder = build();
            builder.append(String.format("\nentity %s{", name));
            return this::build;
        }

        default BuildState createUmlFooter() {
            final StringBuilder builder = build();
            builder.append("\n@enduml");
            return this::build;
        }
    }

    interface EntityState extends BuilderStates {
        default ColumnTypeState createColumnMandatory(final String columnName) {
            final StringBuilder builder = build();
            builder.append(String.format("\n* %s ", columnName));
            return this::build;
        }

        default ColumnTypeState createColumnNullable(final String columnName) {
            final StringBuilder builder = build();
            builder.append(String.format("%n  %s ", columnName));
            return this::build;
        }

        default UmlStartState next() {
            final StringBuilder builder = build();
            builder.append("\n}");
            return this::build;
        }
    }

    interface ColumnTypeState extends BuilderStates {
        default ColumnNotesState columnType(final String columnTypeSpec) {
            final StringBuilder builder = build();
            builder.append(String.format(" %s", columnTypeSpec));
            return this::build;
        }
    }

    interface ColumnNotesState extends BuilderStates {
        default EntityState columnNotes(final String columnNotes) {
            if (!StringUtils.isEmpty(columnNotes)) {
                final StringBuilder builder = build();
                builder.append(String.format(" %s", columnNotes));
            }
            return this::build;
        }
    }


    interface BuildState extends BuilderStates {
        // build() already defined
    }

}
PlantumlEntityDiagramBuilderTest.java
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;

class PlantumlEntityDiagramBuilderTest {

    @Test
    @DisplayName("ddl model with single entity with one primary key column")
    void createSimpleModelWithRelation() {
        final StringBuilder plantumlEntityDiagram = PlantumlEntityDiagramBuilder
                .builder()
                .createUmlHeader()
                .createEntity("Mitarbeiter")
                .createColumnMandatory("id")
                .columnType("varchar2(2000)")
                .columnNotes("<<PK>>")
                .next()
                .createUmlFooter()
                .build();
        assertEquals("""
                @startuml
                entity Mitarbeiter{
                * id  varchar2(2000) <<PK>>
                }
                @enduml""", plantumlEntityDiagram.toString());
    }

}

Weiter zum Beispiel RegEx Statechart oder zurück zum Beispiel mit Zuständen.