Module 6 · La conception

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 concept

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 :

Emprunt.java
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.

🧠 Quiz éclair

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'étatsDiagramme d'activités
Une boîte est…une situation d'un objetune tâche qu'on exécute
De qui parle-t-il ?d'un seul objet, du début à la find'un processus, qui traverse plusieurs acteurs
Pendant une boîte, on…attend un événementtravaille
Une flèche part quand…un événement arrive de l'extérieurla tâche précédente est finie
Le nom d'une boîteValidée, En retardVé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.

🧠 Quiz éclair

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.

⚠️ Piège fréquent

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.

📖 La formule

é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.

✍️ Exercice de lecture

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 / actionpendant 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.

Le même état, occupé brièvement puis longuement
le livre est rendu le jour même
entry /
do / surveiller
exit /
le livre est gardé trois semaines
entry /
do / surveiller
exit /
l'objet entre dans l'état il y séjourne un événement l'en fait sortir
Les douze colonnes valent un séjour dans l'état, quelle que soit sa durée réelle — en haut le lecteur rend le livre le jour même, en bas il le garde trois semaines. 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.
⚠️ Piège fréquent

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.

🧠 Quiz éclair

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.

🔗 Pont — le diagramme d'états est la spécification d'un patron

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.

Le même événement, écrit devant chaque état puis devant le cadre
Sans état composite
EnCours declarerPerdu() ──▶ Perdu
Prolongé declarerPerdu() ──▶ Perdu
EnRetard declarerPerdu() ──▶ Perdu
le jour où une situation active de plus apparaît, il faut penser à réécrire la flèche une fois de plus — et c'est cet oubli-là qui se produit
Avec état composite
Actif
EnCours prolonger(j) ──▶ Prolongé
Prolongé après(échéance) ──▶ EnRetard
Actif declarerPerdu() ──▶ Perdu
la flèche part du CADRE : elle vaut donc depuis n'importe lequel des états qu'il contient, y compris ceux qu'on ajoutera plus tard
Le même comportement, spécifié deux fois. En haut, 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  ──────▶  ◉
✍️ Exercice de lecture

À 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.

OrdreCe qu'on chercheDans l'énoncé, on souligne…
1l'objet dont on parlele nom qui reste le même d'un bout à l'autre
2les étatsles adjectifs et les participes passés
3l'état initial« au départ », « à la création »
4les événementsles verbes à l'infinitif, et les échéances
5les gardes« si », « à condition que », « tant que »
6les actionsce qui doit se produire en plus du changement
7ce 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.

🧠 Quiz éclair

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.

✍️ Exercice de lecture

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.

🎓 À l'examen

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.

À retenir

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.

🗂️ L'aide-mémoire
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 entry et exit de do
entry et exit sont ponctuels et ininterruptibles ; do dure et se fait couper par l'événement qui fait sortir
Ce qui distingue exit d'une action posée sur une transition
exit vaut 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, puis entry — 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 exit du 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