paint-brush
Les machines d'état peuvent vous aider à résoudre des problèmes de programmation complexespar@pragativerma
8,873 lectures
8,873 lectures

Les machines d'état peuvent vous aider à résoudre des problèmes de programmation complexes

par Pragati Verma18m2022/09/26
Read on Terminal Reader
Read this story w/o Javascript

Trop long; Pour lire

« État » est un terme de programmation courant qui est utilisé par tous les développeurs à mesure qu'ils progressent du niveau de programmation débutant au niveau intermédiaire. En informatique, l'état d'un programme est défini comme sa position par rapport aux entrées précédemment stockées. Une variable de contrôle, comme celle utilisée dans une boucle par exemple, change l'état du programme à chaque itération. L'examen de l'état actuel d'un programme peut être utilisé pour tester ou analyser la base de code. L'ajout d'un autre état est difficile car il nécessite de réécrire le code de nombreuses classes différentes.

Companies Mentioned

Mention Thumbnail
Mention Thumbnail

Coin Mentioned

Mention Thumbnail
featured image - Les machines d'état peuvent vous aider à résoudre des problèmes de programmation complexes
Pragati Verma HackerNoon profile picture
0-item


« État » est un terme de programmation courant utilisé par tous les développeurs à mesure qu'ils progressent du niveau de programmation débutant au niveau intermédiaire. Alors, que signifie exactement le terme "État" ?

En général, l'état d'un objet est simplement l'instantané actuel de l'objet ou d'une partie de celui-ci. Pendant ce temps, en informatique, l'état d'un programme est défini comme sa position par rapport aux entrées précédemment stockées. Dans ce contexte, le terme "état" est utilisé de la même manière qu'en science : l'état d'un objet, tel qu'un gaz, un liquide ou un solide, représente sa nature physique actuelle, et l'état d'un programme informatique reflète ses valeurs ou son contenu actuels.

Les entrées stockées sont conservées sous forme de variables ou de constantes dans un programme informatique. Lors de l'évaluation de l'état d'un programme, les développeurs peuvent examiner les valeurs contenues dans ces entrées. L'état du programme peut changer pendant son exécution - les variables peuvent changer et les valeurs de la mémoire peuvent changer. Une variable de contrôle, comme celle utilisée dans une boucle par exemple, change l'état du programme à chaque itération. L'examen de l'état actuel d'un programme peut être utilisé pour tester ou analyser la base de code.


Dans les systèmes plus simples, la gestion des états est fréquemment gérée avec des instructions if-else, if-then-else, try-catch ou des drapeaux booléens ; cependant, cela est inutile lorsqu'il y a trop d'états imaginables dans un programme. Ils peuvent conduire à un code maladroit et compliqué qui est difficile à comprendre, à maintenir et à déboguer.


Un inconvénient des clauses if-else ou des booléens est qu'ils peuvent devenir assez étendus, et l'ajout d'un autre état est difficile car cela nécessite de réécrire le code de nombreuses classes différentes. Supposons que vous souhaitiez créer un jeu comportant le menu principal, une boucle de jeu et un écran de fin.


Construisons un lecteur vidéo par exemple :


 class Video: def __init__(self, source): self.source = source self.is_playing = False self.is_paused = False self.is_stopped = True # A video can only be played when paused or stopped def play(self): if not self.is_playing or self.is_paused: # Make the call to play the video self.is_playing = True self.is_paused = False else: raise Exception( 'Cannot play a video that is already playing.' ) # A video can only be paused when it is playing def pause(self): if self.is_playing: # Make the call to pause the video self.is_playing = False self.is_paused = True else: raise Exception( 'Cannot pause a video that is not playing' ) # A video can only be stopped when it is playing or paused def stop(self): if self.is_playing or self.is_paused: # Make the call to stop the video self.is_playing = False self.is_paused = False else: raise Exception( 'Cannot stop a video that is not playing or paused' )


L'extrait de code ci-dessus est une implémentation if-else d'une simple application de lecteur vidéo, où les trois états de base sont - lecture, pause et arrêt. Cependant, si nous essayons d'ajouter plus d'états, le code deviendra rapidement complexe, gonflé, répétitif et difficile à comprendre et à tester. Voyons à quoi ressemble le code lors de l'ajout d'un autre état 'rewind' :


 class Video: def __init__(self, source): self.source = source self.is_playing = False self.is_paused = False self.is_rewinding = False self.is_stopped = True # A video can only be played when it is paused or stopped or rewinding def play(self): if self.is_paused or self.is_stopped or self.is_rewinding: # Make the call to play the video self.is_playing = True self.is_paused = False self.is_stopped = False self.is_rewinding = False else: raise Exception( 'Cannot play a video that is already playing.' ) # A video can only be paused when it is playing or rewinding def pause(self): if self.is_playing or self.is_rewinding: # Make the call to pause the video self.is_playing = False self.is_paused = True self.is_rewinding = False self.is_stopped = False else: raise Exception( 'Cannot pause a video that is not playing or rewinding' ) # A video can only be stopped when it is playing or paused or rewinding def stop(self): if self.is_playing or self.is_paused or self.is_rewinding: # Make the call to stop the video self.is_playing = False self.is_paused = False self.is_stopped = True self.is_rewinding = False else: raise Exception( 'Cannot stop a video that is not playing or paused or rewinding' ) # 4. A video can only be rewinded when it is playing or paused. def rewind(self): if self.is_playing or self.is_paused: # Make the call to rewind the video self.is_playing = False self.is_paused = False self.is_stopped = False self.is_rewinding = True else: raise Exception( 'Cannot rewind a video that is not playing or paused' )


Sans le modèle State, vous devriez examiner l'état actuel du programme dans tout le code, y compris les méthodes de mise à jour et de dessin. Si vous souhaitez ajouter un quatrième état, tel qu'un écran de paramètres, vous devrez mettre à jour le code de nombreuses classes distinctes, ce qui n'est pas pratique. C'est là que l'idée des machines à états devient utile.


Qu'est-ce qu'une machine d'état ?

Les machines à états ne sont pas un concept nouveau en informatique ; ils constituent l'un des modèles de conception de base utilisés dans le secteur des logiciels. Il est plus orienté système que orienté codage et est utilisé pour modéliser des cas d'utilisation.

Regardons un exemple concret simple de location d'un taxi via Uber :

  1. Lorsque vous lancez le programme pour la première fois, il vous amène à l'écran d'accueil, où vous tapez votre destination dans la zone de recherche.
  2. Une fois le bon emplacement identifié, Uber affiche les options de voyage recommandées telles que Pool, Premier, UberGo, Uber XL et autres, ainsi qu'une estimation de prix.
  3. Le trajet est confirmé et un chauffeur est attribué une fois que vous avez sélectionné l'option de paiement et appuyé sur le bouton "Confirmer" avec le temps de trajet spécifié si nécessaire.
  4. Uber affiche désormais une carte sur laquelle vous pouvez localiser votre chauffeur.


L'écran 1 est le premier écran que tous les utilisateurs de ce cas d'utilisation voient, et il est autonome. L'écran 2 dépend de l'écran 1, et vous ne pourrez pas accéder à l'écran 2 tant que vous n'aurez pas fourni de données précises sur l'écran 1. De même, l'écran 3 dépend de l'écran 2, tandis que l'écran 4 dépend de l'écran 3. Si vous ne ni que votre chauffeur n'annule votre trajet, vous serez redirigé vers l'écran 4, où vous ne pourrez pas planifier un autre trajet tant que celui en cours n'est pas terminé.


Disons qu'il pleut beaucoup et qu'aucun chauffeur n'accepte votre trajet ou qu'aucun chauffeur disponible dans votre région n'est trouvé pour terminer votre trajet ; une notification d'erreur vous avertissant de l'indisponibilité du pilote s'affiche et vous restez sur l'écran 3. Vous pouvez toujours revenir à l'écran 2, à l'écran 1 et même au tout premier écran.

Vous vous trouvez à une étape différente du processus de réservation de taxi et vous ne pouvez passer au niveau suivant que si une action spécifiée à l'étape actuelle réussit. Par exemple, si vous entrez le mauvais emplacement sur l'écran 1, vous ne pourrez pas passer à l'écran 2 et vous ne pourrez pas passer à l'écran 3 à moins que vous ne choisissiez une option de voyage sur l'écran 2, mais vous pouvez revenez toujours à l'étape précédente sauf si votre voyage est déjà réservé.


Dans l'exemple ci-dessus, nous avons divisé le processus de réservation de taxi en plusieurs activités, chacune pouvant ou non être autorisée à appeler une autre activité en fonction du statut de la réservation. Une machine d'état est utilisée pour modéliser cela. En principe, chacune de ces étapes/états devrait être autonome, l'une n'invoquant la suivante qu'une fois l'étape en cours terminée, avec succès ou non.


En termes plus techniques, la machine d'état nous permet de décomposer une grande action compliquée en une succession d'activités séparées plus petites, comme l'activité de réservation de taxi dans l'exemple précédent.


Les événements connectent des tâches plus petites et le passage d'un état à un autre est appelé transition. Nous effectuons normalement certaines actions après le passage d'un état à un autre, telles que la création d'une réservation dans le backend, l'émission d'une facture, l'enregistrement des données d'analyse des utilisateurs, la capture des données de réservation dans une base de données, le déclenchement du paiement une fois le voyage terminé, etc. .


Par conséquent, la formule générale d'une machine à états peut être donnée par :


État actuel + une action / un événement = un autre état


Voyons à quoi ressemblerait une machine d'état conçue pour une simple application de lecteur vidéo :





Et nous pouvons l'implémenter dans le code en utilisant des transitions comme suit :


 from transitions import Machine class Video: # Define the states PLAYING = 'playing' PAUSED = 'paused' STOPPED = 'stopped' def __init__(self, source): self.source = source # Define the transitions transitions = [ # 1. A video can only be played when it is paused or stopped. {'trigger': 'play', 'source': self.PAUSED, 'dest': self.PLAYING}, {'trigger': 'play', 'source': self.STOPPED, 'dest': self.PLAYING}, # 2. A video can only be paused when it is playing. {'trigger': 'pause', 'source': self.PLAYING, 'dest': self.PAUSED}, # 3. A video can only be stopped when it is playing or paused. {'trigger': 'stop', 'source': self.PLAYING, 'dest': self.STOPPED}, {'trigger': 'stop', 'source': self.PAUSED, 'dest': self.STOPPED}, ] # Create the state machine self.machine = Machine{ model = self, transitions = transitions, initial = self.STOPPED } def play(self): pass def pause(self): pass def stop(self): pass


Maintenant, au cas où nous voudrions ajouter un autre état, disons rembobiner, nous pouvons le faire facilement comme suit :





 from transitions import Machine class Video: # Define the states PLAYING = 'playing' PAUSED = 'paused' STOPPED = 'stopped' REWINDING = 'rewinding' # new def __init__(self, source): self.source = source # Define the transitions transitions = [ # 1. A video can only be played when it is paused or stopped. {'trigger': 'play', 'source': self.PAUSED, 'dest': self.PLAYING}, {'trigger': 'play', 'source': self.STOPPED, 'dest': self.PLAYING}, {'trigger': 'play', 'source': self.REWINDING, 'dest': self.PLAYING}, # new # 2. A video can only be paused when it is playing. {'trigger': 'pause', 'source': self.PLAYING, 'dest': self.PAUSED}, {'trigger': 'pause', 'source': self.REWINDING, 'dest': self.PAUSED}, # new # 3. A video can only be stopped when it is playing or paused. {'trigger': 'stop', 'source': self.PLAYING, 'dest': self.STOPPED}, {'trigger': 'stop', 'source': self.PAUSED, 'dest': self.STOPPED}, {'trigger': 'stop', 'source': self.REWINDING, 'dest': self.STOPPED}, # new # 4. A video can only be rewinded when it is playing or paused. {'trigger': 'rewind', 'source': self.PLAYING, 'dest': self.REWINDING}, #new {'trigger': 'rewind', 'source': self.PAUSED, 'dest': self.REWINDING}, # new ] # Create the state machine self.machine = Machine{ model = self, transitions = transitions, initial = self.STOPPED } def play(self): pass def pause(self): pass def stop(self): pass def rewind(self): pass


Ainsi, nous pouvons voir comment les machines à états peuvent simplifier une implémentation complexe et nous éviter d'écrire du code incorrect. Après avoir appris les capacités des machines d'état, il est maintenant important de comprendre pourquoi et quand utiliser des machines d'état.


Pourquoi et quand utiliser les State Machines ?

Les machines d'état peuvent être utilisées dans des applications qui ont des états distincts. Chaque étape peut conduire à un ou plusieurs états ultérieurs, ainsi qu'à la fin du flux de processus. Une machine d'état utilise une entrée utilisateur ou des calculs dans l'état pour choisir l'état à entrer ensuite.


De nombreuses applications nécessitent une étape "d'initialisation", suivie d'un état par défaut qui permet un large éventail d'actions. Les entrées précédentes et présentes, ainsi que les états, peuvent tous avoir un impact sur les actions qui sont exécutées. Des mesures de nettoyage peuvent alors être effectuées lorsque le système est "arrêté".


Une machine à états peut nous aider à conceptualiser et à gérer ces unités de manière plus abstraite si nous pouvons décomposer une tâche extrêmement complexe en unités plus petites et indépendantes, où nous devons simplement décrire quand un état peut passer à un autre état et ce qui se passe lorsque la transition se produit. Nous n'avons pas besoin de nous préoccuper de la façon dont la transition se produit après la configuration. Après cela, nous n'avons qu'à penser quand et quoi, pas comment.

De plus, les machines d'état nous permettent de voir l'ensemble du processus d'état d'une manière très prévisible ; une fois les transitions définies, nous n'avons pas à nous soucier d'une mauvaise gestion ou de transitions d'état erronées ; la transition incorrecte ne peut se produire que si la machine d'état est configurée correctement. Nous avons une vue complète de tous les états et transitions d'une machine d'état.


Si nous n'utilisons pas de machine à états, nous sommes soit incapables de visualiser nos systèmes dans divers états possibles, soit nous associons sciemment ou inconsciemment nos composants étroitement ensemble, soit nous écrivons de nombreuses conditions if-else pour simuler des transitions d'état, ce qui complique les tests unitaires et d'intégration car il faut s'assurer que tous les cas de test sont écrits pour valider la possibilité de toutes les conditions et branchements utilisés.


Avantages des machines d'état

Les machines à états, en plus de leur capacité à développer des algorithmes décisionnels, sont des formes fonctionnelles de planification d'applications. À mesure que les applications deviennent plus complexes, le besoin d'une conception efficace augmente.


Les diagrammes d'état et les organigrammes sont utiles et parfois essentiels tout au long du processus de conception. Les machines d'état sont importantes non seulement pour la planification des applications, mais elles sont également simples à créer.


Voici quelques-uns des principaux avantages des machines d'état dans l'informatique moderne :

  • Il vous aide à éliminer les conditions de codage difficiles. En votre nom, la machine d'état résume toute la logique liée aux états et aux transitions.
  • Les machines d'état ont souvent un nombre fini d'états avec des transitions définies, ce qui permet d'identifier facilement quelle transition/donnée/événement a déclenché l'état actuel d'une requête.
  • Après avoir établi une machine d'état, les développeurs peuvent se concentrer sur la création d'actions et de conditions préalables. Avec une validation et un préconditionnement suffisants, les machines d'état limitent les opérations dans le désordre. Comme dans l'exemple d'Uber, un chauffeur ne peut être récompensé tant que le trajet n'est pas terminé.
  • Les machines à états peuvent être assez faciles à entretenir. Les actions entreprises lors de chaque transition sont logiquement indépendantes les unes des autres. En conséquence, le code correspondant peut être isolé.
  • Les machines à états sont moins susceptibles de changer et sont plus stables. Il devient beaucoup plus facile de maintenir de tels systèmes si les cas d'utilisation actuels et futurs sont très évidents.


Inconvénients des machines d'état

Tout n'est pas bon dans les machines à états, elles peuvent parfois entraîner des inconvénients et des défis. Voici quelques-uns des problèmes courants avec les machines d'état :

  • Les machines à états sont généralement synchrones. Ainsi, si vous avez besoin d'appels d'API/d'exécution de tâches asynchrones en arrière-plan, vous devrez peser soigneusement le pour et le contre avant de choisir la meilleure option.
  • Le code peut rapidement se confondre. Étant donné que les machines d'état sont pilotées par les données, votre équipe produit peut vous demander d'exécuter différentes transitions à partir du même état en fonction de différents paramètres de données/d'entrée. En conséquence, ce type de demande peut entraîner plusieurs transitions avec une vérification des conditions préalables maladroite. Elle dépend entièrement du produit et de la configuration actuelle de la machine.
  • Si vous avez besoin d'équilibrer la charge des instances de machine d'état, choisissez celle dont la persistance est activée ; sinon, vous devrez implémenter votre couche de persistance et les validations nécessaires pour vous assurer que plusieurs requêtes lancées sur des instances de machine d'état distinctes produisent des résultats cohérents.
  • Étant donné qu'il existe peu de ressources ou de communautés dédiées aux implémentations de machines d'état distinctes, l'assistance peut être limitée une fois que vous avez choisi une bibliothèque.


Éléments à garder à l'esprit lors de l'utilisation des machines d'état

Lorsque vous utilisez une machine d'état, votre système devrait idéalement avoir deux composants logiques :

  1. la machine d'état/le système de flux de travail lui-même
  2. la logique métier contenue dans un ou plusieurs services.


La machine d'état peut être considérée comme l'infrastructure qui pilote les transitions d'état ; il vérifie les transitions d'état et exécute les actions configurées avant, pendant et après une transition ; cependant, il ne doit pas savoir quelle logique métier est effectuée dans ces actions.


Donc, en général, c'est une bonne idée d'isoler la machine d'état de la logique métier de base en utilisant des abstractions correctes ; sinon, gérer le code serait un cauchemar.


Voici quelques autres scénarios réels dans lesquels nous devons utiliser la logique de la machine d'état avec prudence :

  • Une machine à états est bien plus que des états, des transitions et des actions. Il devrait également être capable de définir une frontière autour d'un changement d'état. Une transition ne peut réussir que dans des cas particuliers si elle est déclenchée par un système ou un utilisateur digne de confiance. Il pourrait y avoir une variété de situations similaires. En conséquence, nous devrions être en mesure de développer une logique de protection de transition d'état appropriée.
  • Nous nous retrouvons généralement avec de nombreux processus pour la même entité commerciale qui peuvent s'exécuter simultanément. Dans de tels cas, un processus n'entrave pas les autres workflows ; ils peuvent ou non être déclenchés simultanément, mais ils peuvent coexister ; le deuxième flux de travail peut commencer à partir de l'une des phases éligibles du premier flux de travail, après quoi il peut bifurquer et fonctionner de manière indépendante. Ce type de cas d'utilisation est établi par l'entreprise ; toutes les organisations ne l'auront pas.
  • En principe, les systèmes de workflow sont indépendants du domaine métier. Par conséquent, dans le même système de workflow, de nombreux processus sans lien avec la même organisation commerciale peuvent être établis. Ils peuvent avoir un point de départ partagé ou distinct, selon que le système de workflow autorise plusieurs points de départ.
  • Lorsque de nombreux flux de travail distincts sont formés dans le même système de flux de travail, vous obtenez une image globale de tous les processus métier exécutés dans diverses entités commerciales de votre système. Selon les cas d'utilisation métier, différents processus peuvent également avoir certaines étapes identiques.


Cas d'utilisation pratiques ou réels pour les machines d'état :

Voici quelques-unes des applications pratiques qui bénéficient du concept de machines à états dans notre vie quotidienne :

  • Boîtes de dialogue à page unique ou à onglets Un onglet dans la boîte de conversation représente chaque état. Un utilisateur peut initier une transition d'état en sélectionnant un certain onglet. L'état de chaque onglet inclut toutes les actions que l'utilisateur peut effectuer.
  • Un guichet automatique bancaire (GAB). Dans cette application, des états tels que l'attente de l'entrée de l'utilisateur, la confirmation du montant nécessaire par rapport au solde du compte, la distribution de l'argent, l'impression du reçu, etc. sont tous concevables.
  • Un logiciel qui prend une seule mesure, la stocke en mémoire, puis attend que l'utilisateur effectue une autre action. Les étapes de ce programme incluent l'attente de l'entrée de l'utilisateur, l'exécution de la mesure, l'enregistrement des données, l'affichage des résultats, etc. Configuration des tâches ETL, par exemple.
  • Les machines d'état sont couramment utilisées pour concevoir des interfaces utilisateur. Lors de la conception d'une interface utilisateur, des actions utilisateur distinctes déplacent l'interface utilisateur en segments de traitement séparés. Chacun de ces éléments fonctionnera comme un état dans la State Machine. Ces segments peuvent soit conduire à un autre segment pour un traitement ultérieur, soit attendre un autre événement utilisateur. Dans ce scénario, la machine d'état surveille l'utilisateur pour déterminer quelle action il doit effectuer ensuite.


Lorsque vous achetez quelque chose sur un site de commerce électronique en ligne, par exemple, il passe par différentes phases, telles que Commandé, Emballé, Expédié, Annulé, Livré, Payé, Remboursé, etc. La transition se produit automatiquement lorsque les choses se déplacent dans un entrepôt ou un centre logistique et sont scannées à différentes étapes, par exemple lorsqu'un utilisateur annule ou souhaite un remboursement.

  • Le test de processus est une autre application typique des State Machines. Dans cet exemple, chaque étape du processus est représentée par un état. Selon les résultats de l'examen de chaque État, un État distinct peut être déclaré. Cela peut se produire fréquemment si le processus à l'étude est étudié de manière approfondie.


Conclusion

La notion de machines à états est extrêmement utile en programmation. Cela rationalise non seulement le processus de développement d'applications de cas d'utilisation plus complexes, mais réduit également le travail de développement nécessaire. Il offre une compréhension plus simple et élégante des événements modernes et, lorsqu'il est correctement appliqué, peut faire des miracles.