Les diagrammes d'états
Tous les diagrammes vus jusqu'ici décrivent un instant : les concepts du métier, les classes du logiciel, l'ordre des messages d'une opération. Celui-ci décrit une durée — la vie entière d'un objet, depuis sa naissance jusqu'à ce qu'il n'ait plus rien à faire. Et il répond à la question que les autres ne posent pas : pourquoi le même appel réussit aujourd'hui et échoue demain.
Le module Le modèle du domaine
a déclaré une opération estEnRetard() sur un Emprunt, et le
module Le diagramme
de classes & l'architecture logique lui a donné une signature. Ni l'un ni l'autre
n'a dit quand un emprunt devient en retard, ni ce qu'il devient ensuite.
Le diagramme d'états est le document qui le dit. Il ne montre ni classes ni messages : il montre un seul objet, les situations successives dans lesquelles il se trouve, et les événements qui le font passer de l'une à l'autre. Un objet dont le comportement dépend de son passé — et pas seulement de ses arguments — mérite un tel diagramme. Les autres n'en ont aucun besoin, et c'est le premier tri à savoir faire.
L'objet qui se souvient
Prends deux objets du même modèle du domaine. Un Livre à qui l'on demande son
titre répond toujours la même chose : la réponse ne dépend que de ses données, jamais
de ce qui lui est arrivé avant. Un Emprunt à qui l'on demande
prolonger(7), lui, répond parfois oui et parfois non — et l'appel est
rigoureusement identique dans les deux cas. Ce qui a changé n'est pas l'argument, c'est
l'histoire de l'objet.
C'est exactement le tri. Un objet dont la réponse au même message dépend de ce qu'il a vécu est un objet à états : il mérite un diagramme d'états. Un objet dont la réponse ne dépend que de ses arguments et de ses données n'en a aucun besoin, et lui en dessiner un produit un diagramme à un seul état, c'est-à-dire rien.
Le symptôme dans le code est très reconnaissable, et c'est souvent par lui qu'on découvre qu'on avait affaire à un objet à états sans l'avoir vu :
public void prolonger(int jours) {
if (statut.equals("rendu")) {
throw new IllegalStateException("déjà rendu");
}
if (statut.equals("perdu")) {
throw new IllegalStateException("déclaré perdu");
}
if (statut.equals("enRetard")) {
return; // on ignore, sans rien dire
}
if (statut.equals("prolonge")) {
return; // une seule prolongation
}
dateRetourPrevue = dateRetourPrevue.plusDays(jours);
statut = "prolonge";
}
Chaque méthode de la classe rouvre la même cascade, avec les mêmes chaînes de caractères et pas tout à fait les mêmes cas. La règle du cycle de vie existe bel et bien — elle est simplement éparpillée, réécrite à chaque méthode, et rien ne dit si les copies sont d'accord entre elles. Le diagramme d'états est la spécification que ce code applique sans la porter : une fois dessinée, elle se lit d'un coup d'œil, et les oublis se voient.
Voici deux classes. Laquelle mérite un diagramme d'états, et à quoi le vois-tu sans lire les corps de méthode ?
class Livre {
String titre;
String isbn;
String auteurPrincipal() { ... }
boolean correspondA(String recherche) { ... }
}
class Reservation {
Date poseeLe;
String statut;
void confirmer() { ... }
void annuler() { ... }
boolean peutEtreConfirmee() { ... }
}
Reservation, et deux marques le disent avant qu'on ait
ouvert une seule méthode. La première est l'attribut statut : un
champ dont la seule fonction est de retenir où l'on en est est un diagramme
d'états qui s'ignore. La seconde est peutEtreConfirmee() — une opération
qui demande si une autre opération est permise n'a de sens que si la permission
change avec le temps.
Livre n'a ni l'une ni l'autre. auteurPrincipal() et
correspondA() rendent toujours la même réponse pour les mêmes
données ; aucun appel ne modifie ce que le suivant renverra. Un diagramme
d'états pour Livre aurait un seul état et aucune transition.
L'état, et ce qui n'en est pas un
Un état est une situation dans laquelle un objet se trouve pendant un certain temps, et qui détermine sa réaction aux événements qu'il peut recevoir. Il se dessine par un rectangle aux coins arrondis portant son nom, et ce nom s'écrit presque toujours au participe passé ou avec un adjectif — Rendu, En retard, Perdu — parce qu'il décrit une manière d'être, jamais une action.
Cette dernière phrase n'est pas une préférence de style. C'est la ligne de partage qui coûte le plus de points à l'examen, parce que deux diagrammes du cours acceptent des boîtes qui se ressemblent : le diagramme d'états et le diagramme d'activités. Voici ce qui les sépare, et il faut savoir le dire en une phrase.
| Diagramme d'états | Diagramme d'activités | |
|---|---|---|
| Une boîte est… | une situation d'un objet | une tâche qu'on exécute |
| De qui parle-t-il ? | d'un seul objet, du début à la fin | d'un processus, qui traverse plusieurs acteurs |
| Pendant une boîte, on… | attend un événement | travaille |
| Une flèche part quand… | un événement arrive de l'extérieur | la tâche précédente est finie |
| Le nom d'une boîte | Validée, En retard | Vérifier le stock, Emballer |
Le test le plus rapide, quand on hésite devant un énoncé : demande-toi qui est le sujet de chaque boîte. Si c'est toujours le même objet, à des moments différents de sa vie, c'est un diagramme d'états. Si les boîtes changent de sujet — le magasinier vérifie, puis le préparateur emballe, puis le transporteur livre — c'est un processus, donc un diagramme d'activités.
Deux énoncés, presque les mêmes mots. Lequel appelle un diagramme d'états, lequel un diagramme d'activités ?
(A) « Une commande est validée, puis payée, puis expédiée,
puis livrée. Elle peut être annulée tant qu'elle
n'est pas payée. »
(B) « Le magasinier vérifie le stock, puis le préparateur
emballe la commande, puis le transporteur la livre.
Si le stock manque, le service achats commande. »
(A) états, (B) activités — et ce n'est ni la présence d'un « puis » ni celle d'une alternative qui tranche, puisque les deux en ont. C'est le sujet.
En (A), les cinq boîtes parlent toutes de la commande : c'est le même objet qu'on retrouve validé, puis payé, puis expédié. En (B), chaque boîte change d'acteur — le magasinier, le préparateur, le transporteur, le service achats. Personne n'y « est » quoi que ce soit : tout le monde y fait quelque chose.
Le piège de (A) est que « validée, payée, expédiée » ressemble à une suite d'étapes. Mais expédier est le travail de quelqu'un d'autre ; ce que le diagramme retient, c'est que la commande, elle, est désormais expédiée.
Confondre un état avec la valeur d'un attribut. Un
Emprunt qui porte nombreDeProlongations a autant de valeurs
possibles que l'entier en autorise ; en faire des états donnerait un diagramme
infini. Une valeur n'est un état que si le comportement de l'objet change
avec elle, et si le nombre de cas est petit et fixé.
La nuance qui décide, et elle se vérifie en une seconde : combien de cas, et
réagissent-ils différemment ? « Zéro ou une prolongation, et au-delà on
refuse » fait deux cas au comportement distinct — EnCours et
Prolongé sont de vrais états. « Le montant de l'amende » fait
une infinité de cas qui réagissent tous pareil : c'est un attribut, et il reste
dans le diagramme de classes.
La transition, et son étiquette en trois parties
Une transition est une flèche d'un état vers un autre. Elle porte une étiquette, et cette étiquette est le cœur de toute la notation : c'est elle qu'on lit de travers, c'est elle qu'on écrit incomplète, et c'est sur elle que les questions d'examen portent. Elle a trois parties, et deux ponctuations seulement les séparent — les crochets et la barre oblique.
événement [garde] / action
événement [garde] / action
ÉVÉNEMENT ce qui ARRIVE à l'objet
rendre() un appel
après(30 jours) le temps
quand(solde < 0) une condition
devenue vraie
[GARDE] une condition BOOLÉENNE, testée
quand l'événement arrive.
Fausse : rien ne se passe.
[prolongations < 1]
/ ACTION ce qui S'EXÉCUTE pendant le
passage. Instantané.
/ facturerAmende()
ÉTIQUETTES COMPLÈTES
rendre()
rendre() / facturerAmende()
prolonger(j) [prolongations < 1]
payer(m) [solde >= m] / debiter(m)
Seul l'événement est normalement présent ; la garde et l'action s'omettent dès qu'il n'y en a pas. C'est la réponse à la question la plus fréquente sur cette notation, et la source des étiquettes qu'on croit incomplètes alors qu'elles sont justes. Une flèche nue, sans aucun événement, existe aussi — c'est la transition automatique, traitée plus bas —, mais c'est un cas particulier qu'on n'écrit que lorsqu'on le veut vraiment.
Les trois parties se confondent d'autant plus facilement qu'elles se ressemblent à
l'écrit : rendre(), facturerAmende() et
prolongations < 1 ont toutes l'air d'être du code. Le seul moyen sûr de
les distinguer est la position par rapport aux deux signes. Avant les
crochets : ce qui arrive. Entre les crochets : ce qu'on vérifie. Après la barre
oblique : ce qu'on fait.
Il reste une question à laquelle on répond mal une fois sur deux : que devient l'événement quand la garde est fausse ? Il est consommé et perdu. L'objet ne bouge pas, l'action ne s'exécute pas, et rien n'est mis en attente pour plus tard : l'événement a été présenté, aucune transition ne l'a pris, il disparaît. C'est exactement ce qui arrive à un événement pour lequel l'état courant n'a aucune flèche — et c'est le point le plus rentable de tout le module, parce qu'une question d'examen sur trois s'y joue.
Pour chacune de ces étiquettes, dis quel est l'événement, quelle est la garde, quelle est l'action — et laquelle de ces trois parties manque, s'il en manque une.
(A) declarerPerdu() / facturerRemplacement()
(B) après(dateRetourPrevue)
(C) prolonger(jours) [prolongations < 1]
(D) rendre() [enRetard] / facturerAmende()
(E) / envoyerAccuseReception()
Voir le corrigé
(A) événement declarerPerdu(), action
facturerRemplacement() ; pas de garde — le passage a lieu
chaque fois que l'événement arrive.
(B) événement après(dateRetourPrevue), seul. Ni garde
ni action : c'est l'étiquette minimale, et elle est parfaitement valide. Noter
que l'événement est ici le temps — personne n'appelle de méthode,
l'échéance arrive toute seule.
(C) événement prolonger(jours), garde
prolongations < 1 ; pas d'action. Si la garde est fausse
quand l'appel arrive, l'objet reste où il est et l'appel est perdu.
(D) les trois sont là : événement rendre(), garde
enRetard, action facturerAmende(). C'est la forme complète,
et la plus rare.
(E) il manque l'événement — et c'est le seul cas des cinq qui demande de réfléchir. L'étiquette est légale : c'est une transition automatique, qui part dès que l'état d'origine a fini son travail, sans que personne ne la déclenche. Mais écrite par mégarde, à la place d'un événement oublié, elle dit quelque chose de très différent de ce qu'on croyait — d'où la règle de sécurité : une flèche sans événement se justifie, ou se corrige.
entry, do, exit — le ponctuel et le continu
Jusqu'ici, tout ce qui s'exécute est accroché à une flèche. On peut aussi accrocher du comportement à un état lui-même, et c'est utile dès qu'une même chose doit se produire quelle que soit la porte par laquelle on entre. Trois mots-clés, écrits sous le nom de l'état et séparés de lui par un trait :
entry / action— à l'entrée dans l'état, par n'importe quelle transition.exit / action— à la sortie, par n'importe quelle transition.do / action— pendant le séjour, tant que l'objet est là.
La différence entre les deux premiers et le troisième n'est pas une nuance :
entry et exit sont ponctuels et ininterruptibles
— ils occupent un instant, et aucun événement ne peut les couper. do est
continu et interruptible — il dure autant que le séjour, et un événement
qui fait sortir de l'état l'arrête là où il en est. C'est exactement ce que la figure
suivante rend visible.
entry (ici notifier()) et exit
(ici journaliser()) restent larges d'une seule colonne
dans les deux cas, parce que ce sont des instants : allonger le séjour ne
les allonge pas. Seul do s'étire, parce qu'il est le seul à durer — et il
ne se termine pas de lui-même, il est coupé par l'événement qui fait
sortir.
Mettre dans exit ce qui ne vaut que pour une sortie.
Un Emprunt quitte EnCours de trois façons : rendu, échu,
déclaré perdu. Si l'on écrit exit / facturerAmende(), l'amende est
facturée aussi quand le livre est rendu à temps — parce que exit
veut dire « par n'importe quelle transition », sans exception.
Le réflexe qui tranche : cette action dépend-elle de la porte par laquelle je
sors ? Si oui, elle va sur la flèche concernée, jamais dans
exit. Si non — journaliser, libérer une ressource, prévenir un
observateur —, exit est exactement fait pour ça, et l'écrire une fois vaut
mieux que de le répéter sur chacune des flèches.
Une pompe à essence est modélisée ainsi. Un client fait le plein normalement, puis la pompe tombe en panne pendant le service suivant. Quelles actions se sont exécutées, dans quel ordre ?
état EnService
entry / allumerVoyant()
do / surveillerDebit()
exit / eteindreVoyant()
EnService raccrocher() / imprimerRecu() ▶ Libre
EnService panne() ▶ HorsService
Premier client : allumerVoyant(), puis
surveillerDebit() tant qu'il sert, puis — à raccrocher() —
eteindreVoyant() et imprimerRecu().
Second client : allumerVoyant(),
surveillerDebit() interrompu par la panne, puis
eteindreVoyant() — et pas imprimerRecu(),
qui est sur la flèche vers Libre et non dans exit.
C'est là qu'est la leçon : le voyant s'éteint dans les deux cas parce qu'il n'est pas concerné par la raison du départ, tandis que le reçu ne s'imprime que lorsqu'un service s'est terminé normalement. La place de chaque action est la spécification.
Les pseudo-états, et l'auto-transition
Deux marques ne sont pas des états, bien qu'elles occupent une place sur le diagramme. Le pseudo-état initial est un disque plein : il ne désigne aucune situation, il dit seulement dans quel état l'objet naît. Le pseudo-état final est un disque plein cerclé : il dit que l'objet n'a plus aucun comportement — il ne réagira plus à rien. Un diagramme sans initial est incomplet ; un diagramme sans final est parfaitement normal, car beaucoup d'objets n'ont pas de fin.
L'auto-transition, elle, est une vraie transition qui revient sur l'état
d'où elle part. Elle a l'air de ne rien faire, et c'est faux : elle sort
réellement de l'état puis y rentre, donc elle exécute exit, puis
l'action de la flèche, puis entry. C'est la seule façon de relancer un
minuteur, de réinitialiser un compteur, ou de journaliser une tentative qui n'a rien
changé.
Reste le point le plus rentable du module, et il n'a pas de symbole : ce qui n'est pas dessiné est une décision. Si l'état courant n'a aucune flèche portant l'événement reçu, l'événement est ignoré — pas mis en file, pas reporté, pas transformé en erreur : il disparaît, et l'objet reste exactement où il était. Un énoncé qui dit « on ne peut pas annuler une commande payée » ne demande donc pas d'ajouter quoi que ce soit : il demande de ne pas tracer la flèche.
La cascade de if (statut.equals(…)) du début de ce module a un remède
nommé, et le cours le rencontrera avec les patrons du « Gang of Four » :
le patron État. Son principe est de faire de chaque état une classe et
de déléguer chaque opération à l'objet-état courant ; la cascade disparaît alors,
parce qu'aucune méthode n'a plus besoin de demander où l'on en est.
Ce qu'il faut retenir dès maintenant, c'est le sens de la flèche entre les deux : le diagramme d'états n'illustre pas le patron, il le spécifie. Chaque état devient une classe, chaque événement une méthode de l'interface commune, et chaque transition l'installation de l'état suivant. Un diagramme d'états propre est donc déjà la moitié de la conception — et c'est aussi vrai du patron Stratégie, qu'on confondra avec lui : même structure, intentions opposées. L'un change de comportement parce que l'objet a vécu, l'autre parce qu'on l'a configuré.
L'état composite, ou écrire une sortie au lieu de trois
Un état composite est un état qui contient lui-même d'autres états. Ce n'est pas une commodité de mise en page : c'est ce qui empêche une famille de transitions de se répéter. Le besoin apparaît toujours de la même façon — plusieurs états voisins réagissent identiquement au même événement, et l'on se retrouve à tracer la même flèche autant de fois qu'il y a d'états.
Un Emprunt en est l'exemple exact. Tant qu'il n'est ni rendu ni perdu, il est
dans l'une de trois situations : EnCours, Prolongé ou
EnRetard. Et de chacune, le même événement peut survenir : le lecteur
annonce qu'il a perdu le livre.
declarerPerdu() se
réécrit devant chaque situation active, et rien ne garantit que les copies resteront
d'accord entre elles. En bas, les situations actives sont réunies dans un cadre nommé
Actif et l'événement part de ce cadre : il s'écrit une seule fois, et
il couvre automatiquement tout ce que le cadre contiendra. Les transitions
internes — celles qui vont d'une situation active à une autre — restent à
l'intérieur et ne changent pas.
Une transition qui part du cadre s'appelle une transition de groupe, et
elle a une propriété qu'il faut savoir énoncer : quitter le cadre exécute le
exit de l'état interne puis celui du cadre. On sort par étages, du
plus profond vers le plus haut — et l'on entre dans l'autre sens.
Reste la moitié dont la figure ne parle pas, et c'est délibéré : elle ne montre que les sorties. L'entrée pose sa propre question, et elle tombe souvent à l'examen — quand une flèche arrive sur le cadre, dans lequel de ses sous-états atterrit-on ? La réponse n'est pas « le premier », ni « celui du haut » : un cadre qui reçoit une transition doit contenir son propre pseudo-état initial, un disque plein posé à l'intérieur, qui désigne le sous-état d'arrivée. Sans lui, le diagramme est ambigu, et l'ambiguïté est une faute — pas un détail de mise au propre.
L'autre possibilité est de viser directement : une flèche venue de l'extérieur peut traverser la bordure et pointer un sous-état précis. Les deux formes sont légales et ne disent pas la même chose. Pointer le cadre signifie « entre par la porte normale, quelle qu'elle soit » et survivra à un changement du sous-état de départ ; pointer un sous-état signifie « entre ici, et nulle part ailleurs ». Le premier se maintient tout seul, le second dit une intention plus forte — et c'est très exactement le même arbitrage que la transition de groupe, pris dans l'autre sens.
Lire : où est l'objet après cette séquence ?
C'est la question d'examen la plus courante sur ce diagramme, et elle se traite mécaniquement : on part du pseudo-état initial, on traite les événements un par un et dans l'ordre, et pour chacun on se pose deux questions dans cet ordre — l'état où je suis a-t-il une flèche portant cet événement ? puis sa garde est-elle vraie ? Si l'une des deux réponses est non, on ne bouge pas, et on passe à l'événement suivant.
Voici le cycle de vie complet d'un Emprunt, écrit comme on l'écrirait sur une
copie : une transition par ligne. C'est aussi la forme la plus sûre pour vérifier son
propre dessin, parce qu'un oubli s'y voit.
● ──────▶ EnCours
EnCours prolonger(j) [prolongations < 1] ▶ Prolongé
EnCours après(dateRetourPrevue) ▶ EnRetard
EnCours rendre() ▶ Rendu
Prolongé après(dateRetourPrevue) ▶ EnRetard
Prolongé rendre() ▶ Rendu
EnRetard rendre() / facturerAmende() ▶ Rendu
Actif declarerPerdu() / facturer() ▶ Perdu
Actif = { EnCours, Prolongé, EnRetard }
Rendu ──────▶ ◉ Perdu ──────▶ ◉
À partir de la table ci-dessus, dans quel état se trouve l'emprunt après chaque séquence ? Traite les événements dans l'ordre, et dis à chaque fois pourquoi.
(A) prolonger(7) · rendre()
(B) prolonger(7) · prolonger(7) · après(dateRetourPrevue)
(C) après(dateRetourPrevue) · prolonger(7) · rendre()
(D) rendre() · declarerPerdu()
Voir le corrigé
(A) Rendu, sans amende. EnCours a bien une flèche
prolonger, et la garde est vraie puisque aucune prolongation n'a encore
eu lieu → Prolongé. De là, rendre() existe →
Rendu. L'action facturerAmende() n'est que sur la flèche
qui part de EnRetard, et l'on n'y est jamais passé.
(B) EnRetard. La première prolongation mène à Prolongé.
La seconde est l'intérêt de la question : Prolongé n'a aucune
flèche prolonger, donc l'événement est ignoré et l'objet ne bouge
pas. Ce n'est pas une erreur, c'est la spécification — une seule prolongation. Puis
l'échéance arrive → EnRetard.
(C) Rendu, avec amende. L'échéance mène à EnRetard. De
là, prolonger n'existe pas : ignoré, on reste en retard — on ne
prolonge pas un emprunt déjà échu. Puis rendre() emprunte la flèche de
EnRetard, qui porte / facturerAmende() : l'amende est
facturée pendant le passage.
(D) Rendu. rendre() mène à Rendu, qui est
un état final. declarerPerdu() est ignoré — et cette fois pour une raison
plus forte que l'absence de flèche : Rendu n'appartient pas à
Actif, donc la transition de groupe ne le concerne pas. C'est le piège de
la question : une transition de groupe ne vaut que pour ce que son cadre
contient.
Construire un diagramme depuis un énoncé
Le sens inverse est l'autre question d'examen, et elle se traite dans un ordre précis. Cet ordre n'est pas un conseil de confort : chaque étape n'utilise que ce que la précédente a produit, et le faire dans le désordre est la façon la plus sûre d'obtenir un diagramme où des flèches partent d'états qu'on n'a pas encore décidés.
| Ordre | Ce qu'on cherche | Dans l'énoncé, on souligne… |
|---|---|---|
| 1 | l'objet dont on parle | le nom qui reste le même d'un bout à l'autre |
| 2 | les états | les adjectifs et les participes passés |
| 3 | l'état initial | « au départ », « à la création » |
| 4 | les événements | les verbes à l'infinitif, et les échéances |
| 5 | les gardes | « si », « à condition que », « tant que » |
| 6 | les actions | ce qui doit se produire en plus du changement |
| 7 | ce qu'on ne trace PAS | « on ne peut plus », « il est trop tard pour » |
La septième ligne est celle qu'on saute, et c'est elle qui distingue une copie complète d'une copie approximative. Un énoncé qui interdit quelque chose ne demande pas d'ajouter une flèche vers un état Erreur : il demande de ne rien tracer, et de l'écrire en une ligne à côté du diagramme pour que le correcteur sache que c'est un choix et non un oubli.
Un étudiant rend ce diagramme pour l'énoncé « une réservation confirmée ne peut plus être annulée ». Qu'est-ce qui cloche ?
● ──────▶ Demandée
Demandée confirmer() ▶ Confirmée
Demandée annuler() ▶ Annulée
Confirmée annuler() ▶ Erreur
Erreur revenir() ▶ Confirmée
Les deux dernières lignes n'auraient pas dû être écrites.
« Ne peut plus être annulée » se traduit par l'absence de flèche
annuler depuis Confirmée : l'événement arrive, aucune
transition ne le porte, il est ignoré, et la réservation reste confirmée. C'est déjà
exactement le comportement demandé.
Erreur n'est pas un état de la réservation — la réservation, elle, n'a
pas changé de situation. C'est un état du logiciel qui a reçu un appel
illégal, et cela relève de la gestion d'exception, pas du cycle de vie. Le symptôme
qui le trahit : il faut inventer un événement revenir() dont
l'énoncé ne parle nulle part, pour réparer un état dont il ne parle pas davantage.
Applique les sept étapes à cet énoncé, puis écris la table des transitions comme celle de la section précédente. Note aussi, en une ligne, ce que tu as décidé de ne pas tracer.
« Une session ouverte par un lecteur est active dès qu'il
s'est authentifié. Après trente minutes sans action, elle
devient expirée. Depuis une session expirée, se
réauthentifier la réactive, à condition que le compte ne
soit pas suspendu. Le lecteur peut fermer sa session
quand elle est active ; il ne peut pas fermer une session
déjà expirée. Chaque réactivation doit être journalisée. »
Voir le corrigé
L'objet est la session — c'est le seul nom qui traverse tout l'énoncé. Les états sont ses adjectifs : active et expirée. Suspendu n'en est pas un : il qualifie le compte, pas la session, et il servira donc de garde.
L'initial est donné par « dès qu'il s'est
authentifié » : la session naît Active. Les
événements sont après(30 min),
reauthentifier() et fermer(). La seule garde
est [compte non suspendu], et la seule action est la
journalisation.
● ──────▶ Active
Active après(30 min) ▶ Expirée
Active fermer() ▶ Fermée
Expirée reauthentifier()
[compte non suspendu]
/ journaliser() ▶ Active
Fermée ──────▶ ◉
Ce qui n'est pas tracé, et qu'il faut écrire : aucune flèche
fermer() depuis Expirée — l'énoncé l'interdit, donc
l'événement y est ignoré. Et rien non plus pour la réauthentification refusée :
quand la garde est fausse, la session reste expirée, sans qu'aucun état supplémentaire
soit nécessaire.
Deux points valent d'être remarqués. Fermée a été ajouté alors que
l'énoncé ne le nomme pas : « fermer sa session » est un verbe, donc un
événement, et tout événement doit bien mener quelque part. Et si l'on préférait écrire
/ journaliser() comme un entry de Active, ce
serait un autre diagramme : il journaliserait aussi l'ouverture
initiale, ce que l'énoncé ne demande pas.
Cette matière est celle de la semaine 7 — la semaine qui porte aussi la révision pour l'intra. Elle est bien couverte par l'examen intra du 24 octobre : la révision annoncée porte sur « les modules 1 à 5 » du semainier, et les diagrammes d'états sont ce module 5. C'est donc la dernière matière enseignée avant l'examen, et la seule que tu découvriras la semaine même où tu la révises : prévois-la en premier, pas en dernier. Chez Larman, c'est le chapitre 29.
C'est aussi le seul module du bloc qui ne sert aucun livrable, et c'est un fait utile plutôt qu'un manque : le Livrable 2 se remet le 13 octobre, au tout début de cette semaine-là, et le Livrable 3 est du code. Rien de ce que tu lis ici n'a de date de remise — cette matière ne vaut que pour l'examen.
IFT-2007 n'autorise aucun matériel. La notation entière doit donc être en tête, et elle est courte : trois parties d'étiquette, trois mots-clés internes, deux pseudo-états. Ce qui se perd à l'examen n'est d'ailleurs jamais la notation — c'est le sort d'un événement que personne n'attend.
« Donnez le diagramme d'états de ce concept, à partir de la description suivante. »
Les sept étapes de la section « Construire un diagramme depuis un énoncé », dans l'ordre, et la septième surtout : écris à côté du diagramme ce que tu as délibérément laissé sans flèche. Une ligne suffit — « annuler() est ignoré depuis Payée, conformément à l'énoncé » — et elle transforme un trou apparent en décision assumée.
Le réflexe qui rapporte : souligner les adjectifs de l'énoncé avant de dessiner quoi que ce soit. Les états sont presque toujours déjà écrits dans le texte ; c'est en les cherchant après avoir tracé des boîtes qu'on invente des états qui n'y sont pas.
« Dans quel état se trouve l'objet après la séquence d'événements suivante ? »
Traite les événements un par un, en écrivant l'état courant après chacun — jamais de tête. À chaque événement, les deux questions dans l'ordre : une flèche le porte-t-elle depuis ici ? puis sa garde est-elle vraie ?
Ce qui est attendu est presque toujours l'événement ignoré : une séquence d'examen contient au moins un événement que l'état courant n'accepte pas, et la faute est d'inventer alors une erreur ou de reculer d'un état. La bonne réponse est : il ne se passe rien, et on continue.
« Quelle est la différence entre un diagramme d'états et un diagramme d'activités ? »
En une phrase : un diagramme d'états décrit la vie d'un seul objet, dont les boîtes sont des situations ; un diagramme d'activités décrit un processus, dont les boîtes sont des tâches et peuvent changer d'acteur.
Ajoute le critère opératoire, c'est lui qui montre qu'on sait s'en servir : dans un état, l'objet attend un événement extérieur ; dans une activité, on travaille, et la flèche part toute seule quand le travail est fini.
Un diagramme d'états ne se dessine que pour un objet dont la réponse au même
message dépend de ce qu'il a vécu — les autres n'en ont pas besoin, et la cascade de
if (statut…) est le symptôme qui trahit qu'on en avait un sans le savoir.
Toute la notation tient alors en trois pièces : des situations, des flèches
étiquetées événement [garde] / action, et trois mots-clés internes dont la
différence est de nature — entry et exit sont des instants,
do est une durée qu'un départ interrompt.
Ce qui départage vraiment une copie juste d'une copie approximative n'est pourtant aucune de ces pièces : c'est le silence. Un événement qu'aucune flèche ne porte depuis l'état courant, ou dont la garde est fausse, est ignoré et perdu — pas mis en attente, pas transformé en erreur. Interdire quelque chose, dans ce langage, c'est ne rien tracer ; et l'état composite est le même geste appliqué à l'autre extrémité : écrire une sortie une seule fois plutôt que de la recopier devant chaque situation qu'elle concerne.
Et ailleurs : la machine à états est l'une des rares notations qui traverse toute l'informatique sans changer de forme. C'est elle qu'on retrouve dans un protocole réseau, dans le cycle de vie d'une commande chez un marchand, dans un analyseur lexical, dans les statuts d'un ticket de support, et dans à peu près toute interface qui doit décider si un bouton est actif. Le réflexe transférable n'est pas de savoir la dessiner : c'est de reconnaître, devant un code truffé de drapeaux booléens qui se contredisent, qu'on regarde une machine à états que personne n'a écrite.
- Les trois parties d'une étiquette de transition, dans l'ordre et avec leur ponctuation
événement [garde] / action: avant les crochets ce qui arrive, entre les crochets ce qu'on vérifie, après la barre oblique ce qu'on fait- Laquelle des trois est indispensable
- l'événement seul ; garde et action s'omettent librement. Une flèche sans événement existe — la transition automatique — mais se justifie
- Ce que devient un événement qu'aucune flèche ne porte depuis l'état courant
- il est ignoré et perdu — ni file d'attente, ni report, ni erreur. Idem quand la garde est fausse
- Ce qui distingue
entryetexitdedo entryetexitsont ponctuels et ininterruptibles ;dodure et se fait couper par l'événement qui fait sortir- Ce qui distingue
exitd'une action posée sur une transition exitvaut pour toutes les sorties ; une action sur la flèche ne vaut que pour celle-là. Le test : l'action dépend-elle de la porte par laquelle on sort ?- Ce qu'exécute réellement une auto-transition
exit, puis l'action de la flèche, puisentry— elle sort pour de bon avant de rentrer- Le symbole de l'état initial, et celui de l'état final
- initial : disque plein ; final : disque plein cerclé. Ce sont des pseudo-états — l'objet n'y séjourne jamais
- Ce qu'une transition partant d'un état composite couvre
- tous ses sous-états, présents et futurs. En sortant, on exécute le
exitdu sous-état puis celui du cadre - Ce qu'un état composite doit contenir dès qu'une flèche pointe son cadre
- son propre pseudo-état initial, qui désigne le sous-état d'arrivée. Sans lui, on ne sait pas où l'on atterrit — et le diagramme est faux
- Ce qui distingue une boîte d'états d'une boîte d'activités
- dans un état, l'objet attend un événement ; dans une activité, on travaille et la flèche part quand c'est fini. Un diagramme d'états ne parle que d'un seul objet
- Quand une valeur d'attribut mérite de devenir un état
- quand les cas sont peu nombreux, fixés, et réagissent différemment. Une valeur continue, ou sans effet sur le comportement, reste un attribut