Gratuito · Senza registrazione · Compatibile con file .mmd

Editor di diagrammi delle classi Mermaid

Un diagramma delle classi mostra i tipi e le loro relazioni reciproche: cosa contiene cosa, cosa eredita da cosa, cosa dipende da cosa. Va bene quando il nocciolo è la forma del codice — un modello di dominio, un'interfaccia di estensione, un albero di ereditarietà. Se vuoi mostrare cosa accade a runtime e non come i tipi si incastrano, usa il diagramma di sequenza.

Modello di dominio dei pagamenti

Tre tipi di relazione in un diagramma solo: composizione per le parti che non sopravvivono al tutto, ereditarietà per la gerarchia dei metodi di pagamento e una normale associazione con molteplicità. Il significato lo portano soprattutto le frecce; i rettangoli delle classi sono quasi un contorno.

classDiagram
    class Ordine {
        +String numero
        +StatoOrdine stato
        +Importo totale()
        +void aggiungiRiga(Prodotto p, int quantità)
    }
    class RigaOrdine {
        +Prodotto prodotto
        +int quantità
        +Importo valore()
    }
    class MetodoDiPagamento {
        <<abstract>>
        +autorizza(Importo importo) bool
    }
    class CartaDiCredito {
        +String ultime4
        +autorizza(Importo importo) bool
    }
    class BonificoSepa {
        +String iban
        +autorizza(Importo importo) bool
    }
    class Contrassegno {
        +Importo speseIncasso
        +autorizza(Importo importo) bool
    }

    Ordine "1" *-- "1..*" RigaOrdine : contiene
    Ordine --> MetodoDiPagamento : pagato con
    MetodoDiPagamento <|-- CartaDiCredito
    MetodoDiPagamento <|-- BonificoSepa
    MetodoDiPagamento <|-- Contrassegno
Aprilo nell'editor
Pubblicità

Esempi commentati

1. Una classe sola

`+` è pubblico, `-` privato, `#` protetto. Un membro con le parentesi viene disegnato come metodo; senza, è un campo.

classDiagram
    class Utente {
        +String email
        -String hashPassword
        +bool verifica(String candidato)
    }
Apri nell'editor

2. Ereditarietà e interfacce

`<|--` è l'ereditarietà, si legge «quello a destra estende quello a sinistra». L'annotazione `<<interface>>` è un'etichetta, non un comportamento, ma è ciò che decide la leggibilità del diagramma.

classDiagram
    class Repository {
        <<interface>>
        +trova(String id) Entità
        +salva(Entità e) void
    }
    class RepositoryPostgres {
        -Connessione connessione
        +trova(String id) Entità
        +salva(Entità e) void
    }
    class RepositoryInMemoria {
        -Map archivio
        +trova(String id) Entità
        +salva(Entità e) void
    }
    Repository <|.. RepositoryPostgres
    Repository <|.. RepositoryInMemoria
Apri nell'editor

3. Composizione contro aggregazione

La differenza riguarda il ciclo di vita. Il rombo pieno (`*--`) significa che la parte muore insieme al tutto: cancella la fattura e le sue righe spariscono. Il rombo vuoto (`o--`) significa che la parte esiste in modo indipendente.

classDiagram
    class Fattura {
        +String numero
    }
    class RigaFattura {
        +String descrizione
    }
    class Cliente {
        +String ragioneSociale
    }
    Fattura "1" *-- "1..*" RigaFattura : è composta da
    Cliente "1" o-- "0..*" Fattura : ha emesso
Apri nell'editor

4. Tipi generici

Le tildi danno i parametri di tipo: `Repository~Utente~`. Anche l'annidamento funziona, il che a volte è necessario e raramente è una buona idea. Le lettere accentate funzionano dentro un tipo generico senza ostacoli.

classDiagram
    class Repository~T~ {
        +trova(String id) T
        +tutti() List~T~
    }
    class Cache~K, V~ {
        +prendi(K chiave) V
        +metti(K chiave, V valore) void
    }
    class RepositoryUtenti {
        +trovaPerEmail(String email) Utente
    }
    Repository~Utente~ <|-- RepositoryUtenti
Apri nell'editor

5. Note e direzione

`direction LR` dispone il diagramma da sinistra a destra, cosa che di solito giova a un albero di ereditarietà più della disposizione predefinita. La nota è il posto giusto per il vincolo che non entra nel rettangolo della classe.

classDiagram
    direction LR
    class ArchivioEventi {
        +accoda(Evento e) void
        +riproduci(String flusso) List~Evento~
    }
    class Istantanea {
        +int versione
        +byte[] contenuto
    }
    ArchivioEventi --> Istantanea : scrive ogni 100 eventi
    note for ArchivioEventi "Solo in aggiunta. Gli eventi non vengono mai modificati né cancellati."
Apri nell'editor

Riepilogo della sintassi del diagramma delle classi

Vale la pena memorizzare le frecce di relazione: sono ciò che distingue un diagramma delle classi da un disegno di rettangoli e linee, e si leggono da destra a sinistra, cosa che per un bel po' induce in errore.

SintassiSignificato
classDiagramApre il diagramma. Distingue maiuscole e minuscole.
class Nome { ... }Classe con i suoi membri. La graffa di chiusura su una riga a sé.
+membroPubblico.
-membroPrivato.
#membroProtetto.
+metodo(Tipo arg) TipoRitornoMetodo — a renderlo tale sono proprio le parentesi.
<<interface>> / <<abstract>>Stereotipo, scritto sulla prima riga dentro la classe.
A <|-- BEreditarietà: B estende A.
A <|.. BRealizzazione: B implementa l'interfaccia A.
A *-- BComposizione: B non sopravvive ad A.
A o-- BAggregazione: B può esistere senza A.
A --> BAssociazione con direzione.
A ..> BDipendenza — A usa B ma non lo conserva.
A "1" --> "0..*" B : etichettaMolteplicità su entrambi i lati più l'etichetta della relazione.
class Repo~T~Parametro di tipo generico.
note for A "testo"Nota agganciata a una classe.
direction LRCambia la direzione della disposizione.
Pubblicità

Gli errori che rompono un diagramma delle classi

Riprodotti su Mermaid 11.12.2. Il diagramma delle classi è fra i più indulgenti dei sei tipi, quindi metà di questi errori si disegna tranquillamente e ti restituisce l'immagine sbagliata.

Cosa vedi

Parse error che finisce con: got 'EOF_IN_STRUCT'

Perché

Un corpo di classe aperto con `{` e mai chiuso. Stavolta il nome del token aiuta davvero: significa che il file è finito mentre eravamo ancora dentro una classe.

Soluzione

Chiudi la graffa su una riga a sé.

Sbagliato
classDiagram
    class Ordine {
        +String numero
Corretto
classDiagram
    class Ordine {
        +String numero
    }

Cosa vedi

Parse error che finisce con: got 'ANNOTATION_END'

Perché

Una freccia del diagramma di sequenza usata in un diagramma delle classi. `->>` qui non significa nulla, e il parser ci entra abbastanza a fondo da restituire un nome di token fuorviante.

Soluzione

Usa le relazioni del diagramma delle classi: `-->` per l'associazione, `<|--` per l'ereditarietà, `*--` per la composizione.

Sbagliato
classDiagram
    Ordine ->> Cliente
Corretto
classDiagram
    Ordine --> Cliente : appartiene a

Cosa vedi

No diagram type detected matching given configuration

Perché

Maiuscole e minuscole sbagliate nella parola chiave. `classdiagram` non è `classDiagram`.

Soluzione

D maiuscola.

Sbagliato
classdiagram
    class Ordine
Corretto
classDiagram
    class Ordine

Cosa vedi

La freccia punta dalla parte opposta a quella che volevi

Perché

Le frecce di relazione si leggono dalla punta all'indietro. `A <|-- B` significa che B eredita da A, non il contrario. Scritta al rovescio viene disegnata lo stesso — solo che ora afferma che la classe base estende la propria sottoclasse.

Soluzione

Leggilo come «l'estremità lontana estende quella con la punta». Metti il genitore a sinistra di `<|--`.

Sbagliato
classDiagram
    CartaDiCredito <|-- MetodoDiPagamento
Corretto
classDiagram
    MetodoDiPagamento <|-- CartaDiCredito

Cosa vedi

Compare un campo dove ti aspettavi un metodo

Perché

Le parentesi sono l'unica cosa che distingue un metodo da un campo. `+salva` è un campo chiamato salva; `+salva()` è un metodo. Entrambe le forme sono valide, quindi nulla avvisa.

Soluzione

Aggiungi le parentesi e, se vuoi vederlo, il tipo di ritorno dopo.

Sbagliato
classDiagram
    class Repo {
        +salva
        +trova
    }
Corretto
classDiagram
    class Repo {
        +salva(Entità e) void
        +trova(String id) Entità
    }

Cosa vedi

Composizione e aggregazione sembrano uguali a prima vista e dicono il contrario

Perché

`*--` e `o--` differiscono di un carattere e codificano una differenza di significato reale: se la parte possa sopravvivere al tutto. Usare quella sbagliata produce un diagramma formalmente corretto e falso rispetto al tuo dominio.

Soluzione

Rombo pieno `*--` quando cancellare il genitore cancella il figlio. Vuoto `o--` quando non lo cancella.

Sbagliato
classDiagram
    Ordine o-- RigaOrdine : contiene
Corretto
classDiagram
    Ordine *-- RigaOrdine : contiene

Note sul disegno

Misurato su Mermaid 11.12.2, la versione che usa questo sito.

Cresce verso l'alto più in fretta di qualunque altro tipo qui

Misurato su classi da due membri ciascuna, legate per ereditarietà: tre classi danno un viewBox di circa 176×548, quaranta ne danno 180×7726, cioè all'incirca 194 pixel di altezza per classe — la crescita più ripida fra i sei tipi di questo sito. Un diagramma da quaranta classi supera i settemila pixel di altezza ed è inservibile come immagine unica. `direction LR` aiuta, ma oltre le quindici classi circa la soluzione onesta è dividere il diagramma secondo i confini di dominio.

Il numero di membri non influisce quasi sulla larghezza

La larghezza la detta la firma più lunga di un singolo membro, non quanti ce ne sono. Una classe con venti campi corti non è più larga di una con tre. In altre parole: puoi essere generoso con i membri e avaro con le classi, che è esattamente l'opposto dell'istinto. In italiano le firme escono più lunghe che in inglese, quindi la larghezza la decide di solito un unico metodo dal nome descrittivo.

Accenti e apostrofo funzionano ovunque, anche nei generici

Verificato: `Città`, `Attività` e `Autenticità` funzionano come nomi di classe e di membro, l'apostrofo passa senza problemi in un identificatore, e anche `Repository~Utente~` si compone senza errori. Non serve spogliare il modello degli accenti per farlo disegnare. L'unico vincolo reale lo introduce la tilde, che è sintassi.

I generici usano le tildi e questo ha una conseguenza

`Repository~T~` si scrive così perché le parentesi angolari andrebbero in conflitto con l'HTML nelle etichette. Ne deriva anche che una tilde letterale nel nome di una classe o di un membro viene letta come l'inizio di un parametro di tipo. È raro, ma quando capita disorienta parecchio.

Le etichette sono HTML, quindi l'esportazione PNG ridisegna

Le etichette delle classi sono disegnate dentro un `<foreignObject>` nell'SVG, e i browser si rifiutano di rasterizzarlo su un canvas. L'esportazione PNG di questo sito prima falliva in silenzio restituendo un file SVG; ora ridisegna il diagramma con etichette di testo SVG semplice. Il PNG esce corretto e a piena dimensione, con una composizione appena diversa da quella a schermo.

Quando conviene un altro diagramma

Se stai documentando una base dati e non un sistema di tipi, usa il diagramma ER. La distinzione conta: i diagrammi delle classi modellano comportamento ed ereditarietà, che le tabelle non hanno, e i diagrammi ER modellano per bene chiavi e molteplicità, che i diagrammi delle classi liquidano in fretta.

Se il diagramma è fatto per lo più di rettangoli collegati da `-->` e senza membri, stai disegnando un'architettura, non un diagramma delle classi. Un diagramma di flusso con dei sottografi verrà meglio e affermerà di meno.

E se l'elenco delle classi nasce dal codice, chiediti se non debba nascere da lì anche il diagramma. Un diagramma delle classi mantenuto a mano per una base di codice che cambia ogni settimana diventa falso in un mese, e un diagramma sbagliato costa più di nessun diagramma.

Altri tipi di diagramma

Scritto da Dominik Malsch · Ultimo aggiornamento:

Apri l'editor →