Skip to content
Snippets Groups Projects
Verified Commit a6ff33c8 authored by Marco Aceti's avatar Marco Aceti
Browse files

Add 'Lezione 09'

parent 13dd3a9c
No related branches found
No related tags found
No related merge requests found
---
layout: post
title: "[11] UML"
date: 2022-11-14 14:30:00 +0200
toc: true
---
<style>
p.plantuml-parent {
margin-bottom: 0;
}
p.tab {
margin-left: 30px;
}
</style>
# UML
## Introduzione
__UML__ (_Unified Modeling Language_) è un linguaggio di _modeling_ il cui scopo è determinare uno standard comune nella rappresentazione visuale del software.
UML rappresenta in realtà una famiglia di formalismi, che si concretizzano nei concetti di __diagrammi__.
## 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.
<!-- Hardcoded diagram because the PlantUML jekyll plugin produces a malformed version -->
<p class="plantuml-parent">
<object class="plantuml" style="width: 85%" data="{{ site.baseurl }}/assets/11_UML-base.svg"></object>
</p>
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.
{% plantuml style="width: 40%" %}
hide fields
hide methods
class Card
interface Comparable
{% endplantuml %}
<p class="tab"> 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;</p>
{% plantuml style="width: 36%" %}
hide fields
hide methods
abstract CardStack
enum Suit
{% endplantuml %}
- __metodi__: preceduti da un cerchio e dal tipo di valore ritornato;
{% plantuml style="width: 25%" %}
class Deck {
+ void shuffle()
+ Card draw()
+ void sort()
}
{% endplantuml %}
- __attributi__: preceduti da un quadrato, corrispondono agli attributi dell'oggetto;
{% plantuml style="width: 23%" %}
class Card {
- Rank rank
- Suit suit
}
{% endplantuml %}
- __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 <span style="color:green">verde</span> per la visibilità `public`, l'<span style="color:orange">arancio</span> per `protected` e il <span style="color:red">rosso</span> 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 è <u>sottolineata</u> 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 riferircisi ad esso anche senza avere precedentemente istanziato la classe).
### Relazioni
Nel diagramma delle classi UML esistono relazioni di diversi tipi.
Ogni relazione viene rappresentata tramite una specifica forma di freccia:
- __frecce tratteggiate__ (___associazione___): 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 da `Collections`);
{% plantuml style="width: 40%" %}
hide fields
hide methods
class Deck
class Collections
Deck .> Collections
{% endplantuml %}
- __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 di `Deck` aggrega da 0 a 52 carte);
{% plantuml style="width: 33%" %}
hide fields
hide methods
class Deck
class Card
Deck .o Card
{% endplantuml %}
- __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). \\
Senza l'uno l'altro non può vivere e viceversa. \\
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 ad un altro aereo (a differenza delle carte che possono passare a più mani).
{% plantuml style="width: 36%" %}
hide fields
hide methods
class Aereo
class Motore
Aereo .* Motore
{% endplantuml %}
- __frecce con la punta a triangolo__ (___implementazione___): una classe può _implementare_ una classe astratta o un'interfaccia.
{% plantuml style="width: 40%" %}
hide fields
hide methods
class Card
interface Comparable
Card .|> Comparable
{% endplantuml %}
La direzione delle frecce è importante perché indica il _senso_ della relazione. \\
Per esempio, il mazzo _conosce_ le carte che contiene, ma le carte _non conoscono_ i mazzi di cui fanno parte &#8211; è per questo che la direzione della freccia va da `Deck` a `Card` e non viceversa. <br>
## Sequence diagram
### Concetto e struttura
Lo scopo del __diagramma di sequenza__ è rappresentare il flusso di interazione tra attori all'interno di un software nel tempo.
Di seguito ne è indicato un esempio.
{% responsive_image path: 'assets/11_sequence-diagram-example.png' %}
Si compone di alcuni elementi chiave:
- __attori__: rappresentano le entità coinvolte nel processo; spesso sono _oggetti_.
- __invocazioni__: identificano chiamate di metodo su un attore da parte di un altro e sono rappresentate con una freccia che va da _sinistra verso destra_. <br>
La parte a sinistra è il _chiamante_ e la parte a destra è il _chiamato_.
- __valori di ritorno__: visualizzati tramite una freccia tratteggiata che va da _destra verso sinistra_.
* __cicli__: aree rettangolari etichettate con il termine `loop` che specificano la presenza di un ciclo in una certa zona del diagramma.
* __condizioni__: aree rettangolari etichettate con il termine `opt` che specificano la necessità di verificare alcune condizioni prima di entrare nella zona corrispondente.
## State diagram
### Concetto e struttura
L'obiettivo del **diagramma di stato** è fornire un'astrazione di comportamento significativa che sia comune all'intera classe.
La sua struttura deriva dai classici *State Charts*, dei quali costituisce un'ulteriore astrazione.
Al fine di comprendere meglio i diagrammi di stato, può essere utile ricordare che:
> Negli _State Charts_, un automa è una sestupla $$\langle S, \, I, \, U; \; \delta, \, t, \, s_0 \rangle$$.
> - $$S$$: insieme finito e non vuoto degli stati;
> - $$I$$: insieme finito dei possibili ingressi;
> - $$U$$: insieme finito delle possibili uscite;
> - $$\delta$$: funzione di transizione;
> - $$t$$: funzione di uscita;
> - $$s_0$$: stato iniziale.
Negli _State Diagram_, ogni _stato_ è rappresentato da un rettangolo e lo _stato iniziale_ è indicato da un pallino nero.
{% responsive_image path: 'assets/11_state-diagram-example.png' %}
Nel diagramma le frecce possono avere diversi significati:
- **evento/azione**: semplice e immediata transizione da uno stato ad un altro;
- **guardie**: disambiguano le transazioni in uscita da uno stato che sono legate ad uno stesso evento;
- **_time event_**: rappresentano eventi temporizzati.
* *After(duration)*: indicano un tempo massimo di permanenza nello stato destinazione. \\
Allo scadere del timer, lo stato cambia.
* **_change event_**: rappresentano eventi che si innescano al verificarsi di un cambiamento.
* *When(condition)*: indicano eventi espressi in termini di valori degli attributi.
Il verificarsi di eventi non esplicitamente marcati da un arco deve portare alla terminazione dell'esecuzione e al sollevamento di un errore.
## Superstate
Ulteriore evoluzione dello State Diagram, il **Superstate** consente di rappresentare più facilmente una "gerarchia" di stati.
La transizione in uno stato può quindi condurre ad un'altra FSM concettualmente "innestata".
{% responsive_image path: 'assets/11_superstate-example.png' %}
Nel caso d'esempio, lo stato `acceso` possiede al suo interno un ulteriore diagramma di stato.
### Ulteriori aggiunte
* è possibile associare al diagramma uno stato **_history_**, il cui scopo è memorizzare lo stato storico prima dell'interruzione dell'FSM;
* è possibile rendere il diagramma capace di rappresentare il concetto di **concorrenza** tramite la divisione in **regioni** (ognuna regolata da una propria FSM).
Le regioni possono essere attive contemporaneamente. I confini tra regioni, come mostrato nell'esempio, sono identificati da linee tratteggiate.
{% responsive_image path: 'assets/11_concurrency-example.png' %}
## Use cases diagram
### Concetto e struttura
I **diagrammi dei casi d'uso** rappresentano l'astrazione di un insieme di scenari tra loro correlati. \\
Essi adottano un linguaggio che verte alla risoluzione di esigenze comunicative tramite un lessico potenzialmente meno tecnico.
Tale natura "informale" li rende ottimi mezzi di comunicazione col *cliente*.
Possono essere utilizzati, ad esempio, per:
* eplicitare differenti modalità di fare un compito;
* stabilire quale dovrebbe essere la normale interazione nello scenario e le eccezioni che possono verificarsi.
Infatti, ogni _scenario_ è corredato di:
* __pre e post condizioni__ da rispettare;
* __flusso di esecuzione__ da percorrere in condizioni normali;
* eventuali __eccezioni__ e loro possibili trattamenti.
Infine, parte della versatilità degli *Use Case diagrams* risiede nella loro capacità di collegarsi, eventualmente, ad altri tipi di diagrammi (*Sequence, Activity, etc*) che possono essere impiegati per descriverne in modo più approfondito il flusso.
### Scenari
I componenti di ogni scenario si dividono in **Attori** e **Casi d'Uso**.
{% plantuml style="width: 50%" %}
:Actor: - (Use)
"Attore" as Actor
"Caso d'uso" as (Use)
{% endplantuml %}
In generale il *collegamento* tra un attore e un caso d'uso rappresenta una __relazione di partecipazione__, *i.e* "Questo attore partecipa a questo caso d'uso".
L'interazione può comunque essere denominata.
Sono contemplati anche collegamenti fra un caso d'uso e un altro (vedi [paragrafo dedicato](#assoc-ucuc)).
### Identificazione degli attori
Gli _attori_ non sono necessariamente persone fisiche.
Possono corrispondere anche a dei **ruoli** o addirittura ad un **sistema esterno**.
Ogni attore è un'entità esterna al sistema ed interagisce direttamente con esso, fungendo allo stesso tempo da *fonte* e *destinatario* di informazione.
Ci sono due attori particolari:
* **attore beneficiario**: colui che trae beneficio dall'interazione con lo use case, *i.e.* chi è **interessato** a quella funzionalità. \\
Gli altri attori possono cambiare, ma il beneficiario rimmarrà probabilmente lo stesso;
* **attore primario**: colui che avvia l'interazione con lo use case.
### Identificazione use case
Il miglior modo di identificare i casi d'uso è interrogarsi su due fronti:
* **sistema**: _"quali funzionalità si desidera che il sistema possieda?"_;
* **attori beneficiari**: _"cosa vogliono?"_, _"come agiscono?"_, _"perchè si interfacciano col sistema?"_ e _"cosa si aspettano?"_.
### Associazioni
Ogni diagramma dei casi d'uso deve seguire due convenzioni per quanto riguarda le associazioni.
* > Ogni attore deve avere almeno un'interazione con un caso d'uso.
Un attore che non dovesse possedere alcuna associazione con un caso d'uso sarebbe impossibilitato a interagire col sistema e rappresentarlo nel diagramma non avrebbe alcun senso.
* > Ogni caso d'uso deve essere associato ad almeno un attore.
Un caso d'uso che non coinvolge alcun attore è un caso d'uso che, per definizione, non ha senso di esistere, poichè nessuno è in grado di interagirvi.
#### <a name="assoc-ucuc"></a>Relazioni _use case - use case_
Esistono due tipologie di relazioni tra use case:
* **inclusione (*include*)**: relazione che esprime il predicato *"far parte di"*. \\
Chi include conosce sempre gli inclusi, ma non viceversa.
La parte inclusa *deve* essere eseguita;
* **estensione (*extend*)**: relazione che viene utilizzata per rappresentare casi eccezionali che specificano comportamenti particolari in alcuni use case.
#### Generalizzazione
L'associazione di **generalizzazione** rappresenta un particolare tipo di relazione, applicabile sia ad una coppia *attore - attore* che ad una coppia *use case - use case*.
La sua semantica dipende dal contesto a cui viene applicata:
* **tra attori**: permette di esplicitare eventuali relazioni tra ruoli. \\
Ad esempio un ruolo potrebbe includerne un altro.
* **tra use case**: la semantica è simile all'*extend*, ma senza punti d'estensione. Infatti, alcune parti della descrizione vengono *ereditate* e altre vengono *sostituite*. Non si applica il secondo principio della Liskov.
### Esempi di utilizzo
Nel seguente diagramma,
{% plantuml style="width: 60%" %}
"Book Borrower" as BB
"Extend Loan" as (ext)
"Borrow copy of a book" as (bor)
"Check for reservation" as (chk)
BB -- ext
BB -- bor
ext ..> chk : << include >>
bor ..> chk : << include >>
{% endplantuml %}
l'attore _Book Borrower_ è associato alle seguenti operazioni:
- prendere in prestito un libro;
- chiedere l'estensione del prestito di un libro.
In entrambi i casi, il bibliotecario deve controllare l'esistenza di una richiesta di prenotazione per il libro.
Il prossimo diagramma è differente:
{% plantuml style="width: 100%" %}
"Book Borrower" as BB
usecase bor as "Borrow copy of book
--
**extension points**
status validation:
after confirming identity"
"Refuse loan" as (ref)
BB - bor
bor <. ref : << extend >>
{% endplantuml %}
_rifiuta il prestito_ può essere l'__estensione__ di un comportamento normale come _prendi in prestito il libro_.
In quest'ultimo ci sono dei punti di estensione in cui vengono fatti dei controlli, come la verifica dello stato di prestito del libro o dell'identità del richiedente.
## Activity diagram
### Concetto e struttura
Gli _activity diagram_ presentano una conformazione simile agli _state diagram_, ma con svariate differenze:
* al posto degli stati __vi sono le *attività*__;
* non si usano più le __transizioni__ etichettate tramite eventi &#8211; queste sono quasi tutte __implicitamente temporizzate__;
* possono esserci ___azioni_ dentro le attività__;
* le *attività* possono rappresentare __elementi esterni__ al sistema.
La peculiare capacità di collegarsi con attività esterne fa sì che sia possibile utilizzare gli activity diagram come __collante__ con e tra diversi casi d'uso.
Inoltre la __visuale parzialmente informale__, eppure leggermente più tecnica e profonda rispetto ai diagrammi dei casi d'uso, rende gli activity diagram un __ottimo mezzo di comunicazione interna__ (*e.g.* con un manager).
### Livelli di astrazione
Si possono utilizzare i diagrami delle attività per:
- descrivere la logica interna di un __business process__ (caso più comune);
- descrivere il __flusso interno di un metodo__, con eventuali indicazioni di (pseudo)concorrenza;
- dettagliare il __flusso di un caso d'uso__, ovvero chiarire meglio il suo flusso di esecuzione rispetto ad altri diagrammi (*e.g.* Sequence Diagram).
Questa rappresentazione è assai utile nei casi in cui, ad esempio, la concorrenza è un fattore rilevante.
### Sincronizzazione
{% responsive_image path: 'assets/11_activity-example.png' %}
Attraverso l'uso di barre si possono stabilire dei punti di sincronizzazione (JOIN). \\
I JOIN, se non diversamente specificato, vengono considerati in ___AND___.
È però possibile porre dei vincoli diversi per stabilire i criteri di soddisficamento della barra di sincronizzazione (come una __*OR*__).
### Decisioni
{% responsive_image path: 'assets/11_activity-decision-example.png' %}
È possibile specificare nel flusso di esecuzione dei momenti di __decisione__.
I corsi d'azione intraprendibili in questi frangenti sono rappresentati tramite degli archi.
Le decisioni devono rispettare due proprietà:
* gli archi collegati alla decisione devono essere __mutualmente esclusivi__;
* l'__unione__ delle condizioni di decisione deve essere sempre vera.
È bene puntualizzare che i punti di decisione sono _veri_ momenti di decisione umana, non banali valutazioni di una condizione logica.
### Swim lane
{% responsive_image path: 'assets/11_activity-swim-lane-example.png' %}
Si può partizionare il diagramma al fine di rappresentare, sulle singole _activity_, delle particolari responsibilità.
Queste vengono visualizzate tramite delle _"corsie"_ verticali che identificano _chi_ svolge una determinata attività.
## Component diagram
### Concetto e struttura
Lo scopo del __diagramma dei componenti__ è rappresentare e raggruppare i componenti del sistema. \\
_"Componente"_ è un termine trasversale che include file, librerie, documenti _etc._ (ma che è diverso dal concetto di _classe_!).
Il diagramma include:
* __componenti__: rettangoli che rappresentano una funzione di sistema ben determinata. \\
Possono essere _annidati_;
* __interfacce__: cerchi che indicano le interfacce implementate o utilizzate dai componenti. \\
I collegamenti con i componenti indicano la presenza di una dipendenza;
* __stereotipi__: racchiusi tra i caratteri `<<>>`, etichettano e identificano una serie di funzionalità appartenenti ad uno stesso "gruppo".
{% responsive_image path: 'assets/11_component-diagram-example.png' %}
Si noti che un componente può usarne un altro conoscendone solo l'interfaccia.
### Identificare i componenti
Alcune linee guida per identificare e rappresentare correttamente i componenti sono:
- capire quali parti del sistema sono rimpiazzabili facilmente e/o sono versionate separatamente;
- identificare quali parti del sistema svolgono una funzione ben determinata;
- pensare in termini di "gerarchia" dei componenti;
- chiarire l'esistenza di dipendenze con altri componenti e di dipendenze con le interfacce.
## Deployment diagram
Il _Deployment diagram_ permette di rappresentare la __dislocazione fisica__ delle risorse. \\
Più precisamente, specifica la dislocazione fisica delle _istanze dei componenti_.
La conformazione del diagramma è quindi molto simile a quella del diagramma dei componenti, ma con qualche differenza:
* i __nodi__ del sistema indicano macchine fisiche;
* i __collegamenti__ tra nodi eplicitano le modalità di comunicazione tra gli stessi (_e.g._ RMI, HTTP).
{% responsive_image path: 'assets/11_deployment-diagram-example.png' %}
Il Deployment diagram risulta di particolare utilità per il _deployer_, _i.e._ la figura che si occupa dell'installazione fisica del sistema.
# UML
__UML__ (_Unified Modeling Language_) è un linguaggio di _modeling_ il cui scopo è determinare uno standard comune nella rappresentazione visuale del software.
UML rappresenta in realtà una famiglia di formalismi, che si concretizzano nei concetti di __diagrammi__.
- [**Class diagram**](./01_class-diagram.md)
- [**Sequence diagram**](./02_sequence-diagram.md)
- [**State diagram**](./03_state-diagram.md)
- [**Superstate**](./04_superstate.md)
- [**Use cases diagram**](./05_use-cases.md)
- [**Activity diagram**](./06_activity-diagram.md)
- [**Component diagram**](./07_component-diagram.md)
- [**Deployment diagram**](./08_deployment-diagram.md)
# 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.
<!-- Hardcoded diagram because the PlantUML jekyll plugin produces a malformed version -->
<p class="plantuml-parent" align="center">
<object class="plantuml" style="width: 70%" data="/assets/11_UML-base.svg"></object>
</p>
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.
```plantuml
@startuml
hide fields
hide methods
class Card
interface Comparable
@enduml
```
<p class="tab"> 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;</p>
```plantuml
@startuml
hide fields
hide methods
abstract CardStack
enum Suit
@enduml
```
- __metodi__: preceduti da un cerchio e dal tipo di valore ritornato;
```plantuml
@startuml
class Deck {
+ void shuffle()
+ Card draw()
+ void sort()
}
@enduml
```
- __attributi__: preceduti da un quadrato, corrispondono agli attributi dell'oggetto;
```plantuml
@startuml
class Card {
- Rank rank
- Suit suit
}
@enduml
```
- __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 <span style="color:green">verde</span> per la visibilità `public`, l'<span style="color:orange">arancio</span> per `protected` e il <span style="color:red">rosso</span> 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 è <u>sottolineata</u> 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 riferircisi ad esso anche senza avere precedentemente istanziato la classe).
### Relazioni
Nel diagramma delle classi UML esistono relazioni di diversi tipi.
Ogni relazione viene rappresentata tramite una specifica forma di freccia:
- __frecce tratteggiate__ (___associazione___): 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 da `Collections`);
```plantuml
@startuml
hide fields
hide methods
class Deck
class Collections
Deck .> Collections
@enduml
```
- __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 di `Deck` aggrega da 0 a 52 carte);
```plantuml
@startuml
hide fields
hide methods
class Deck
class Card
Deck .o Card
@enduml
```
- __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). \
Senza l'uno l'altro non può vivere e viceversa. \
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 ad un altro aereo (a differenza delle carte che possono passare a più mani).
```plantuml
@startuml
hide fields
hide methods
class Aereo
class Motore
Aereo .* Motore
@enduml
```
- __frecce con la punta a triangolo__ (___implementazione___): una classe può _implementare_ una classe astratta o un'interfaccia.
```plantuml
@startuml
hide fields
hide methods
class Card
interface Comparable
Card .|> Comparable
@enduml
```
La direzione delle frecce è importante perché indica il _senso_ della relazione. \
Per esempio, il mazzo _conosce_ le carte che contiene, ma le carte _non conoscono_ i mazzi di cui fanno parte &#8211; è per questo che la direzione della freccia va da `Deck` a `Card` e non viceversa.
# Sequence diagram
## Concetto e struttura
Lo scopo del __diagramma di sequenza__ è rappresentare il flusso di interazione tra attori all'interno di un software nel tempo.
Di seguito ne è indicato un esempio.
![Sequence diagram](/assets/11_sequence-diagram-example.png)
Si compone di alcuni elementi chiave:
- __attori__: rappresentano le entità coinvolte nel processo; spesso sono _oggetti_;
- __invocazioni__: identificano chiamate di metodo su un attore da parte di un altro e sono rappresentate con una freccia che va da _sinistra verso destra_. \
La parte a sinistra è il _chiamante_ e la parte a destra è il _chiamato_;
- __valori di ritorno__: visualizzati tramite una freccia tratteggiata che va da _destra verso sinistra_;
- __cicli__: aree rettangolari etichettate con il termine `loop` che specificano la presenza di un ciclo in una certa zona del diagramma;
- __condizioni__: aree rettangolari etichettate con il termine `opt` che specificano la necessità di verificare alcune condizioni prima di entrare nella zona corrispondente.
# State diagram
## Concetto e struttura
L'obiettivo del **diagramma di stato** è fornire un'astrazione di comportamento significativa che sia comune all'intera classe.
La sua struttura deriva dai classici *State Charts*, dei quali costituisce un'ulteriore astrazione.
Al fine di comprendere meglio i diagrammi di stato, può essere utile ricordare che:
> Negli _State Charts_, un automa è una sestupla \\(\langle S, \\, I, \\, U; \\; \delta, \\, t, \\, s_0 \rangle\\).
> - \\(S\\): insieme finito e non vuoto degli stati;
> - \\(I\\): insieme finito dei possibili ingressi;
> - \\(U\\): insieme finito delle possibili uscite;
> - \\(\delta\\): funzione di transizione;
> - \\(t\\): funzione di uscita;
> - \\(s_0\\): stato iniziale.
Negli _State Diagram_, ogni _stato_ è rappresentato da un rettangolo e lo _stato iniziale_ è indicato da un pallino nero.
![](/assets/11_state-diagram-example.png)
Nel diagramma le frecce possono avere diversi significati:
- **evento/azione**: semplice e immediata transizione da uno stato ad un altro;
- **guardie**: disambiguano le transazioni in uscita da uno stato che sono legate ad uno stesso evento;
- **_time event_**: rappresentano eventi temporizzati.
* *After(duration)*: indicano un tempo massimo di permanenza nello stato destinazione. \
Allo scadere del timer, lo stato cambia.
* **_change event_**: rappresentano eventi che si innescano al verificarsi di un cambiamento.
* *When(condition)*: indicano eventi espressi in termini di valori degli attributi.
Il verificarsi di eventi non esplicitamente marcati da un arco deve portare alla terminazione dell'esecuzione e al sollevamento di un errore.
# Superstate
Ulteriore evoluzione dello State Diagram, il **Superstate** consente di rappresentare più facilmente una "gerarchia" di stati.
La transizione in uno stato può quindi condurre ad un'altra FSM concettualmente "innestata".
![Esempio superstate](/assets/11_superstate-example.png)
Nel caso d'esempio, lo stato `acceso` possiede al suo interno un ulteriore diagramma di stato.
## Ulteriori aggiunte
* è possibile associare al diagramma uno stato **_history_**, il cui scopo è memorizzare lo stato storico prima dell'interruzione dell'FSM;
* è possibile rendere il diagramma capace di rappresentare il concetto di **concorrenza** tramite la divisione in **regioni** (ognuna regolata da una propria FSM).
Le regioni possono essere attive contemporaneamente. I confini tra regioni, come mostrato nell'esempio, sono identificati da linee tratteggiate.
![Esempio concorrenza](/assets/11_concurrency-example.png)
# Use cases diagram
## Concetto e struttura
I **diagrammi dei casi d'uso** rappresentano l'astrazione di un insieme di scenari tra loro correlati. \
Essi adottano un linguaggio che verte alla risoluzione di esigenze comunicative tramite un lessico potenzialmente meno tecnico.
Tale natura "informale" li rende ottimi mezzi di comunicazione col *cliente*.
Possono essere utilizzati, ad esempio, per:
* eplicitare differenti modalità di fare un compito;
* stabilire quale dovrebbe essere la normale interazione nello scenario e le eccezioni che possono verificarsi.
Infatti, ogni _scenario_ è corredato di:
* __pre e post condizioni__ da rispettare;
* __flusso di esecuzione__ da percorrere in condizioni normali;
* eventuali __eccezioni__ e loro possibili trattamenti.
Infine, parte della versatilità degli *Use Case diagrams* risiede nella loro capacità di collegarsi, eventualmente, ad altri tipi di diagrammi (*Sequence, Activity, etc*) che possono essere impiegati per descriverne in modo più approfondito il flusso.
## Scenari
I componenti di ogni scenario si dividono in **Attori** e **Casi d'Uso**.
```plantuml
@startuml
scale 500 width
:Actor: - (Use)
"Attore" as Actor
"Caso d'uso" as (Use)
@enduml
```
In generale il *collegamento* tra un attore e un caso d'uso rappresenta una __relazione di partecipazione__, *i.e* "Questo attore partecipa a questo caso d'uso".
L'interazione può comunque essere denominata.
Sono contemplati anche collegamenti fra un caso d'uso e un altro (vedi [paragrafo dedicato](#assoc-ucuc)).
## Identificazione degli attori
Gli _attori_ non sono necessariamente persone fisiche.
Possono corrispondere anche a dei **ruoli** o addirittura ad un **sistema esterno**.
Ogni attore è un'entità esterna al sistema ed interagisce direttamente con esso, fungendo allo stesso tempo da *fonte* e *destinatario* di informazione.
Ci sono due attori particolari:
* **attore beneficiario**: colui che trae beneficio dall'interazione con lo use case, *i.e.* chi è **interessato** a quella funzionalità. \
Gli altri attori possono cambiare, ma il beneficiario rimmarrà probabilmente lo stesso;
* **attore primario**: colui che avvia l'interazione con lo use case.
## Identificazione use case
Il miglior modo di identificare i casi d'uso è interrogarsi su due fronti:
* **sistema**: _"quali funzionalità si desidera che il sistema possieda?"_;
* **attori beneficiari**: _"cosa vogliono?"_, _"come agiscono?"_, _"perchè si interfacciano col sistema?"_ e _"cosa si aspettano?"_.
## Associazioni
Ogni diagramma dei casi d'uso deve seguire due convenzioni per quanto riguarda le associazioni.
* > Ogni attore deve avere almeno un'interazione con un caso d'uso.
Un attore che non dovesse possedere alcuna associazione con un caso d'uso sarebbe impossibilitato a interagire col sistema e rappresentarlo nel diagramma non avrebbe alcun senso.
* > Ogni caso d'uso deve essere associato ad almeno un attore.
Un caso d'uso che non coinvolge alcun attore è un caso d'uso che, per definizione, non ha senso di esistere, poichè nessuno è in grado di interagirvi.
### <a name="assoc-ucuc"></a>Relazioni _use case - use case_
Esistono due tipologie di relazioni tra use case:
* **inclusione (*include*)**: relazione che esprime il predicato *"far parte di"*. \
Chi include conosce sempre gli inclusi, ma non viceversa.
La parte inclusa *deve* essere eseguita;
* **estensione (*extend*)**: relazione che viene utilizzata per rappresentare casi eccezionali che specificano comportamenti particolari in alcuni use case.
### Generalizzazione
L'associazione di **generalizzazione** rappresenta un particolare tipo di relazione, applicabile sia ad una coppia *attore - attore* che ad una coppia *use case - use case*.
La sua semantica dipende dal contesto a cui viene applicata:
* **tra attori**: permette di esplicitare eventuali relazioni tra ruoli. \
Ad esempio un ruolo potrebbe includerne un altro.
* **tra use case**: la semantica è simile all'*extend*, ma senza punti d'estensione. Infatti, alcune parti della descrizione vengono *ereditate* e altre vengono *sostituite*. Non si applica il secondo principio della Liskov.
## Esempi di utilizzo
Nel seguente diagramma,
```plantuml
@startuml
scale 800 width
"Book Borrower" as BB
"Extend Loan" as (ext)
"Borrow copy of a book" as (bor)
"Check for reservation" as (chk)
BB -- ext
BB -- bor
ext ..> chk : << include >>
bor ..> chk : << include >>
@enduml
```
l'attore _Book Borrower_ è associato alle seguenti operazioni:
- prendere in prestito un libro;
- chiedere l'estensione del prestito di un libro.
In entrambi i casi, il bibliotecario deve controllare l'esistenza di una richiesta di prenotazione per il libro.
Il prossimo diagramma è differente:
```plantuml
@startuml
scale 1024 width
"Book Borrower" as BB
usecase bor as "Borrow copy of book
--
**extension points**
status validation:
after confirming identity"
"Refuse loan" as (ref)
BB - bor
bor <. ref : << extend >>
@enduml
```
_rifiuta il prestito_ può essere l'__estensione__ di un comportamento normale come _prendi in prestito il libro_.
In quest'ultimo ci sono dei punti di estensione in cui vengono fatti dei controlli, come la verifica dello stato di prestito del libro o dell'identità del richiedente.
# Activity diagram
## Concetto e struttura
Gli _activity diagram_ presentano una conformazione simile agli _state diagram_, ma con svariate differenze:
* al posto degli stati __vi sono le *attività*__;
* non si usano più le __transizioni__ etichettate tramite eventi &#8211; queste sono quasi tutte __implicitamente temporizzate__;
* possono esserci ___azioni_ dentro le attività__;
* le *attività* possono rappresentare __elementi esterni__ al sistema.
La peculiare capacità di collegarsi con attività esterne fa sì che sia possibile utilizzare gli activity diagram come __collante__ con e tra diversi casi d'uso.
Inoltre la __visuale parzialmente informale__, eppure leggermente più tecnica e profonda rispetto ai diagrammi dei casi d'uso, rende gli activity diagram un __ottimo mezzo di comunicazione interna__ (*e.g.* con un manager).
## Livelli di astrazione
Si possono utilizzare i diagrami delle attività per:
- descrivere la logica interna di un __business process__ (caso più comune);
- descrivere il __flusso interno di un metodo__, con eventuali indicazioni di (pseudo)concorrenza;
- dettagliare il __flusso di un caso d'uso__, ovvero chiarire meglio il suo flusso di esecuzione rispetto ad altri diagrammi (*e.g.* Sequence Diagram).
Questa rappresentazione è assai utile nei casi in cui, ad esempio, la concorrenza è un fattore rilevante.
## Sincronizzazione
![Esempio activity diagram](/assets/11_activity-example.png)
Attraverso l'uso di barre si possono stabilire dei punti di sincronizzazione (JOIN). \
I JOIN, se non diversamente specificato, vengono considerati in ___AND___.
È però possibile porre dei vincoli diversi per stabilire i criteri di soddisficamento della barra di sincronizzazione (come una __*OR*__).
## Decisioni
![Esempio activity decision diagram](/assets/11_activity-decision-example.png)
È possibile specificare nel flusso di esecuzione dei momenti di __decisione__.
I corsi d'azione intraprendibili in questi frangenti sono rappresentati tramite degli archi.
Le decisioni devono rispettare due proprietà:
* gli archi collegati alla decisione devono essere __mutualmente esclusivi__;
* l'__unione__ delle condizioni di decisione deve essere sempre vera.
È bene puntualizzare che i punti di decisione sono _veri_ momenti di decisione umana, non banali valutazioni di una condizione logica.
## Swim lane
![Swim lane esempio](/assets/11_activity-swim-lane-example.png)
Si può partizionare il diagramma al fine di rappresentare, sulle singole _activity_, delle particolari responsibilità.
Queste vengono visualizzate tramite delle _"corsie"_ verticali che identificano _chi_ svolge una determinata attività.
# Component diagram
## Concetto e struttura
Lo scopo del __diagramma dei componenti__ è rappresentare e raggruppare i componenti del sistema. \
_"Componente"_ è un termine trasversale che include file, librerie, documenti _etc._ (ma che è diverso dal concetto di _classe_!).
Il diagramma include:
* __componenti__: rettangoli che rappresentano una funzione di sistema ben determinata. \
Possono essere _annidati_;
* __interfacce__: cerchi che indicano le interfacce implementate o utilizzate dai componenti. \
I collegamenti con i componenti indicano la presenza di una dipendenza;
* __stereotipi__: racchiusi tra i caratteri `<<>>`, etichettano e identificano una serie di funzionalità appartenenti ad uno stesso "gruppo".
![Esempio component diagram](/assets/11_component-diagram-example.png)
Si noti che un componente può usarne un altro conoscendone solo l'interfaccia.
## Identificare i componenti
Alcune linee guida per identificare e rappresentare correttamente i componenti sono:
- capire quali parti del sistema sono rimpiazzabili facilmente e/o sono versionate separatamente;
- identificare quali parti del sistema svolgono una funzione ben determinata;
- pensare in termini di "gerarchia" dei componenti;
- chiarire l'esistenza di dipendenze con altri componenti e di dipendenze con le interfacce.
# Deployment diagram
Il _Deployment diagram_ permette di rappresentare la __dislocazione fisica__ delle risorse. \
Più precisamente, specifica la dislocazione fisica delle _istanze dei componenti_.
La conformazione del diagramma è quindi molto simile a quella del diagramma dei componenti, ma con qualche differenza:
* i __nodi__ del sistema indicano macchine fisiche;
* i __collegamenti__ tra nodi eplicitano le modalità di comunicazione tra gli stessi (_e.g._ RMI, HTTP).
![Esempio deployment diagram](/assets/11_deployment-diagram-example.png)
Il Deployment diagram risulta di particolare utilità per il _deployer_, _i.e._ la figura che si occupa dell'installazione fisica del sistema.
......@@ -93,6 +93,16 @@
- [Model view presenter](./08_patterns/17_mvp.md)
- [Builder](./08_patterns/18_builder.md)
- [UML](./09_uml/00_index.md)
- [Class diagram](./09_uml/01_class-diagram.md)
- [Sequence diagram](./09_uml/02_sequence-diagram.md)
- [State diagram](./09_uml/03_state-diagram.md)
- [Superstate](./09_uml/04_superstate.md)
- [Use cases diagram](./09_uml/05_use-cases.md)
- [Activity diagram](./09_uml/06_activity-diagram.md)
- [Component diagram](./09_uml/07_component-diagram.md)
- [Deployment diagram](./09_uml/08_deployment-diagram.md)
# Verifica e convalida
# Reti di Petri
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment