Class diagram
Concetto e struttura
Lo scopo del diagramma delle classi è fornire una vista statica del software (una sorta di “fotografia”) tramite la rappresentazione delle sue classi, corredate di metodi, attributi e relazioni.
I componenti identificabili in un diagramma delle classi sono:
- oggetti (Classi e Interfacce), rispettivamente riconoscibili per le lettere “C” e “I” nella parte superiore di ogni blocco.
Esiste anche il marcatore "A", che rappresenta una classe astratta. Inoltre, per i diagrammi UML relativi a Java si può usare la lettera "E" per rappresentare le classi enum;
- metodi: preceduti da un cerchio e dal tipo di valore ritornato;
- attributi: preceduti da un quadrato, corrispondono agli attributi dell’oggetto;
- relazioni: frecce che connettono gli oggetti.
È possibile rappresentare il cerchio dei metodi e il quadrato degli attributi con colori diversi in base alla
visibilità.
In Java, ad esempio, si può usare il verde per la visibilità public
,
l’arancio per protected
e il rosso per private
.
Valgono anche due regole sintattiche generali:
- se una scritta è in corsivo vuol dire che all’elemento corrispondente manca qualche definizione ed è dunque da considerarsi astratto;
- se una scritta è sottolineata vuol dire che l’elemento corrispondente (tipicamente metodo o attributo) è _ statico_, ovvero ha una visibilità a livello di classe e non a livello di istanza (i.e è possibile riferirsi a esso anche senza avere precedentemente istanziato la classe);
- se è incluso tra << >> allora si tratta di uno stereotipo, ovvero un nuovo elemento introdotto, può essere un attributo o un’interfaccia ad esempio, per questo specifico use-case che permette di estendere UML.
Relazioni
Nel diagramma delle classi UML esistono relazioni di diversi tipi. Ogni relazione viene rappresentata tramite una specifica forma di freccia:
-
frecce tratteggiate (dipendenza): sono le più generiche e indicano una relazione “gerarchica” tra classi. Ciò che c’è scritto nella classe da cui parte la freccia dipende dal codice che c’è nella classe a cui arriva la freccia (e.g.
Deck
dipende daCollections
);La dipendenza può essere un qualcosa di negativo che alle volte si vuole evitare, questo perché può portare a diverse complicazioni, perciò vanno gestite correttamente.
Quando si verifica un cambiamento nella classe “destinazione” (nell’immagine, Collections) è necessario cambiare anche il contenuto di Deck, ad esempio se cambia il nome di un metodo in Collections sarà necessario cambiare la chiamata di quel metodo anche in Deck.
-
frecce continue (associazione): Questo tipo di legame indica un certo legame con altre istanze di una classe. Associazione, aggregazione e composizione rappresentano tutte lo stesso concetto ma con gradi differenti, l’associazione rappresenta il legame base tra due classi, ovvero conosce una o più istanze di una classe, ma quest’ultime non vanno a definirne la classe.
Un esempio che rende più comprensibile il concetto è l’associazione che c’è tra professore e studenti, un professore conosce $n$ studenti ma loro non vanno a definire il professore stesso; D’altra parte però il professore può eseguire delle operazioni sugli studenti (ad esempio valutarli).
È importante notare come l’associazione sia bidirezionale quando non è presente la freccia alla fine della linea tratteggiata, mentre è monodirezionale quando è presente (nel caso di studenti e professore possiamo dire sia bidirezionale, perché anche gli alunni possono interagire con il professore, di conseguenza questa associazione è navigabile in entrambi i sensi).
-
frecce con rombo bianco (aggregazione): indica che all’interno della classe (e.g.
Deck
) è presente una collezione (in questo caso una lista) di (n) oggetti (Card
nell’esempio).
Questa relazione non è più tra classi, bensì tra istanze delle classi (e.g. un’istanza diDeck
aggrega da 0 a 52 carte);In questo caso si indica una relazione più forte rispetto ad un’associazione, infatti nell’esempio del professore, sia gli studenti che il professore rimangono tali anche se non sono associati l’uno all’altro, in questo caso invece c’è una relazione più stretta, possiamo dire che un mazzo senza carte non sarebbe utilizzabile (anche se esiste il concetto di mazzo vuoto, ma non è possibile giocare a nessun gioco con un mazzo sempre vuoto).
Notiamo come in questo caso vale lo stesso discorso della monodirezionalità o bidirezionalità, infatti in questo caso abbiamo una freccia perché il mazzo conosce le carte ma una carta non sa in che mazzo si trova.
Un’ultima particolarità è la cardinalità, a destra viene indicato che un mazzo può contenere da 0 a 52 carte, ma a sinistra non vi è alcuna indicazione, questo significa che un mazzo può appartenere idealmente a infiniti mazzi (nella realtà non è ovviamente possibile, ma nel nostro programma non abbiamo bisogno di $n$ istanze identiche di una carte, che tra l’altro è immutabile, ne basta una). La cardinalità è applicabile alle associazioni, alle composizioni e alle aggregazioni.
-
frecce con rombo nero (composizione): è utilizzata quando si hanno degli elementi che sono fisicamente collegati tra loro (non solo virtualmente come nel caso delle carte).
L’oggetto contenuto non può nascere prima che nasca il contenitore e non può morire dopo che muore il contenitore. Possiamo quindi dire che non possono vivere l’uno senza l’altro (a parte rari casi come nell’esempio successivo). Un ultima differenza è che l’oggetto contenuto non può essere acceduto direttamente, ma tramite un interfaccia fornita dal contenitore.
Un esempio può essere la rappresentazione del concetto di aereo: senza il motore l’aereo non può esistere, poiché il primo è un oggetto indispensabile per funzionamento del secondo. Specularmente, non accadrà mai che il motore passi a un altro aereo (a differenza delle carte che possono passare a più mani). Possiamo notare però che se un motore cada dall’aereo, quest’ultimo continuerà a funzionare perché ce ne sono altri, questo però e un caso particolare.
Viene fatta la distinzione tra associazione, aggregazione e composizione, ma quasi nella totalità dei casi a livello di codice si sfrutterà un attributo per rappresentarle tutte. È importante capire la cardinalità per poter implementare al meglio la situazione.