In
questa appendice descriviamo un particolare tipo di diagrammi, che si rivela
molto utile per descrivere il dialogo fra utente e sistema. Si tratta degli statechart,
inventati da David
Harel nel 1987,[1] come
strumento di modellazione di sistemi complessi, che abbiamo introdotto nel
Capitolo 9. Gli statechart, con il nome di state machine diagrams, sono
stati in seguito adottati da UML (Unified Modeling Language), il
linguaggio visuale standardizzato usato nell’ingegneria del software,
soprattutto per la progettazione di sistemi orientati agli oggetti.[2]
UML comprende numerose
notazioni differenti, che possono essere selezionate in funzione delle esigenze
e delle preferenze del progettista. Anche per descrivere l’interazione fra
utente e sistema, UML propone diverse alternative. La scelta degli statechart
non è quindi l’unica possibile. La proponiamo perché ci sembra quella che
concilia semplicità e potenza descrittiva. In
questo libro seguiamo la notazione adottata da UML 2.
Gli
statechart sono diagrammi a stati di tipo gerarchico. In altre parole, la loro
caratteristica principale è quella di permettere di modellare un sistema
descrivendone la sua evoluzione da uno stato all’altro, per livelli di astrazione successivi. Questo è molto utile per
descrivere con relativa semplicità situazioni che altrimenti richiederebbero
diagrammi molto complessi dal punto di vista grafico. Essi possono essere
utilizzati per rappresentare sistemi di vario tipo, e non necessariamente per
descrivere i dialoghi fra utente e sistema.
Nel
seguito, ci limiteremo a descrivere i costrutti di uso più frequente in modo
piuttosto informale, rimandando il lettore che desiderasse informazioni più
complete ai testi specialistici.[3]
In un
diagramma per macchine a stati, uno stato del sistema modellato è
rappresentato da un rettangolo con gli angoli arrotondati, con il nome dello
stato al suo interno:
Figura 1. Rappresentazione grafica di uno stato di nome A
Una transizione
è rappresentata da un segmento orientato che connette fra loro due
stati. Essa indica il passaggio da uno stato all’altro.
Una
transizione è contrassegnata da una etichetta composta da tre parti, ciascuna
delle quali è opzionale:
evento [condizione] /attività
evento (detto anche trigger) indica l’evento che innesca la transizione da uno stato all’altro;
pre-condizione (detta anche guard) indica una condizione booleana che deve essere verificata affinché la transizione abbia luogo;
attività indica una azione che deve essere eseguita dal sistema durante la transizione.
Così, nel seguente diagramma,
quando il sistema si trova nello stato A, al verificarsi dell’evento e si passa
allo stato B
effettuando l’azione A,
purchè sia verificata la condizione c.
Figura 2. Una transizione da A a B
Quando
il sistema si trova in un certo stato e accade un determinato evento, si può
verificare una sola transizione uscente da quello stato. Di conseguenza, quando
ci sono più transizioni etichettate con lo stesso evento, le condizioni associate
devono essere mutuamente esclusive. Questa situazione si può rappresentare
anche utilizzando un rombo, che simboleggia una scelta, come in Figura
3.
Figura 3. Il rombo rappresenta un’alternativa
Lo stato
iniziale di un diagramma è quello indicato dalla (unica) transizione
uscente da un pallino nero. Lo stato finale viene rappresentato invece
da un pallino nero cerchiato.[4]
Così, nel diagramma seguente, A è lo stato iniziale.
Quando si verifica l’evento e, il sistema va nello
stato finale.
Figura 4. La notazione per indicare stato iniziale e stato finale
Allo
scopo di semplificare il diagramma, più transizioni possono essere connesse
attraverso giunzioni:
Figura 5. Le giunzioni possono semplificare un diagramma
Il
diagramma di Figura
6 descrive il funzionamento di una macchina erogatrice
di bevande.
Figura 6. Diagramma a stati che modella il funzionamento di una macchina erogatrice di bevande
A volte può essere utile poter
indicare che il sistema modellato effettua delle attività (eventualmente
subordinate al verificarsi di certi eventi e condizioni) senza cambiare stato.
Queste situazioni possono essere rappresentate scrivendo eventi, condizioni e
attività all’interno dello stato, con la stessa notazione vista in precedenza: evento[condizione]/attività. In questo caso, il rettangolo
arrotondato che rappresenta lo stato viene suddiviso in due sezioni, una per il
nome e una per le altre informazioni.
Per esempio, in Figura
7 abbiamo raffinato l’esempio di Figura
6, indicando che l’erogatrice, nello stato Seleziona bevanda, è in
grado di segnalare all’utente quali bevande sono esaurite. In questo esempio
abbiamo anche utilizzato gli eventi speciali entry ed exit. Il primo si verifica automaticamente quando il
sistema entra nello stato, e causa l’esecuzione dell’attività specificata (nel
nostro caso: display “Seleziona bevanda”). Il secondo si verifica automaticamente
immediatamente prima che il sistema esca dallo stato. L’attività specificata
(nel nostro caso: display “Grazie!”) verrà
eseguita dopo ogni altra attività associata allo stato.
Figura 7. Uno stato con transizioni interne
Le
attività interne permettono di modellare situazioni complesse senza che ciò
comporti necessariamente una
eccessiva proliferazione di stati nel diagramma.
Uno stato
composto (o superstato) è uno stato che può essere decomposto in una
o più regioni, ciascuna delle quali può contenere altri stati (detti sottostati).
Consideriamo, per il momento, stati composti da una sola regione.
La Figura
8 mostra uno stato A contenente una sola regione, la quale contiene una macchina
a stati che specifica, ad un livello d’astrazione inferiore, il “comportamento
interno” di A.
Figura 8. A è uno stato composto, B e C sono suoi sottostati.
Quando il sistema entra nello stato composto A, esso entra nel sottostato B (che è lo stato iniziale del diagramma interno),
per poi transitare nel sottostato C, e quindi terminare. Se A e B hanno attività associate agli eventi speciali entry, esse vengono eseguite (prima quella associata ad
A, e subito dopo
quella associata a B). Analogamente per gli eventi speciali exit associati a C e ad A: all’uscita, verrà prima eseguita l’azione
relativa a C,
e quindi quella relativa ad A.
Per non complicare troppo i diagrammi, i
sottostati di uno stato composto possono venire specificati a parte. In questo
caso, per indicare che uno stato è composto, si usa la notazione seguente:
Figura 9. Il simbolo che indica che A è uno stato composto
Vediamo un esempio di stato
composto. Nel diagramma di Figura
6 possiamo dettagliare a parte lo stato Inserisci monete, come inFigura 10. In tale diagramma, abbiamo supposto che tutte le
bevande abbiano lo stesso prezzo e che lo stato Macchina spenta effettui, all’uscita,
l’azzeramento di un contatore importo.[5]
Figura 10. La descrizione dello stato composto Inserisci monete di Figura 108
Come si vede dall’esempio di Figura 11, il diagramma contenuto
in uno stato composto non deve necessariamente possedere un solo punto di
ingresso e un solo punto di uscita. Possiamo, infatti, definire altri pseudo-stati
di ingresso e di uscita, usando la notazione indicata nella stessa figura.
Figura 11. Pseudo-stati di ingresso e di uscita
Più
pseudo-stati di ingresso e di uscita possono essere differenziati assegnando
loro un nome. Il diagramma di Figura
11 può essere anche rappresentato come in Figura
12.
Figura 12. Una rappresentazione alternativa dello stato composto di Figura 11
Può
essere necessario richiamare uno stesso diagramma da varie parti di un
diagramma di più alto livello. In tal caso, si dice che il diagramma richiamato
rappresenta una sottomacchina del chiamante. Per esempio, nella Figura
13 una sottomacchina S è richiamata da due stati diversi. La notazione A:S si può leggere “lo stato A è una istanza della sottomacchina S”.
Figura 13. Una sottomacchina S richiamata da più punti di un diagramma a stati di più alto livello
Per permettere di rappresentare
in modo semplice anche situazioni complesse, senza dover disegnare nel
diagramma troppe transizioni, si possono definire alcune notazioni abbreviate.
Per esempio, possiamo rendere più compatto un diagramma in cui lo stesso evento
e
conduce a uno stesso stato a partire da più stati diversi, come in Figura
14.
È
anche possibile rendere più compatti quei diagrammi in cui da uno stesso stato
si raggiungono più stati diversi, come in Figura
15 (a). In questo caso, si introduce la notazione di Figura
15 (b), che utilizza uno pseudo-stato selettore S. L’evento e è
definito (nella nota accanto al diagramma) come la disgiunzione degli eventi e1 ed e2.
Il selettore attiverà lo stato A
quando e=e1, e lo stato B quando e=e2.
Ovviamente, dovremo indicare con precisione la regola per associare ciascun
evento a ciascuno stato di destinazione, come si è fatto in figura.
Figura 15. Semplificazione di un diagramma attraverso la introduzione di un selettore S
La notazione precedente può essere generalizzata
al caso in cui la selezione di uno stato fra più stati alternativi dipende dal
valore di un parametro p, come in Figura 16. In questo caso,
l’evento e(p) causa la transizione allo stato D(p).
Figura 16. Attivazione di uno stato D(p) in funzione di un evento e(p), dipendente dal parametro p
Queste semplici convenzioni possono semplificare
in modo significativo diagrammi complessi. Per esempio, il diagramma di Figura 17 (a) può essere
rappresentato in forma compatta come in Figura 17 (b).
Finora
abbiamo considerato stati composti costituiti da una sola regione.
Quando le regioni sono più di una, gli stati contenuti in una regione sono concorrenti
a quelli delle altre. Pertanto, lo stato composto si potrà trovare
contemporaneamente in più sottostati, ciascuno appartenente ad una sua diversa
regione.
Le
regioni di uno stato sono separate da linee tratteggiate, orizzontali o
verticali, come nellaFigura 18,
che mostra uno stato composto da due regioni.
Con
riferimento a questa figura, quando si entra nello stato composto A, vengono attivati contemporaneamente gli stati
iniziali di tutte le regioni (nel nostro caso, B e D). I
diagrammi di ogni regione poi evolvono in parallelo.
Quando
tutti i diagrammi raggiungono il loro stato finale, allora termina anche lo
stato composto e, nel caso, viene eseguita l’azione associata al suo evento
speciale exit).
Figura 18. Uno stato composto A costituito da due regioni
La Figura
19 mostra un semplice esempio di diagramma con stati
concorrenti. Esso rappresenta
l’evoluzione della preparazione di un corso universitario. Per superare
l’esame, occorre frequentare un laboratorio, realizzare un progetto di esame e
sostenere con successo un compito scritto.
Figura 19. Macchina a stati che rappresenta la preparazione di un corso universitario
[1] D.Harel, Statecharts:
A visual formalism for complex systems, in Science of Computer
Programming, vol.8, n.10, pagg.231-274, 1987, reperibile anche in rete.
[2] Per una introduzione a UML, si veda, per esempio, M.Fowler,
UML distilled (Terza edizione). Ed. italiana: Pearson
– Addison Wesley, 2004.
[3] Tutti i libri su UML hanno un
capitolo sui diagrammi per macchine a stati. Tuttavia, questi diagrammi vengono
trattati spesso in modo sommario. Il riferimento più completo è quello
ufficiale, costituito dalla specifica dell’OMG: Unified Modeling Language
Superstructure – Version 2.0 (sezione 15), che si può trovare sul
sito www.omg.org.
[4] Il
pallino nero non rappresenta uno stato del sistema. Pertanto, la freccia che da
esso esce non può avere associati eventi, condizioni o azioni. Il pallino nero
cerchiato, invece, è uno stato a tutti gli effetti. Pertanto, la freccia in
esso entrante può avere associati eventi, condizioni o azioni.
[5] Questo può essere specificato associando allo
stato Macchina spenta una transizione interna del tipo: exit
/ azzera importo.