Instructions: Diagramme d'état-transition
Les diagrammes d'état-transition représentent une notation graphique formelle pour la spécification de machines d'état employées en vue de modéliser le comportement dynamique d'éléments de modèle. Ces instructions exposent cette notation et illustrent comment l'utiliser de façon efficace.
Relations
Description principale

Explication

Les machines d'état servent à modéliser le comportement dynamique d'un élément de modèle et, plus précisément, les aspects dépendant d'événements du comportement du système (voir Concept : Evénements et signaux). Les machines d'état servent surtout à définir un comportement dépendant d'un état ou variant en fonction de l'état dans lequel se trouve l'élément de modèle. Pour les éléments de modèle dont le comportement reste inchangé par rapport à leur état, les machines d'état n'ont pas besoin de décrire ce comportement (ces éléments sont généralement des classes passives dont la mission première est de gérer des données). Les machines d'état doivent notamment être employées pour modéliser le comportement de classes actives utilisant des événements d'appels et de signaux en vue d'implémenter leurs opérations (comme transitions dans la machine d'état de la classe).

Une machine d'état se compose d'états liés par des transitions. Un état désigne la condition d'un objet dans lequel il réalise certaines tâches ou attend un événement. Une transition est une relation entre deux états déclenchée par un événement, exécutant certaines actions ou évaluations et entraînant un état de fin spécifique. Les éléments d'une machine d'état sont illustrés à la figure 1.

Diagramme illustrant une notation de machine d'état.

Figure 1. Notation de machine d'état.

Un éditeur simple peut être affiché comme processeur d'état avec les états Vide, Attente d'une commande et Attente d'un texte. Les événements Charger fichier, Insérer un texte, Insérer caractère et Sauvegarder et quitter génèrent des transitions dans la machine d'état. La machine d'état pour l'éditeur est illustrée à la figure 1 ci-dessous.

Diagramme décrit dans la légende.

Figure 2. La machine d'état pour un éditeur simple.

Etats

Un état désigne la condition d'un objet dans lequel il réalise certaines tâches ou attend un événement. Un objet peut conserver un état pour une durée déterminée. Un état possède plusieurs propriétés :

Nom Chaîne de texte distinguant l'état d'autres états. Un état peut également être anonyme, à savoir qu'il n'a pas de nom.
Actions d'entrée/sorties Actions exécutées à l'entrée et à la sortie de l'état.
Transitions internes Transitions gérées sans provoquer de changement d'état.
Sous-états Structure imbriquée d'un état qui inclut des sous-états disjoints (actifs de façon séquentielle) ou simultanés (actifs de façon simultanée).
Evénements différés Liste d'événements non gérés dans cet état mais reportés et mis en file d'attente pour une gestion par l'objet dans un autre état.

Comme illustré à la figure 1, il existe deux états spéciaux pouvant être définis pour la machine d'état d'un objet. L'état initial indique le point de départ par défaut pour la machine d'état ou le sous-état. Un état initial est signalé par un rond noir. L'état final indique l'aboutissement de l'exécution de la machine d'état ou la fin de l'état intégré. Un état final est signalé par un rond noir entouré d'un cercle non rempli. Les états initial et final sont des pseudo-états. Aucun ne présente les caractéristiques habituelles d'un état normal à part le nom. Une transition d'un état initial à un état final peut posséder l'ensemble des fonctions, dont une condition de garde et une action, mais ne pas avoir d'événement déclencheur.

Transitions

Une transition est une relation entre deux états : elle indique qu'un objet dans le premier état réalise certaines actions et passe à un second état lorsqu'un événement spécifié se produit et que des conditions déterminées sont remplies. Au moment du changement d'état, le lancement de la transition est demandé. Tant que la transition n'est pas lancée, l'état de l'objet est dit source ; après le lancement en revanche, il est dit cible. Une transition possède plusieurs propriétés :

Etat source Etat affecté par la transition : si un objet se trouve dans l'état source, une transition sortante peut être lancée lorsque l'objet reçoit l'événement déclencheur de la transition et si la condition de garde (le cas échéant) est remplie.
Déclencheur d'événements Evénement rendant possible le lancement de la transition (à condition que sa condition de garde soit remplie) lorsqu'il est reçu par l'objet dans l'état source.
Condition de garde Expression booléenne évaluée lorsque la transition est déclenchée par la réception du déclencheur d'événements. Si l'expression a la valeur True, la transition peut être lancée ; si elle a la valeur False, la transition n'est pas lancée. S'il n'existe pas d'autre transition pouvant être déclenchée par le même événement, celui-ci est perdu.
Action Calcul atomique exécutable qui peut directement agir sur l'objet possédant la machine d'état et indirectement sur d'autres objets que l'objet peut voir.
Etat cible Etat actif après l'aboutissement de la transition.

Une transition peut avoir plusieurs sources, auquel cas elle représente le regroupement de divers états simultanés, ainsi que plusieurs cibles, auquel cas elle symbolise une division vers divers états simultanés.

Déclencheurs d'événements

Dans le contexte d'une machine d'état, un événement est l'occurrence d'un stimulus pouvant déclencher une transition d'état. Les événements peuvent être de signaux et d'appels, correspondre au temps qui passe ou à un changement d'état. Un signal ou un appel peuvent comporter des paramètres dont les valeurs sont disponibles pour la transition, y compris des expressions pour les conditions de garde et l'action. Une transition sans déclenchement est également possible : elle n'a dans ce cas pas de déclencheur d'événements. Ces transitions, également qualifiées de transitions d'achèvement, sont déclenchées de façon implicite quand leur état source a terminé sa tâche.

Conditions de garde

Une condition de garde est évaluée une fois que l'événement déclencheur de la transition s'est produit. Vous pouvez avoir plusieurs transitions depuis le même état source et avec le même déclencheur d'événements, tant que les conditions de garde ne se chevauchent pas. Une condition de garde est évaluée une seule fois pour la transition, au moment où l'événement a lieu. L'expression booléenne peut faire référence à l'état de l'objet.

Actions

Une action est un calcul atomique exécutable : elle ne peut donc pas être interrompue par un événement et s'exécute complètement. Elle fonctionne par conséquent à l'inverse d'une tâche, que d'autres événements peuvent interrompre. Les actions peuvent inclure des appels d'opérations (au propriétaire de la machine d'état et d'autres objets visibles), la création ou la destruction d'un autre objet, ou encore l'envoi d'un signal à un autre objet. Dans ce dernier cas, le nom du signal est précédé du mot clé send.

Actions d'entrée/sorties

Les actions d'entrée et les sorties permettent à la même action d'être distribuée chaque fois que l'état est activé ou quitté, respectivement. Grâce à ces actions, l'opération est effectuée proprement, sans devoir explicitement appliquer des actions sur chaque transition entrante ou sortante. Ces actions peuvent ne pas avoir d'arguments ou de conditions de garde. Les actions d'entrée au niveau supérieur d'une machine d'état pour un élément de modèle peuvent posséder des paramètres représentant les arguments que la machine reçoit à la création de l'élément.

Transitions internes

Les transitions internes permettent aux événements d'être gérés dans l'état sans quitter ce dernier, ce qui évite le déclenchement d'actions d'entrée ou les sorties. Les transitions internes peuvent posséder des éléments avec des paramètres et des conditions de garde et correspondre principalement à des gestionnaires d'interruption.

Evénements différés

La gestion des événements différés est reportée tant qu'un état dans lequel un événement n'est pas différé n'est pas actif. Une fois cet état actif, l'occurrence de l'événement est déclenchée et peut provoquer des transitions comme s'il venait de se produire. L'implémentation d'événements différés demande la présence d'une file d'attente interne d'événements. Si un événement se produit mais est répertorié comme différé, il est placé dans la file d'attente. Les événements sont retirés de cette file d'attente dès que l'objet passe à un état ne les mettant pas en différé.

Sous-états

Un état simple n'a aucune sous-structure. Un état doté de sous-états (états imbriqués) est dit composite. Les sous-états peuvent être imbriqués à n'importe quel niveau. Une machine d'état imbriquée doit avoir au moins un état initial et un état final. Les sous-états servent à simplifier les machines d'état complexes en montrant que certains états sont uniquement possibles dans un contexte donné (état parent).

Diagramme montrant des sous-états.

Figure 3. Sous-états.

A partir d'une source hors d'un état composite parent, une transition peut cibler l'état composite ou un sous-état. Si la cible est l'état composite, la machine d'état imbriquée doit inclure un état initial auquel passe le contrôle une fois l'état composite actif et après distribution de son action d'entrée (le cas échéant). Si la cible est l'état imbriqué en revanche, le contrôle passe à lui après distribution de l'action d'entrée de l'état composite (le cas échéant), puis de l'action d'entrée de l'état imbriqué (le cas échéant).

Une transition issue d'un état composite peut avoir comme source l'état composite ou le sous-état. Dans les deux cas, le contrôle quitte d'abord l'état imbriqué (et sa sortie, le cas échéant, est distribuée), puis l'état composite (dont la sortie, le cas échéant, est également distribuée). Une transition dont la source est l'état composite interrompt principalement la tâche de la machine d'état imbriquée.

Etats historiques

Sauf mention contraire, lorsqu'une transition passe à un état composite, l'action de l'état imbriqué revient à l'état initial (excepté si la transition prend directement comme cible un sous-état). Les états historiques permettent à la machine d'état de passer à nouveau au dernier sous-état actif avant de quitter l'état composite. La figure 3 offre un exemple d'utilisation d'un état historique.

Diagramme montrant des états historiques.

Figure 4. Etat historique.

Techniques communes de modélisation

Les machines d'état servent le plus souvent à modéliser le comportement d'un objet au cours de son cycle de vie. Elles s'avèrent notamment nécessaires lorsque des objets ont un comportement dépendant de leur état. Les objets pouvant posséder des machines d'état sont des classes, des sous-systèmes, des cas d'utilisation et des interfaces (pour affirmer les états devant être respectés par un objet qui génère l'interface). Dans le cas de systèmes en temps réel, les machines d'état sont également employées pour des capsules et des protocoles (pour affirmer les états devant être respectés par un objet à l'origine du protocole).

Tous les objets ne requièrent pas des machines d'état. Si le comportement d'un objet est simple, en ce sens qu'il ne fait que stocker et recevoir des données, il est invariable quel que soit l'état et sa machine d'état n'a guère d'intérêt.

La modélisation de la durée de vie d'un objet implique trois éléments : l'indication des événements auxquels l'objet peut répondre, la réponse à ces événements et l'impact du passé sur le comportement actuel. Cette modélisation demande aussi de décider l'ordre dans lequel l'objet peut répondre de façon intelligible aux événements, en partant de l'heure de création de l'objet et en continuant jusqu'à sa destruction.

Pour modéliser la durée de vie d'un objet :

  • Définissez le contexte pour la machine d'état, qu'il s'agisse d'une classe, d'un cas d'utilisation ou du système dans son ensemble.
    • Si le contexte est une classe ou un cas d'utilisation, rassemblez les classes voisines, y compris les classes parent et celles accessibles par des associations ou des dépendances. Ces classes voisines sont des cibles candidates pour des actions et pour intégration à des conditions de garde.
    • Si le contexte est le système dans son ensemble, limitez-vous à un comportement du système et prenez en compte la durée de vie des objets impliqués dans cet aspect. La durée de vie du système entier est trop importante pour être un contexte pertinent.
  • Etablissez les états initial et final pour l'objet. S'il existe des préconditions ou des postconditions pour ces états, définissez-les également.
  • Déterminez les événements auxquels l'objet répond. Vous les trouverez dans les interfaces de l'objet. Dans le cas de systèmes en temps réel, ils peuvent aussi figurer dans les protocoles de l'objet.
  • De l'état initial à l'état final, indiquez les états de niveau supérieur dans lesquels l'objet peut se trouver. Connectez ces états aux transitions déclenchées par les événements appropriés. Poursuivez en ajoutant ces transitions.
  • Identifiez toutes les actions d'entrée ou les sorties.
  • Développez ou simplifiez la machine d'état à l'aide de sous-états.
  • Vérifiez que tous les événements déclenchant les transitions dans la machine d'état correspondent à ceux attendus par les interfaces générées par l'objet. De la même façon, assurez-vous que tous les événements attendus par les interfaces de l'objet sont gérés par la machine d'état. Dans le cas de systèmes en temps réel, effectuez des vérifications similaires pour les protocoles d'une capsule. Enfin, veillez aux endroits où vous voulez explicitement ignorer des événements (par exemple, des événements différés).
  • Vérifiez que toutes les actions dans la machine d'état sont prises en charge par les relations, méthodes et opérations de l'objet parent.
  • Faites un suivi de la machine d'état en la comparant aux séquences d'événements attendues et leurs réponses. Recherchez les états inaccessibles et ceux dans lesquels la machine reste bloquée.
  • Si vous réorganisez ou restructurez la machine d'état, assurez-vous que la sémantique reste inchangée.

Conseils et astuces

  • Lorsque le choix se présente, utilisez la sémantique visuelle de la machine d'état au lieu d'écrire le code de détail de transition. Par exemple, ne déclenchez pas une transition sur plusieurs signaux et servez-vous du code de détail pour gérer le flux de contrôle différemment en fonction du signal. Utilisez des transitions distinctes déclenchées par des signaux différents. Evitez dans le code de transition la logique conditionnelle masquant les comportements supplémentaires.
  • Nommez les états en fonction de vos attentes ou de ce qui se produit dans chacun d'eux. Pour rappel, un état n'est pas un point dans le temps, mais une période au cours de laquelle la machine d'état attend que quelque chose se passe. Par exemple, attenteFin est un nom plus pertinent que fin et retarderTâche plus que délai. Ne nommez pas les états comme s'il s'agissait d'actions.
  • Nommez tous les états et les transitions dans une seule machine d'état ; le débogage au niveau de la source est ainsi simplifié.
  • Utilisez avec prudence des variables d'état (attributs servant à contrôler le comportement) et n'y recourez pas au lieu de créer de nouveaux états. S'il y a peu d'états, que les comportements dépendent peu ou pas d'états et que peu de comportements ou aucun sont simultanés ou indépendants de l'objet contenant la machine d'état, vous pouvez employer des variables d'état. En revanche, si un comportement complexe et dépendant d'états est potentiellement simultané ou si des événements devant être gérés peuvent se produire en dehors de l'objet contenant la machine d'état, envisagez la collaboration de deux objets actifs ou plus (éventuellement définie comme composition). Dans des systèmes en temps réel, un comportement complexe, dépendant d'états et simultané doit être modélisé avec une capsule renfermant des sous-capsules.
  • S'il existe plus de 5 ± 2 états dans un même diagramme, pensez à utiliser des sous-états. Tout est question de bon sens : dix états dans un pattern totalement standard peuvent être acceptables, mais deux états avec 40 transitions entre eux doivent évidemment être repensés. Assurez-vous que la machine d'état est compréhensible.
  • Nommez les transitions pour ce qui déclenche l'événement et/ou ce qui se produit pendant la transition. Choisissez des noms facilitant la compréhension.
  • Dans le cas d'un vertex de choix, demandez-vous si vous pouvez déléguer la responsabilité pour ce choix à un autre composant, afin qu'il soit présenté à l'objet en tant qu'ensemble distinct de signaux sur lesquels agir (par exemple, au lieu d'un choix dans msg->data > x) ou si l'émetteur ou tout autre intermédiaire prend la décision et envoie un signal avec la décision explicite dans le nom du signal (par exemple, utilisez les signaux estPlein et estVide au lieu de créer une valeur et de vérifier les données du message).
  • Formulez de façon descriptive la question à laquelle une réponse est donnée dans le vertex de choix, comme yAtIlEncoreDeLaVie ou estIlPossibleDeSePlaindre.
  • Dans un objet donné, essayez de prendre des noms uniques de vertex de choix, pour les mêmes raisons que les noms de transitions doivent aussi être uniques.
  • Y a-t-il des fragments de code ou des transitions excessivement longs ? Des fonctions doivent-elles être employées à la place et les fragments de code courants sont-ils capturés en tant que fonctions ? Une transition doit ressembler à du pseudocode de niveau élevé et respecter des règles de longueur identiques ou encore plus strictes que les fonctions C++. Par exemple, une transition avec plus de 25 lignes de code est considérée trop longue.
  • Les fonctions doivent être nommées par rapport à leur action.
  • Prêtez une attention spéciale aux actions d'entrée et les sorties : il est particulièrement facile d'effectuer des changements et d'oublier de changer ces actions en conséquence.
  • Les sorties peuvent servir à fournir des fonctions de sécurité, par exemple si elles visent à appliquer une assertion : tel est le cas de la sortie de l'état allumerChauffage, qui éteint le chauffage.
  • En général, les sous-états doivent contenir deux états ou plus, sauf si la machine d'état est abstraite et sera assortie de sous-classes de l'élément parent.
  • Les points de choix doivent être utilisés à la place de la logique conditionnelle dans les actions et les transitions. Ces points sont facilement reconnaissables, alors que la logique conditionnelle au sein du code est masquée et peut facilement vous échapper.
  • Evitez les conditions de garde.
    • Si l'événement déclenche plusieurs transitions, aucun contrôle ne détermine la condition de garde évaluée en premier. Par conséquent, les résultats peuvent être imprévisibles.
    • Plusieurs conditions de garde peuvent avoir la valeur True, mais une seule transition peut en revanche être suivie. Le chemin choisi peut être imprévisible.
    • Les conditions de garde n'étant pas visuelles, il est difficile d'en détecter la présence.
  • Evitez les machines d'état ressemblant à des graphiques de flux.
    • Ceci peut signaler une tentative de modélisation d'une abstraction qui n'existe pas vraiment, comme suit :
      • utilisation d'une classe active pour modéliser le comportement le plus adapté à une classe passive ou de données ;
      • modélisation d'une classe de données à l'aide d'une classe de données et d'une classe active étroitement liées (la classe de données a été utilisée pour transmettre les informations de type mais la classe active contient la plupart des données à associer à la classe de données).
    • Cette mauvaise utilisation des machines d'état se détecte grâce aux symptômes suivants :
      • des messages envoyés à soi-même, essentiellement pour réutiliser du code,
      • quelques états avec beaucoup de points de choix,
      • dans certains cas, une machine d'état sans cycles. Ce type de machine est valide pour traiter des applications de contrôle ou pour contrôler une séquence d'événements. Leur présence lors de l'analyse symbolise généralement la dégénération de la machine d'état dans un graphique de flux.
    • Une fois l'incident identifié :
      • envisagez la division de la classe active en unités plus petites avec des responsabilités plus distinctes ;
      • déplacez plus de comportements dans une classe de données associée à la classe active de l'incident ;
      • déplacez plus de comportements dans les fonctions de la classe active ;
      • établissez des signaux plus pertinents au lieu de vous fier aux données.

Conception avec des machines d'états abstraites

Une machine d'état abstraite doit recevoir plus de détails avant de pouvoir servir à des fins pratiques. Elle peut être employée pour définir un comportement réutilisable générique, ensuite amélioré dans des éléments de modèle.

Diagramme décrit dans la légende.

Figure 5. Une machine d'état abstraite.

Observez la machine d'état abstraite à la figure 5. La machine d'état simple illustrée est représentative du niveau le plus abstrait du comportement (l'automate de contrôle) de différents types d'éléments dans des systèmes gérés par des événements. Même s'ils partagent tous cette forme de niveau élevé, les divers types d'éléments peuvent présenter des comportements détaillés complètement différents dans l'état En fonctionnement selon leur objectif. Par conséquent, cette machine d'état serait probablement définie dans une classe abstraite servant de classe racine pour les différentes classes actives spécialisées.

Définissons par conséquent deux améliorations distinctes de cette machine d'état abstraite à l'aide de l'héritage. Ces deux améliorations (R1 et R2) sont illustrées à la figure 6. Par souci de clarté, nous avons dessiné en gris les éléments hérités de la classe parent.

Diagramme décrit dans la légende.

Figure 6. Deux améliorations de la machine d'état à la figure 5.

Les deux améliorations se distinguent clairement par leur mode de composition de l'état En fonctionnement et d'extension de la transition de départ d'origine. Il va de soi que ces choix peuvent uniquement être effectués une fois l'amélioration connue : ils sont donc impossibles avec une seule transition de bout en bout dans la classe abstraite.

Etats de chaîne

La possibilité de poursuivre tant les transitions entrantes que celles sortantes est essentielle pour le type d'amélioration décrit plus haut. Il peut sembler que les points d'entrée et les états finaux, associés aux transitions de continuation, suffisent pour fournir cette sémantique. Malheureusement, ils sont insuffisants dans le cas de plusieurs transitions distinctes devant être étendues.

Pour le pattern de comportement abstrait est requise une méthode de chaînage de deux segments de transition ou plus, tous exécutés dans la portée d'une même étape d'achèvement. Les transitions passant à un état hiérarchique sont alors divisées entre la partie entrante, se terminant en effet à la frontière de l'état, et une extension se poursuivant dans l'état. De la même façon, les transitions sortantes émanant d'un état imbriqué de façon hiérarchique sont segmentées entre une partie se terminant à la frontière de l'état parent et une autre se poursuivant de cette frontière à l'état cible. Cet effet peut être obtenu en langage UML avec l'introduction du concept d'état de chaîne. La modélisation s'effectue par un stéréotype (<<chainState>>) du concept d'état UML. Il s'agit d'un état dont le seul objectif est de "chaîner" des transitions automatiques (sans déclenchement) à une transition entrante. Un état de chaîne ne possède aucune structure interne, aucune action d'entrée, aucune tâche interne et aucune sortie. Il est également dénué de transitions déclenchées par des événements. Cet état peut inclure n'importe quel nombre de transitions. Il peut avoir une transition sortante sans événement déclencheur : cette transition se lance automatiquement lorsqu'une transition entrante active l'état. L'objectif de l'état est de chaîner une transition entrante à une transition sortante distincte. Entre la ou les transition(s) entrantes et la transition sortante chaînée, l'une se connecte à un autre état dans l'état parent et l'autre se connecte à un autre état hors de l'état parent. L'idée de l'introduction d'un état de chaîne consiste à séparer la spécification interne de l'état parent de son environnement externe : il s'agit donc d'un concept d'encapsulation.

En réalité, un état de chaîne correspond à un état de passage permettant de chaîner une transition à une transition de continuation spécifique. Si aucune transition de continuation n'est définie, la transition se termine dans l'état de chaîne et une transition dans un état parent finit par se lancer pour faire avancer les choses.

L'exemple de segment de machine d'état à la figure 7 illustre des états de chaîne et leur notation. Les états de chaîne sont représentés dans un diagramme de machine d'état par de petits cercles blancs dans l'état hiérarchique approprié (cette notation est similaire aux états initiaux et finaux). Les cercles sont des icônes du stéréotype de l'état de chaîne et sont généralement dessinés près de la frontière pour plus de commodité. En fait, une variation de notation consisterait à les dessiner sur la frontière de l'état parent.

Diagramme décrit dans le texte d'accompagnement.

Figure 7. Etats de chaîne et transitions chaînées.

La transition chaînée dans cet exemple comprend trois segments de transition chaînée e1/a11-/a12-/a13. A la réception du signal e1, la transition intitulée e1/a11 est prise et son action a11 exécutée, puis l'état de chaîne c1 est atteint. Après cela, la transition de continuation entre c1 et c2 est prise puis, comme c2 est aussi un état de chaîne, la transition de c2 à S21. Si tous les états le long de ces chemins ont des actions d'entrée et les sorties, la séquence d'exécutions d'actions est la suivante :

  • sortie de S11
  • action a11
  • sortie de S1
  • action a12
  • action d'entrée de S2
  • action a13
  • action d'entrée de S21

Tout ceci est exécuté dans la portée d'une seule étape d'achèvement.

Il faut le comparer à la sémantique d'exécution d'actions de la transition e2/a2 directe, à savoir :

  • sortie de S11
  • sortie de S1
  • action a2
  • action d'entrée pour l'état S2
  • action d'entrée pour l'état S21