Aller au contenu
Réussir mon BTS SIO

Tape une notion, une commande ou le nom d'une épreuve.

Par exemple

Chapitre 914 minIntermédiaire

Diagramme d'états-transitions : la machine à états d'un objet

Par Josselyn C., prof de BTS SIO. Mis à jour le .

À la fin de ce chapitre, tu sauras

  • décrire le cycle de vie d'un objet par ses états et ses transitions ;
  • écrire une transition complète : événement, garde et effet ;
  • traduire une machine à états en code C# ;
  • déduire les tests unitaires d'un diagramme d'états-transitions.

4 exercices corrigés1 schéma

Le diagramme d'états-transitions (ou machine à états, state machine diagram) décrit le cycle de vie d'un objet : les états par lesquels il passe, et les transitions qui le font changer d'état, déclenchées par des événements, sous certaines conditions, avec certains effets. On le dessine pour les objets dont le comportement dépend de leur état : une commande, un ticket, une réservation, une fiche de frais. Ce chapitre présente la notation, la syntaxe d'une transition, la traduction en code et les vérifications qui en découlent.

États et transitions

Un état est une situation durable dans la vie de l'objet, pendant laquelle il réagit d'une certaine façon aux événements. Il se dessine par un rectangle aux coins arrondis, nommé par un adjectif ou un participe : « Créée », « Clôturée », « Validée ». Une fiche clôturée accepte d'être validée ; une fiche créée, non.

Une transition est le passage d'un état à un autre. Elle se dessine par une flèche et porte un libellé de la forme :

text
événement [garde] / effet
  • l'événement (trigger) déclenche la transition : l'appel d'une opération (Valider(comptable)), un signal reçu (virement confirmé), un événement temporel (when(1er du mois), after(30 jours)) ;
  • la garde est une condition, entre crochets : la transition n'a lieu que si elle est vraie au moment de l'événement ;
  • l'effet est l'action exécutée pendant la transition, après la barre oblique.

Les trois parties sont facultatives. Une transition sans événement est franchie dès que l'action de l'état est terminée.

ÉlémentNotation
État initialUn disque plein. La transition qui en part ne porte ni événement ni garde : on indique en note l'opération qui crée l'objet (ici Visiteur.OuvrirFiche(mois))
État finalUn disque plein entouré d'un cercle ; l'objet a fini sa vie (il peut être détruit)
ÉtatUn rectangle arrondi, avec son nom
TransitionUne flèche, avec événement [garde] / effet
Transition réflexiveUne flèche qui revient sur le même état : l'objet sort puis rentre
Activité d'entréeentry / action dans l'état : exécutée à chaque fois qu'on y entre
Activité de sortieexit / action : exécutée à chaque fois qu'on en sort
Activité continuedo / activité : exécutée tant qu'on reste dans l'état
Transition interneévénement / action écrit dans l'état : on traite l'événement sans sortir de l'état
État compositeUn état qui contient lui-même des sous-états
Point de choixUn losange : la transition se poursuit vers une branche ou une autre selon des gardes

Le cycle de vie d'une fiche de frais

La fiche de frais d'AppliFrais passe par cinq états, que la base de données code sur deux lettres (CR, CL, VA, MP, RB) et que le code C# représente par l'énumération EtatFiche.

Créée (CR)SaisirForfait(…) / mettre à jour la ligneAjouterHorsForfait(…) / ajouter une lignecréée parVisiteur.OuvrirFiche(mois)Cloturer()par le visiteur, ou par le planificateurle 1er du mois suivantClôturée (CL)Refuser(motif) / marquer la ligne refuséeValider(comptable)/ montantValide := Total()Validée (VA)entry / notifier le visiteurMettreEnPaiement()Mise en paiement (MP)ConfirmerRemboursement()Remboursée (RB)after(10 ans) / archiverAucune flèche nerevient en arrière :une fiche validéene se rouvre pas.
Les cinq états d'une fiche de frais et les événements qui la font avancer

Ce que le diagramme dit, et que le code doit respecter :

  • une fiche naît à l'état Créée quand le visiteur l'ouvre ; tant qu'elle y est, il peut saisir et modifier ses frais (transitions internes : la fiche ne change pas d'état) ;
  • quand il a terminé, le visiteur appelle Cloturer() ; le 1er du mois suivant, le planificateur le fait pour les fiches restées ouvertes. La fiche passe à l'état Clôturée, et on ne peut plus ajouter de frais ;
  • dans cet état, le comptable peut refuser une ligne hors forfait (transition interne), puis il la valide : elle passe à Validée, et son montant validé est calculé pendant la transition ; en entrant dans cet état, le visiteur est notifié (le code du chapitre 6 laisse cette notification à un service de messagerie, qu'il ne montre pas) ;
  • elle est mise en paiement, puis remboursée quand le service de paie confirme le virement ;
  • aucune flèche ne revient en arrière : une fiche validée ne peut pas être rouverte. C'est une règle de gestion, que le diagramme rend évidente ;
  • au bout de dix ans, durée de conservation des pièces comptables, la fiche est archivée et sort de l'application : c'est l'état final.

Les états composites

Un état composite regroupe plusieurs sous-états qui partagent un comportement. Clôturée, Validée et Mise en paiement ont un point commun : la fiche est verrouillée, le visiteur ne peut plus rien modifier. On pourrait les regrouper dans un état composite « Verrouillée ». Une transition qui part du contour de l'état composite s'applique à tous ses sous-états : par exemple, une transition Annuler() [fraude constatée] vers un état « Annulée » partant du contour de « Verrouillée » s'appliquerait que la fiche soit clôturée, validée ou mise en paiement.

Les états composites évitent de multiplier les flèches identiques. Au BTS, il suffit de savoir les lire.

De la machine à états au code

Une machine à états se traduit de deux façons.

Avec une énumération et des gardes dans chaque opération. C'est la méthode d'AppliFrais (chapitre 6) : l'état est un attribut de type EtatFiche, et chaque opération qui déclenche une transition vérifie d'abord l'état de départ, puis fait l'effet, puis change l'état.

csharp
public void Valider(Comptable comptable)
{
    VerifierEtat(EtatFiche.Cloturee);     // la transition ne part que de Clôturée
    MontantValide = Total();              // l'effet : / montantValide := Total()
    ValideePar = comptable;
    Etat = EtatFiche.Validee;             // l'état d'arrivée
}

Une opération appelée dans un état d'où ne part aucune transition correspondante lève une exception : c'est la traduction de « tout ce qui n'est pas dessiné est interdit ».

Avec une table de transitions. Quand les transitions sont nombreuses, on les range dans une table (état de départ, événement) → état d'arrivée, que le code consulte. C'est le principe des bibliothèques de machines à états. Pour une machine de cinq ou six états, la première méthode est plus simple et plus lisible.

Le patron de conception État (State) va plus loin : une classe par état, chacune implémentant les opérations à sa façon. Il est utile quand le comportement de chaque état est riche, rarement au BTS.

Écrire les tests à partir du diagramme

Le diagramme d'états-transitions donne directement la liste des tests unitaires à écrire :

  • un test par transition dessinée : à partir de l'état de départ, l'opération mène à l'état d'arrivée et produit son effet (une fiche clôturée validée par Inès passe à Validée, avec le bon montant) ;
  • un test par transition interdite importante : une fiche créée qu'on tente de valider lève une exception et reste Créée ; une fiche validée qu'on tente de clôturer aussi ;
  • un test par garde : la garde vraie, puis la garde fausse.

Avec cinq états, quatre transitions entre eux et trois transitions internes, une douzaine de tests couvrent tout le cycle de vie. Le cours Tests montre comment les écrire (écrire un bon test unitaire).

Exercices

Exercice 1 sur 4 Lire une transition Réussi À revoir

Explique en français chacune de ces transitions d'un diagramme d'états-transitions de la classe Reservation (location de vélos) :

  1. Confirmee → EnCours : RetirerVelo() [pièce d'identité vérifiée] / démarrer le chronomètre
  2. EnAttentePaiement → Annulee : after(15 min)
  3. EnCours → Terminee : RendreVelo() / calculer le prix final
  4. Dans l'état EnCours : Prolonger(minutes) / recalculer l'heure de fin
Voir la réponseMasquer la réponse de l'exercice 1
  1. Une réservation confirmée passe à « en cours » quand le client retire le vélo, à condition que sa pièce d'identité ait été vérifiée ; pendant le passage, le chronomètre démarre. Sans vérification, l'événement est ignoré : la réservation reste confirmée.
  2. Une réservation en attente de paiement est annulée automatiquement si elle y reste 15 minutes : événement temporel, sans garde ni effet.
  3. Quand le client rend le vélo, la réservation passe à « terminée », et le prix final est calculé pendant la transition.
  4. C'est une transition interne : la prolongation recalcule l'heure de fin sans quitter l'état « en cours » (les éventuelles activités entry et exit ne sont pas rejouées).

Tu avais trouvé ?

Exercice 2 sur 4 Dessiner la machine à états d'un ticket Réussi À revoir

Un ticket d'assistance est ouvert à sa création. Quand un technicien le prend en charge, il est en cours. Le technicien peut le mettre en attente d'une réponse de l'utilisateur ; la réponse le remet en cours. Quand le technicien a résolu l'incident, le ticket est résolu et l'utilisateur en est averti. L'utilisateur peut alors confirmer (le ticket est clos) ou rouvrir le ticket (il revient en cours). Sans réponse de l'utilisateur sous 7 jours, un ticket résolu est clos automatiquement. Un ticket clos n'évolue plus.

Donne les états et les transitions, au format Départ → Arrivée : événement [garde] / effet.

Voir la réponseMasquer la réponse de l'exercice 2

États : Ouvert, EnCours, EnAttente, Resolu, Clos.

Transitions

  • ● → Ouvert, sans événement ni garde : l'opération qui crée le ticket, Creer(titre, description), s'indique dans une note
  • Ouvert → EnCours : PrendreEnCharge(technicien)
  • EnCours → EnAttente : DemanderInformation(question)
  • EnAttente → EnCours : ReponseUtilisateur(texte)
  • EnCours → Resolu : Resoudre(solution) / avertir l'utilisateur (ou entry / avertir l'utilisateur dans l'état Resolu)
  • Resolu → Clos : Confirmer()
  • Resolu → EnCours : Rouvrir(motif)
  • Resolu → Clos : after(7 jours)
  • Clos → ◉ (état final), ou pas de transition sortante : « un ticket clos n'évolue plus ».

Deux transitions mènent de Resolu à Clos, avec deux événements différents : c'est permis. Pas de flèche de Clos vers ailleurs : un ticket clos ne se rouvre pas, il faudrait en ouvrir un nouveau.

Tu avais trouvé ?

Exercice 3 sur 4 Du diagramme au code Réussi À revoir

Écris en C# les opérations PrendreEnCharge(Technicien technicien), Rouvrir(string motif) et Resoudre(string solution) de la classe Ticket de l'exercice précédent. L'état est une énumération EtatTicket ; le ticket mémorise son technicien, sa solution et le nombre de réouvertures.

Voir la réponseMasquer la réponse de l'exercice 3
csharp
public enum EtatTicket { Ouvert, EnCours, EnAttente, Resolu, Clos }

public class Ticket
{
    public EtatTicket Etat { get; private set; } = EtatTicket.Ouvert;
    public Technicien? Technicien { get; private set; }
    public string? Solution { get; private set; }
    public int NbReouvertures { get; private set; }

    public void PrendreEnCharge(Technicien technicien)
    {
        VerifierEtat(EtatTicket.Ouvert);
        Technicien = technicien;
        Etat = EtatTicket.EnCours;
    }

    public void Resoudre(string solution)
    {
        VerifierEtat(EtatTicket.EnCours);
        Solution = solution;
        Etat = EtatTicket.Resolu;
        // effet : avertir l'utilisateur (par un service de messagerie)
    }

    public void Rouvrir(string motif)
    {
        VerifierEtat(EtatTicket.Resolu);
        NbReouvertures++;
        Solution = null;
        Etat = EtatTicket.EnCours;
    }

    private void VerifierEtat(EtatTicket attendu)
    {
        if (Etat != attendu)
            throw new InvalidOperationException($"Ticket à l'état {Etat}, {attendu} attendu.");
    }
}

Chaque opération suit le même schéma : vérifier l'état de départ, faire l'effet, changer d'état. Appeler Rouvrir sur un ticket clos lève une exception, puisqu'aucune transition ne part de Clos. Pour être complet, il faudrait aussi enregistrer le motif de réouverture, par exemple dans un historique.

Tu avais trouvé ?

Exercice 4 sur 4 Trouver les incohérences Réussi À revoir

Le diagramme de classes de Reservation montre les opérations Confirmer(), Annuler() et RendreVelo(). Son diagramme d'états-transitions montre les transitions EnAttentePaiement → Confirmee : PaiementAccepte(), Confirmee → EnCours : RetirerVelo(), EnCours → Terminee : RendreVelo() et Confirmee → Annulee : Annuler(). Relève les incohérences entre les deux diagrammes.

Voir la réponseMasquer la réponse de l'exercice 4
  • Confirmer() existe dans le diagramme de classes mais ne déclenche aucune transition. Soit elle est inutile, soit c'est elle qui devrait déclencher EnAttentePaiement → Confirmee à la place de l'événement PaiementAccepte().
  • PaiementAccepte() et RetirerVelo() déclenchent des transitions, mais n'apparaissent pas dans le diagramme de classes : il faut les y ajouter (ou dire qu'il s'agit de signaux reçus d'un autre système, pour le paiement).
  • Annuler() n'est possible que depuis Confirmée : une réservation en attente de paiement ne peut pas être annulée par le client. C'est peut-être voulu (elle s'annule toute seule au bout de 15 minutes), mais il faut le vérifier avec le client.
  • Il n'y a ni état initial ni état final dessinés : on ne sait pas dans quel état naît une réservation.

Tu avais trouvé ?

Chapitre terminé !

Fais les 4 exercices avant de passer à la suite : c'est en t'entraînant que tout se fixe.

chapitres
–
de série
–
du cours
–
Chapitre suivant : Diagrammes de déploiement, de composants, de paquetages et d'objetsRevoir le sommaire du cours