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 <|-- ContrassegnoEsempi 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)
}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 <|.. RepositoryInMemoria3. 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 emesso4. 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~ <|-- RepositoryUtenti5. 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."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.
| Sintassi | Significato |
|---|---|
| classDiagram | Apre il diagramma. Distingue maiuscole e minuscole. |
| class Nome { ... } | Classe con i suoi membri. La graffa di chiusura su una riga a sé. |
| +membro | Pubblico. |
| -membro | Privato. |
| #membro | Protetto. |
| +metodo(Tipo arg) TipoRitorno | Metodo — a renderlo tale sono proprio le parentesi. |
| <<interface>> / <<abstract>> | Stereotipo, scritto sulla prima riga dentro la classe. |
| A <|-- B | Ereditarietà: B estende A. |
| A <|.. B | Realizzazione: B implementa l'interfaccia A. |
| A *-- B | Composizione: B non sopravvive ad A. |
| A o-- B | Aggregazione: B può esistere senza A. |
| A --> B | Associazione con direzione. |
| A ..> B | Dipendenza — A usa B ma non lo conserva. |
| A "1" --> "0..*" B : etichetta | Molteplicità 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 LR | Cambia la direzione della disposizione. |
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é.
classDiagram
class Ordine {
+String numeroclassDiagram
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.
classDiagram
Ordine ->> ClienteclassDiagram
Ordine --> Cliente : appartiene aCosa vedi
No diagram type detected matching given configuration
Perché
Maiuscole e minuscole sbagliate nella parola chiave. `classdiagram` non è `classDiagram`.
Soluzione
D maiuscola.
classdiagram
class OrdineclassDiagram
class OrdineCosa 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 `<|--`.
classDiagram
CartaDiCredito <|-- MetodoDiPagamentoclassDiagram
MetodoDiPagamento <|-- CartaDiCreditoCosa 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.
classDiagram
class Repo {
+salva
+trova
}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.
classDiagram
Ordine o-- RigaOrdine : contieneclassDiagram
Ordine *-- RigaOrdine : contieneNote 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: