Le macchine a stati vengono utilizzate per modellare il comportamento dinamico di un elemento del modello e, più
specificamente, aspetti del comportamento del sistema guidati dall'evento (consultare Concetto: Eventi e segnali). Le macchine a stati vengono espressamente utilizzate per
definire il comportamento dipendente dallo stato o il comportamento che varia in rapporto allo stato in cui si trova
l'elemento del modello. Gli elementi del modello il cui comportamento non varia con lo stato dell'elemento, non
richiede macchine a stati per descriverne il comportamento (questi elementi sono classi tipicamente passive la cui
principale responsabilità è quella di gestire dati). In particolare, le macchine a stati devono essere usate per
modellare il comportamento delle classi attive che utilizzano gli eventi di chiamata per implementare le loro
operazioni (come transizioni nelle macchine a stati della classe).
Una macchina a stati è costituita da stati, collegati a transizioni. Uno stato è una condizione di un oggetto in cui
vengono eseguiti alcuni compiti o si attende un evento. Una transizione è una relazione tra due stati attivati da
alcuni eventi che esegue certe azioni o valutazioni e che termina in uno specifico stato-di-fine. Gli elementi di una
macchina a stati sono descritti nella Figura 1.
Figura 1. Notazione della macchina a stati.
Un semplice editor può essere visualizzato come macchina a stati finita, con gli stati di Vuota, In attesa di
un comando e In attesa di testo. Gli eventi Carica file, Inserisci testo, Inserisci
carattere e Salva ed esci causano delle transizioni nella macchina a stati. La macchina a stati per l'editor
viene descritta nella Figura 1 di seguito riportata.
Figura 2. La macchina a stati per un editor semplice.
Uno stato è una condizione di un oggetto in cui vengono eseguiti alcuni compiti o si attende un evento. Un oggetto può
rimanere in uno stato per un quantitativo di tempo finito. Uno stato ha diverse proprietà:
Nome
|
Una stringa di testo che distingue lo stato dagli altri stati; uno stato può anche essere anonimo, cioè
senza nome.
|
Azioni di entrata/uscita
|
Azioni eseguite entrando e uscendo dallo stato.
|
Transizioni interne
|
Transizioni gestite senza causare un cambio di stato.
|
Stati secondari
|
La struttura nidificata di uno stato, che coinvolge gli stati secondari disgiunti (attivi
sequenzialmente) o simultanei (attivi simultaneamente).
|
Eventi posticipati
|
Un elenco di eventi non gestiti in quello stato ma posticipati e messi in coda perché vengano gestiti
dall'oggetto in un altro stato.
|
Come descritto nella Figura 1, sono presenti due stati speciali che possono essere definiti per una macchina a stati
dell'oggetto. Lo stato iniziale indica il luogo predefinito di partenza per la macchina a stati o per lo stato
secondario. Uno stato iniziale viene rappresentato con un cerchio in nero. Lo stato finale indica il completamento
dell'esecuzione della macchina a stati o che lo stato di inclusione è stato completato. Uno stato finale viene
rappresentato da un cerchio nero circondato da un cerchio vuoto. Gli stati di inizio e di fine sono proprio
pseudostati. Nessuno dei due può avere le parti usuali di uno stato normale, tranne che per il nome. Una transizione
dallo stato iniziale a quello finale può avere il completamento totale delle funzioni, inclusa una condizione di
guardia ed una azione, ma può non avere un evento trigger.
Una transizione è una relazione tra due stati ed indica che un oggetto nel primo stato eseguirà talune azioni ed
entrerà in un secondo stato quando si verificheranno eventi specificati e quando verranno soddisfatte delle condizioni
specifiche. In un tale cambio di stato, la transizione viene definita come 'generata'. Finché la transizione non
genera, si dice che l'oggetto si trova nello stato di 'origine'; dopo la generazione, si definisce stato di
'destinazione'. Una transizione ha diverse proprietà:
Condizione di origine
|
Lo stato influenzato dalla transizione; se un oggetto si trova nello stato di origine, si può generare
una transizione in partenza quando l'oggetto riceve l'evento trigger della transizione e se la
condizione di guardia, se c'è, viene soddisfatta.
|
Evento trigger
|
L'evento che rende la transizione eleggibile alla generazione (ammesso che la sua condizione di guardia
venga soddisfatta) quando viene ricevuto dall'oggetto nello stato di origine.
|
Condizione di guardia
|
Un'espressione booleana che viene valutata quando la transizione viene attivata dalla ricezione
dell'evento trigger; se l'espressione dà una valutazione di tipo "True", la transizione ha i requisiti
per essere generata. Se invece la valutazione è "False", la transizione non viene generata. Se non
esistono transizioni che potrebbero essere attivate dallo stesso evento, quest'ultimo viene perso.
|
Azione
|
Un calcolo atomico eseguibile, che può agire direttamente sull'oggetto che possiede la macchina a
stati, e indirettamente su altri oggetti visibili all'oggetto.
|
Condizione di destinazione
|
Lo stato che è attivo dopo il completamento della transizione.
|
Una transizione può avere origini multiple, nel qual caso rappresenta una unione di molteplici stati concorrenti nonché
obiettivi multipli, per cui rappresenta un bivio verso molteplici stati concorrenti.
Nel contesto della macchina a stati, un evento è un avvenimento di emissione che può attivare una transizione di stato.
Gli eventi possono includere eventi di segnale, eventi di chiamata, il passaggio del tempo o un cambio di stato. Un
segnale o chiamata possono avere parametri i cui valori sono disponibili alla transizione, incluso le espressioni per
le condizioni di guardia e l'azione. E' inoltre possibile avere una transizione senza trigger, rappresentata da una
transizione senza evento di attivazione. Queste transizioni, denominate anche transizioni di completamento, sono
implicitamente attivate quando il loro stato di origine ha completato il suo compito.
Una condizione di guardia viene valutata dopo che si è verificato l'evento di attivazione per la transizione. E'
possibile avere transizioni multiple provenienti dallo stesso stato di origine e con lo stesso evento trigger, finché
le condizioni di guardia non si sovrappongono. Una condizione di guardia viene valutata solo una volta per transizione,
al momento in cui si verifica l'evento. L'espressione booleana può far riferimento allo stato dell'oggetto.
Un'azione è un calcolo atomico eseguibile, non può essere cioè interrotto da un evento e pertanto viene eseguito fino
al suo completamento. Questo è in contrasto con un compito, che può essere interrotto da altri eventi. Le azioni
possono comprendere chiamate di operazioni (il proprietario di una macchina a stati nonché di altri oggetti visibili)
la creazione o la distruzione di un altro oggetto, o l'invio di un segnale ad un altro oggetto. Nel caso di invio di un
segnale, il nome del segnale ha come prefisso la parola chiave 'send'.
Le azioni di entrata e di uscita consentono alla stessa azione di essere inviate ogni volta che si entra o si esce da
uno stato. Le azioni di entrata e di uscita consentono di effettuare ciò in modo pulito, senza dover esplicitamente
mettere le azioni su ogni transizione in entrata ed in uscita. Le azioni di entrata e di uscita non dispongono di
argomenti e di condizioni di guardia. Le azioni di entrata a livello superiore di una macchina a stati per un elemento
del modello possono avere parametri che rappresentano gli argomenti che la macchina riceve quando viene creato
l'elemento.
Le transizioni interne consentono la gestione degli eventi all'interno dello stato, senza lasciare lo stato, evitando
perciò le entrate di attivazione o le azioni d'uscita. Le transizioni interne possono presentare eventi con parametri e
condizioni di guardia, e rappresentare essenzialmente dei gestori di interruzione.
Gli eventi posticipati sono quelli la cui gestione viene ritardata finché non diventa attivo uno stato in cui l'evento
non è ritardato. Quando questo stato diventa attivo, l'evento viene attivato e può comportare transizioni come se si
fosse appena verificato. L'implementazione degli eventi posticipati richiede la presenza di una coda interna di eventi.
Se si verifica un evento ma viene elencato come posticipato, viene messo in coda. Gli eventi vengono tolti da questa
coda appena l'oggetto entra in uno stato che non rimanda questi eventi.
Uno stato semplice è uno che non ha strutture secondarie. Uno stato con stati secondari (stati nidificati) viene
definito stato composito. Gli stati secondari possono essere nidificati a qualunque livello. Una macchina a stati
nidificata può avere al massimo uno stato iniziale ed uno finale. Gli stati secondari vengono utilizzati per
semplificare macchine a stati complesse e flat, mostrando che alcuni stati sono possibili solo all'interno di un
particolare contesto (lo stato di chiusura).
Figure 3. Stati secondari
Da una origine al di fuori di uno stato composito di chiusura, una transizione può indirizzarsi allo stato composito
oppure allo stato secondario. Se il suo obiettivo è lo stato composito, la macchina a stati nidificata deve comprendere
uno stato di inizio a cui il controllo passa dopo aver immesso lo stato composito e dopo aver distribuito la sua
eventuale azione d'entrata. Se il suo obiettivo è lo stato nidificato, il controllo passa allo stato nidificato dopo
aver distribuito l'azione di immissione dello stato composito (se presente) e poi l'azione di immissione dell'eventuale
stato nidificato.
Una transizione che conduce fuori da uno stato composito può avere come sua origine lo stato composito o uno stato
secondario. In entrambi i casi, il controllo prima lascia lo stato nidificato (e la sua azione di uscita, se c'è, viene
distribuita), poi lascia lo stato composito (e la sua azione di uscita, se c'è, viene distribuita). Una transizione la
cui origine è lo stato composito essenzialmente interrompe il compito della macchina a stati nidificata.
Se non diversamente specificato, quando una transizione entra in uno stato composito, l'azione della macchina a stati
nidificata parte di nuovo dallo stato iniziale (a meno che la transizione non si indirizzi direttamente ad uno stato
secondario). Gli stati cronologici consentono alla macchina a stati di reimmettere l'ultimo stato secondario che era
attivo prima di lasciare lo stato composito. Un esempio di utilizzo di stato cronologico viene presentato nella Figura
3.
Figure 4. Stato cronologico.
Le macchine a stati sono usate più comunemente per modellare il comportamento di un oggetto durante il suo periodo di
vita. Sono particolarmente necessarie quando gli oggetti hanno un comportamento dipendente dallo stato. Gli oggetti che
possono avere le macchine a stati contengono classi, sistemi secondari, casi d'uso e interfacce (per sostenere stati
che devono essere soddisfatti da un oggetto che realizza l'interfaccia). Nel caso di sistemi in tempo reale, le
macchine a stati vengono utilizzate anche per capsule e protocolli (per sostenere stati che devono essere soddisfatti
da un oggetto che realizza il protocollo).
Non tutti gli oggetti richiedono macchine a stati. Se il comportamento di un oggetto è semplice, come quello che
semplicemente memorizza o che richiama i dati, il comportamento dell'oggetto non varia con lo stato e la sua macchina
di stato è di poco interesse.
La modellazione della durata di un oggetto coinvolge tre aspetti: la specifica degli eventi a cui l'oggetto può
rispondere, la risposta a quegli eventi e l'impatto del passato sul comportamento attuale. La modellazione della durata
di un oggetto include anche l'ordine con il quale l'oggetto può rispondere in modo significativo agli eventi, iniziando
dal momento della creazione dell'oggetto e continuando fino alla sua fine.
Per modellare la durata di un oggetto:
-
Impostare il contesto per una macchina a stati, se è una classe, un caso d'uso o un sistema nel suo intero.
-
Se il contesto è una classe o un caso d'uso, raccogliere le classi limitrofe, incluso le classi principali
o quelle raggiungibili da associazioni o dipendenze. Queste classi limitrofe sono obiettivi candidati per
le azioni e per le inclusioni nelle condizioni di guardia.
-
Se il contesto è il sistema nella sua interezza, restringere l'attenzione su un comportamento del sistema e
poi considerare le durate degli oggetti coinvolti in quell'aspetto. La durata dell'intero sistema è
semplicemente troppo grande per poter essere un punto significativo su cui concentrarsi.
-
Stabilire gli stati iniziali e finali per l'oggetto. Se esistono condizioni antecedenti o successive di stati
iniziali e finali, definire anche quelli.
-
Determinare gli eventi a cui rispondono gli oggetti. Questi si possono trovare nelle interfacce dell'oggetto. In
caso di sistemi in tempo reale, questi si possono trovare anche nei protocolli dell'oggetto.
-
Partendo dallo stato iniziale in direzione di quello finale, la struttura del livello superiore indica che
l'oggetto potrebbe essere presente. Collegare questi stati con le transizione attivare dagli eventi appropriati.
Continuare aggiungendo queste transizioni.
-
Identificare eventuali azioni di entrata o di uscita.
-
Espandere o semplificare la macchina a stati usando gli stati secondari.
-
Controllare che tutte le transizioni di attivazione degli eventi nella macchina a stati coincidano con gli eventi
previsti dalle interfacce realizzate dall'oggetto. Analogamente, controllare che tutti gli eventi previsti dalle
interfacce dell'oggetto vengano gestiti dalla macchina a stati. In caso di sistemi in tempo reale, effettuare
controlli equivalenti per i protocolli di una capsula. Alla fine, stabilire dove si vogliono ignorare
esplicitamente gli eventi (ad es., eventi posticipati).
-
Controllare che tutte le azioni nella macchina a stati siano supportate da relazioni, metodi e operazioni di
oggetti di inclusione.
-
Eseguire una traccia lungo tutta la macchina a stati, confrontandola con le sequenze previste di eventi e con le
loro risposte. Cercare stati non raggiungibili e stati in cui la macchina si blocca.
-
Se si sta riorganizzando o ristrutturando la macchina a stati, assicurarsi che le semantica non sia cambiata.
-
Quando viene data una scelta, usare la semantica visiva della macchina a stati piuttosto che scrivere codice di
transizione dettagli. Ad esempio, non attivare una transizione su svariati segnali, usare il codice del dettaglio
per gestire il flusso di controllo che dipende in modo diverso dal segnale. Usare transizioni separate, attivate da
segnali separati. Evitare logiche condizionali nel codice di transizione che nasconde il comportamento aggiuntivo.
-
I nomi sono fissati secondo quello che ci si aspetta o quello che sta accadendo durante lo stato. Ricordare che uno
stato non è un 'punto in un periodo'; è un periodo durante il quale la macchina a stati attende che accade
qualcosa. Ad esempio, 'inattesaDellaFine' è un nome migliore di 'fine'; 'tempiPerAlcuniCompiti' è migliore di
'timeout' (supero tempo). Non assegnare nomi agli stati come se fossero azioni.
-
Assegnare un nome a tutti gli stati e alle transizioni unicamente all'interno di una macchina a stati: ciò renderà
più semplice il debug del livello di origine.
-
Usare le variabili di stato (attributi utilizzati per controllare il comportamento) cautamente; non usarle per
creare nuovi stati. Quando gli stati sono pochi, con pochi o nessun comportamento dipendente dallo stato, e dove
c'è un piccolo o nessun comportamento che potrebbe essere concorrente con o indipendente dall'oggetto che contiene
la macchina a stati, possono essere usate le variabili di stato. In presenza di un comportamento complesso,
dipendente dallo stato, potenzialmente simultaneo, o se gli eventi da gestire possono originare al di fuori
dell'oggetto che contiene la macchina a stati, prendere in considerazione la possibilità di utilizzare la
collaborazione di due o più oggetti attivi (possibilmente definiti come composizione). Nei sistemi in tempo reale,
dipendenti dallo stato complesso, il comportamento simultaneo deve essere modellato utilizzando una capsula
contenente capsule secondarie.
-
Con più di 5 ± 2 stati in un singolo diagramma, considerare l'utilizzo di stati secondari. Si applica il senso
comune: dieci stati in un pattern assolutamente regolare possono andar bene, ma due stati con quaranta transizioni
tra di loro hanno ovviamente bisogno di essere riconsiderate. Assicurarsi che la macchina a stati sia
comprensibile.
-
Assegnare il nome alle transizioni per quello che attivano e/o per quello che accade durante la transizione.
Scegliere i nomi che aumentano la comprensione.
-
Quando si osserva il vertice di una scelta, bisognerebbe chiedersi se è possibile delegare la responsabilità di
quella scelta ad un altro componente, come quella che viene presentata all'oggetto come insieme distinto di segnali
sui quali agire (ad es., invece di una scelta su msg->data > x), far prendere la decisione al mittente o a
qualche altro attore intermediario ed inviare il segnale con la decisione esplicita nel nome del segnale (ad es.,
usare segnali con il nome isFull e isEmpty invece di avere un valore di segnale cui è stato assegnato un nome e
controllare la data del messaggio).
-
Assegnare un nome alle domande che hanno avuto una risposta al vertice della scelta descrittivamente, ad es.
'esisteAncoraLaVita' o 'tempoDiProtestare'.
-
All'interno di ciascun oggetto, provare a conservare univoci i nomi del vertice della scelta (per lo stesso motivo
per il quale si conservano univoci i nomi delle transizioni).
-
Ci sono troppi frammenti di lunghe code o transizioni? Dovrebbero invece essere usate le funzioni, e i frammenti di
codice comune sono catturati come funzioni? Una transizione dovrebbe leggere come pseudo-codice ad alto livello, e
dovrebbe aderire allo stesso o anche a più regole stringenti di lunghezza come le funzioni C++. Ad esempio, una
transizione con più di 25 righe di codice viene considerata eccessivamente lunga.
-
Alle funzioni bisogna assegnare un nome in base a quello che fanno.
-
Porre particolare attenzione alle azioni di entrata e di uscita: è molto semplice apportare modifiche e dimenticare
di cambiare le azioni di entrata e di uscita.
-
Le azioni di uscita possono essere usate per fornire funzioni di sicurezza, ad es. l'azione di uscita dallo stato
'heaterOn' disattiva la stufa (heater), dove le azioni vengono usate per rinforzare un'asserzione.
-
Generalmente gli stati secondari devono contenere uno o più stati, a meno che la macchina a stati non sia astratta
e verrà ridefinita da classi secondarie dell'elemento di inclusione.
-
I punti di scelta devono essere usati al posto della logica condizionale nelle azioni o nelle transizioni. Il punto
di scelta è facilmente individuabile, laddove la logica condizionale nel codice viene nascosto alla vista ed è
semplice da lasciarsi sfuggire.
-
Evitare condizioni di guardia
-
Se l'evento attiva svariate transizioni, non c'è controllo su quale condizione di guardia venga valutata
per prima. Come risultato, i risultati possono essere imprevedibili.
-
Più di un condizione di guardia potrebbe essere 'true', ma può essere seguita solo una transizione. Il
percorso scelto può essere imprevedibile.
-
Le condizioni di guardia sono non-visive; è più difficile 'vedere' la loro presenza.
-
Evitare macchine a stati somiglianti a diagrammi di flusso.
-
Ciò può indicare un tentativo di modellare un'astrazione che non è davvero lì, come:
-
uso di una classe attiva per modellare il comportamento che meglio si adatta ad una classe di dati
o passiva.
-
modellazione di una classe di dati utilizzando una classe di dati ed una classe attiva accoppiati
molto saldamente (ad es., la classe di dati è stata utilizzata per passare le informazioni sul tipo
ma la classe attiva contiene la maggior parte dei dati che deve essere associata alla classe di
dati).
-
Questo uso sbagliato delle macchine di stato può essere riconosciuto dai seguenti sintomi:
-
messaggi spediti a 'self', principalmente solo per riutilizzare il codice
-
pochi stati, con molti punti di scelta
-
in alcuni casi, una macchina a stati senza cicli. Queste macchine a stati sono valide in
applicazioni di controllo del processo o quando si prova a controllare una sequenza di eventi; la
loro presenza durante l'analisi di solito rappresenta la degenerazione della macchina a stati in un
diagramma di flusso.
-
Quando il problema viene identificato:
-
Considerare la possibilità di dividere la classe attiva in unità più piccole con più responsabilità
distinte.
-
Spostare più comportamenti in una classe di dati associata al problema della classe attiva.
-
Spostare più comportamenti in funzioni di classe attiva.
-
Rendere più significativi i segnali invece di confidare sui dati.
Una macchina a stati astratta è una astratto che ha bisogno di maggiori dettagli prima di poter essere utilizzata per
scopi pratici. Le macchine a stati astratte possono essere utilizzate per definire un comportamenti generico e
riutilizzabile che viene in seguito perfezionato negli elementi del modello successivo.
Figure 5. Una macchina a stati astratta
Esaminare la macchina a stati astratta nella Figura 5. La semplice macchina a stati rappresentata è rappresentativa del
massimo livello astratto di comportamento (l'automa di "controllo") di molti differenti tipi di elementi in sistemi
guidati dall'evento. Sebbene tutti condividano il modulo di alto livello, i differenti tipi di elemento possono avere
comportamenti dettagliati ampiamente diversi nello stato di Esecuzione a secondo del loro scopo. Pertanto, questa
macchina a stati potrebbe essere più ampiamente definita in una certa classe astratta che serve come classe root per le
diverse classi attive specializzate.
Si possono dunque definire due differenti perfezionamenti di questa macchina a stati astratta utilizzando
l'ereditarietà. Questi due perfezionamenti, R1 e R2, sono illustrati nella Figura 6. Per chiarezza, gli elementi
ereditati dalla classe principale sono disegnati in grigio.
Figure 6. Due perfezionamenti della macchina a stati nella Figura 5.
I due perfezionamenti differiscono chiaramente nel modo in cui scompongono lo stato di Esecuzione ed anche nel modo in
cui estendono la transizione originale di "inizio". Queste scelte possono ovviamente essere fatte solo una volta che si
conosce il perfezionamento e perciò, non è stato possibile farle con una transizione singola completa nella classe
astratta.
La capacità di "continuare" entrambe le transizioni in entrata ed in uscita è fondamentale per il tipo di
perfezionamento precedentemente descritto. Può apparire che i punti di ingresso e gli stati finali, uniti alle
transizioni continuate siano sufficienti a fornire queste semantiche. Sfortunatamente, ciò non è sufficiente dove
esistono transizione multiple differenti che hanno bisogno di essere estese.
Quello che viene richiesto per un pattern astratto di comportamento è un modo di concatenazione di due o più segmenti
di transizione che vengono tutti eseguiti nell'ambito di un singolo passo di esecuzione al completamento
(run-to-completion). Ciò vuol dire che le transizioni che entrano in uno stato gerarchico vengono divise nella parte in
entrata che effettivamente termina sul confine dello stato e nell'estensione che continua all'interno dello stato. In
modo analogo, le transizioni in uscita derivanti da uno stato nidificato gerarchicamente vengono segmentate in una
parte che termina nel confine dello stato di inclusione ed una parte che continua dal limite dello stato fino allo
stato di destinazione. Questo risultato può essere raggiunto in UML con l'introduzione del concetto di stato a
catena. Questo è modellato da uno stereotipo (<<chainState>>) del concetto di stato UML. Si tratta di
uno stato il cui unico scopo è quello di "incatenare" ulteriori transizioni automatiche (disattive) in una transizione
di immissione. Uno stato di catena non ha una struttura interna, non ha compiti interni, né azione di uscita. Non ha
neanche transizioni attivate dagli eventi. Può avere un numero qualsiasi di transizioni di immissione. Può avere una
transizione senza evento di attivazione; questa transizione si alimenta automaticamente quando una transizione di
immissione attiva lo stato. Lo scopo di tale ambito è quello di incatenare una transizione di immissione in una
transizione di output separata. Tra le transizioni di immissione e quelle di output incatenate, una si collega ad un
altro stato all'interno dello stato che li contiene e l'altra si collega ad un altro stato al di fuori dello stato che
li contiene. Lo scopo per il quale è stato introdotto uno stato di catena è stato quello di separare la specifica
interna dello stato di contenimento dal suo ambiente esterno. è una questione di incapsulamento.
In effetti, uno stato a catena rappresenta uno stato di "attraversamento" che serve a incatenare una transizione ad una
transizione di continuazione specifica. Se non viene definita una transizione di continuazione, la transizione termina
in uno stato a catena e si devono eventualmente generare transizioni su uno stato di chiusura per far circolare le
cose.
Il segmento di esempio della macchina a stati nella Figura 7 illustra gli stati a catena e le loro notazioni. Gli stati
a catena sono rappresentati in un diagramma di macchina a stati attraverso cerchi bianchi posizionati all'interno dello
stato gerarchico appropriato (questa notazione è simile agli stati di inizio e di fine). I cerchi sono icone dello
stereotipo dello stato a catena e sono di solito disegnate accanto al limite, per praticità. (Infatti, una variazione
di notazione dovrebbe essere disegnata sul bordo dello stato di inclusione).
Figure 7. Stati a catena e transizioni incatenate.
La transizione incatenata di questo esempio consiste in tre segmenti di transizione incatenati e1/a11-/a12-/a13. Quando
viene ricevuto il canale e1, viene presa la transizione etichettata e1/a11, la sua azione a11 viene eseguita e poi
viene raggiunto lo stato c1. Dopo di ciò, viene presa la transizione di continuazione tra c1 e c2 e, alla fine, poiché
c2 è anche uno stato a catena, la transizione da c2 a S21. Se gli stati, insieme a questi percorsi, hanno tutti le
azioni di entrata e di uscita, la sequenza attuale dell'esecuzione dell'azione procede nel modo seguente:
-
azione d'uscita di S11
-
azione a11
-
azione d'uscita di S1
-
azione a12
-
azione di entrata di S2
-
azione a13
-
azione di entrata di S21
Tutto ciò viene eseguito nell'ambito di un passo singolo completo.
Questo dovrebbe essere confrontato con le semantiche di esecuzione dell'azione della transizione diretta e2/a2, che
sono:
-
azione d'uscita di S11
-
azione d'uscita di S1
-
azione a2
-
azione di entrata per stato S2
-
azione di entrata per stato S21
|