Il linguaggio di modellazione UML
In ingegneria del software, UML (unified modeling language, “linguaggio di modellizzazione unificato”) è un linguaggio di modellazione che deriva dall’unificazione di tre diversi approcci di modellazione che si sono sviluppati con la programmazione orientata agli oggetti (OOP).
Quali sono i principi delle OOP?
- Ereditarietà;
- Incapsulamento.
- Polimorfismo;
Cosa è l’ereditarietà?
L’ereditarietà è una relazione tra classi ma può essere definita anche come una corrispondenza tra due insiemi.
Di fatto, una classe è un tipo di dato astratto che in particolare definisce degli attributi e dei metodi. Parliamo quindi di dati e di funzioni. I dati, ossia le informazioni che vengono gestiti dall’algoritmo, sono caratterizzati da un tipo e possono essere costanti o variabili. Il tipo di dato è un insieme di valori che una variabile può assumere e delle operazioni che possono essere eseguite sulla variabile. Le funzioni sono gli algoritmi, ossia sequenze finite di passi che servono a risolvere un determinato problema.
Cosa è il l’incapsulamento?
Con la programmazione orientata agli oggetti si vuole togliere la separazione tra dati e funzioni per il principio dell’ Information Hiding (Incapsulamento), ossia per far si che si possa gestire meglio l’uso delle variabili limitando l’uso solo ad alcune funzioni che hanno bisogno di quelle variabili.
L’information hiding quindi è un principio insito nella definizione stessa di programmazione orientata agli oggetti. Inoltre, la differenza tra un oggetto e una classe sta nel fatto che l’oggetto è un’istanza della classe.
Cosa è il polimorfismo?
Il polimorfismo è quella proprietà grazie alla quale è possibile che funzioni con stesso nome possono svolgere algoritmi diversi a seconda del numero e del tipo di parametri che gli vengono passati.
Ad esempio immaginiamo di avere la funzione che calcola l’area di un quadrato, di un cerchio, di un triangolo e di un rettangolo. Abbiamo quindi quattro diverse implementazioni di un algoritmo il cui risultato è sempre l’area. In un programma strutturata dovrei usare quattro metodi diversi con il vincolo di usare quattro nomi diversi, con la OOP assolutamente no! Avremo una classe “padre” nominata figura geometrica contenente la funzione che calcola l’area e delle classi “figlie” quali rettangolo, triangolo e quadrato che ereditano attributi e funzioni dalla classe figura geometrica e in ogni classe figlia specificherò come va implementata la funzione del calcolo dell’aerea che però avrà sempre lo stesso nome in tutte le classi.
Qualunque sia il linguaggio utilizzato il compilatore verifica sempre questi tre livelli:
- LESSICO, ossia le regole che definiscono il vocabolario.
- SINTASSI, regole del linguaggio per scomporre le frasi (il compilatore controlla che la frase si attenga alle regole del linguaggio).
- SEMANTICA, controllo di corrispondenza tra ciò che è stato dichiarato ed utilizzato, tipo di dati,ecc ecc, dopodiché genera il linguaggio.
Abbiamo quindi fatto un quadro degli elementi che ci servono per fare questa modellazione in UML.
I casi d’uso nel linguaggio UML
Prendiamo la definizione che risale al 1995 di uno degli autori del linguaggio stesso secondo il quale è “una serie di transizioni di un sistema il cui compito è produrre un risultato di valore misurabile per uno o più attori del sistema”.
Il caso d’uso stabilisce cosa accade nel sistema in seguito ad un evento di attivazione.
Esattamente un caso d’uso è una particolare funzionalità, descritta mediante messaggi scambiati da un’entità e gli attori.
Cosa è l’entità?
E’ un sistema, un sottosistema, una classe ecc. L’attore è una qualunque entità esterna.
Lo scopo della descrizione dei casi d’uso è di rendere comprensibile il funzionamento, o almeno l’individuazione delle funzionalità da realizzare, anche a persone che non sono esperte dell’informatica.
Dove si collocano le entità?
Nella prima fase, quindi nella fase di Analisi che risulta essere la fase più astratta, concettuale, quella in cui si fa lo studio del dominio, poi si procede con l’individuazione dei requisiti, una volta tirati fuori i requisiti per rendere univoca la comprensione usiamo i casi d’uso. L’aspetto fondamentale è quello di descrivere il contorno di tutto il sistema.
Tra attore e caso d’uso c’è una qualche relazione di attivazione, per cui quando un attore richiede una funzionalità si deve attivare il caso d’uso corrispondente. Generalmente i casi d’uso rappresentano solo una porzione dei requisiti ossia quelli funzionali. Tutti i requisiti detti esterni o non-funzionali non vengono rappresentati dai casi d’uso.
Di un caso d’uso dobbiamo quindi definire SINTASSI E SEMANTICA. Non abbiamo un lessico perché come formalismo è elementare.
La sintassi è la seguente:
SEMANTICAMENTE il caso d’uso ci rappresenta una funzionalità del sistema e quindi un requisito funzionale. Una volta che ho identificato tutti i requisiti ho trovato tutti i casi d’uso e questi casi d’uso, gli attori, e le relazioni tra di essi li vado a mettere insieme nel diagramma di casi d’uso dove ho lo schema di tutta la funzionalità del sistema che mi farà capire in linea di massima quali saranno i compiti del mio sistema.
Altri elementi che trovo sono gli attori, da non confondere con gli utenti perché l’attore può anche essere un elemento hardware (come ad esempio stampante, tastiera, e ancora tanto altro).
La sintassi dell’attore è la seguente:
Ogni attore ha un nome identificativo scritto sotto la figura dell’omino. Terzo elemento sintattico sono le frecce di collegamento, che rappresentano le relazioni, ossia le corrispondenze tra attore e caso d’uso, tra caso d’uso e attore, tra attore e attore o infine tra caso d’uso e caso d’uso.
Elementi |
Relazione | Funzione |
Attori | Generalizzazione | un attore, specializzato, eredita la partecipazione a tutti i casi d’uso con i quali l’attore specializzato comunica |
Attore – caso d’uso | Associazione | Esprime la partecipazione di uno o più attori ad un caso d’uso |
Casi d’uso | Estensione | Il comportamento di un caso d’uso base può, opzionalmente essere esteso dal comportamento definito da un altro caso d’uso |
Inclusione | Il comportamento di un caso d’uso di base incorpora, sempre il comportamento dei casi d’uso di inclusione | |
Generalizzazione | Un caso d’uso generale ed uno più specifici casi d’uso che ne ereditano ed aggiungono caratteristiche |
segue dunque la tabella con le rispettive notazioni del linguaggio UML.
Attori: relazione di generalizzazione
Tra attore e attore ho una proprietà di generalizzazione, ossia una relazione il cui significato è analogo a quello dell’ereditarietà. Ho quindi un attore generico e un attore specializzato, quest’ultimo ha tutte le proprietà di quello generico con in più delle proprie.
La notazione per questo caso è la seguente:
Attori: relazione di associazione
Tra attore e caso d’uso l’unica relazione ammessa è lì associazione con la caratteristica che può essere uni o bidirezionale, dall’attore al caso d’uso, dal caso d’uso all’attore o entrambe o non specifico nulla. Ho 4 possibilità diverse rappresentate da 4 frecce di collegamento diverse.
Qual è il significato? Esprime la partecipazione di uno o più attori al caso d’uso. Se per esempio abbiamo il caso in cui bisogna creare una password, il guest richiede l’ inserimento della password e il caso d’uso si attiva. (vedere esempio slide 22 della presentazione “Introduzione agli use case”)
La cardinalità di una relazione è il numero di entità coinvolte da una parte e dall’altra. Se la cardinalità è uno a molti significa che a un’ entità ne corrisponderanno dall’altra parte molte.
Esempio: Una persone può acquistare n libri, uno stesso libro può essere acquistato da una sola persona.
Attore e casi d’uso: relazioni di estensione, inclusione e generalizzazione
Per quanto riguarda le relazioni tra i casi d’uso ne abbiamo di tre tipi diversi: estensione, inclusione e generalizzazione.
Il simbolo della relazione di estensione e di inclusione è la freccia tratteggiata.
Prendiamo un esempio di estensione. Ipotizziamo di avere un cliente che deve effettuare un prelievo da un conto corrente, deve essere fatto quindi un controllo sulla presenza minimo di importo disponibile. Per fare ciò possiamo pensare di notificare che l’importo residuo vada a zero e attivare un caso d’uso di notifica azzeramento credito solo nel caso in cui il credito vada a zero. Per questo si chiama relazione di estensione, poiché è quella parte che viene eseguita solo nel caso in cui si realizzi una condizione bene precisa che viene detta condizione di estensione.
Il punto di estensione è il dato su cui vado a fare il controllo della condizione che mi potrebbe portare nel caso d’uso estendente che in questo caso è l’importo prelevato.
Quando l’importo prelevato fa si che si azzeri il credito passo al caso d’uso estendente.
L’altro tipo di relazione è graficamente simile all’estensione ma concettualmente è la duale. Il caso d’inclusione prevede una parte di codice che viene eseguito sempre, tutte le volte che si esegue il caso d’uso includente. Lo porto fuori quindi per una questione di visibilità e per garantire la modularità.
Graficamente è rappresentato così:
Se devo effettuare un prelievo o se voglio richiedere un mutuo, in entrambi i casi, devo controllare se sul conto c’è un certo importo. E dato che devo controllare la stessa cosa in entrambi i casi, invece di ripetere la procedura due volte, la porto fuori e ho un caso d’uso che è incluso in entrambi. Qui il verso della freccia è verso il caso d’uso incluso.
Casi d’uso: relazione di generalizzazione
L’ultima relazione è quella di generalizzazione, in cui ho un caso d’uso più ampio e un caso d’uso “figlio” che specializza quello più ampio. Ad esempio in termini di metodi è il calcolo dell’area del cerchio o del rettangolo rispetto al calcolo dell’area della figura geometrica. E’ quindi un esempio di polimorfismo, di over-riding tra classi diverse.
Abbiamo quindi ora un quadro completo per realizzare il modello dei casi d’uso. Attraverso tutte queste relazioni possiamo esprimere tutto il funzionamento di un intero sistema software a livello astratto, al termine dell’analisi dei requisiti.
Casi d’uso grafici e testuali
I vari casi d’uso, presi singolarmente, possono essere descritti in maniera più precisa perché bisogna formalizzare per bene tutta la sequenza di passi che vogliamo realizzare per avere quella funzionalità. E quindi oltre la rappresentazione grafica possiamo descrivere testualmente ciascun caso d’uso. Si utilizzano altre informazioni che l’UML consiglia come ad esempio quali sono gli attori, se ci sono delle precondizioni o postcondizioni, e ci permette anche di distinguere lo scenario di base da scenari alternativi che possono essere gli insuccessi dello scenario base.