Mettre à jour:
Encore merci pour les exemples, ils ont été très utiles et avec ce qui suit, je ne veux rien leur enlever.
Les exemples actuellement fournis, pour autant que je les comprenne et les machines à états, ne sont-ils que la moitié de ce que nous comprenons habituellement par une machine à états?
Dans le sens où les exemples changent d'état, mais cela n'est représenté qu'en changeant la valeur d'une variable (et en permettant différents changements de valeur dans différents états), alors qu'en général une machine à états devrait également changer son comportement, et le comportement non (seulement) dans le sens d'autoriser différents changements de valeur pour une variable en fonction de l'état, mais dans le sens d'autoriser l'exécution de différentes méthodes pour différents états.
Ou ai-je une idée fausse des machines d'état et de leur utilisation courante?
Meilleures salutations
Question d'origine:
J'ai trouvé cette discussion sur les machines d'état et les blocs d'itérateur en c # et les outils pour créer des machines d'état et quoi pas pour C #, donc j'ai trouvé beaucoup de choses abstraites mais en tant que noob tout cela est un peu déroutant.
Il serait donc formidable que quelqu'un puisse fournir un exemple de code source C # qui réalise une machine à états simple avec peut-être 3,4 états, juste pour avoir l'essentiel.
la source
Réponses:
Commençons par ce diagramme d'état simple:
Nous avons:
Vous pouvez le convertir en C # de plusieurs manières, par exemple en exécutant une instruction switch sur l'état et la commande en cours, ou en recherchant des transitions dans une table de transition. Pour cette machine à états simple, je préfère une table de transition, qui est très facile à représenter à l'aide d'un
Dictionary
:Par préférence personnelle, j'aime concevoir mes machines à états avec une
GetNext
fonction pour renvoyer le prochain état de façon déterministe , et uneMoveNext
fonction pour muter la machine à états.la source
GetHashCode()
utilisation des nombres premiers.StateTransition
classe est utilisée comme clé dans le dictionnaire et l'égalité des clés est importante. Deux instances distinctes deStateTransition
doivent être considérées comme égales tant qu'elles représentent la même transition (par exempleCurrentState
etCommand
sont les mêmes). Pour mettre en œuvre l' égalité , vous devez remplacerEquals
ainsi queGetHashCode
. En particulier, le dictionnaire utilisera le code de hachage et deux objets égaux doivent retourner le même code de hachage. Vous obtenez également de bonnes performances si pas trop d'objets non égaux partagent le même code de hachage, c'est pourquoi ilGetHashCode
est implémenté comme indiqué.Vous souhaiterez peut-être utiliser l'une des machines à états finis open source existantes. Par exemple bbv.Common.StateMachine trouvé sur http://code.google.com/p/bbvcommon/wiki/StateMachine . Il a une syntaxe fluide très intuitive et de nombreuses fonctionnalités telles que, les actions d'entrée / sortie, les actions de transition, les gardes, la mise en œuvre hiérarchique, passive (exécutée sur le thread de l'appelant) et la mise en œuvre active (propre thread sur lequel le fsm s'exécute, les événements sont ajoutés à une file d'attente).
En prenant l'exemple de Juliette, la définition de la machine d'état devient très simple:
Mise à jour : l'emplacement du projet a été déplacé vers: https://github.com/appccelerate/statemachine
la source
Voici un exemple d'une machine à états finis très classique, modélisant un appareil électronique très simplifié (comme un téléviseur)
la source
private void DoNothing() {return;}
et remplacé toutes les instances de null parthis.DoNothing
. A l'effet secondaire agréable de retourner l'état actuel.States
toUnpowered, Standby, On
. Mon raisonnement est que si quelqu'un me demandait dans quel état se trouve ma télévision, je dirais «Off» et non «Start». J'ai aussi changéStandbyWhenOn
etStandbyWhenOff
versTurnOn
etTurnOff
. Cela rend le code plus intuitif, mais je me demande s'il existe des conventions ou d'autres facteurs qui rendent ma terminologie moins appropriée.Une auto-promo sans vergogne ici, mais il y a quelque temps, j'ai créé une bibliothèque appelée YieldMachine qui permet de décrire une machine à états à complexité limitée d'une manière très propre et simple. Par exemple, considérons une lampe:
Notez que cette machine à états a 2 déclencheurs et 3 états. Dans le code YieldMachine, nous écrivons une seule méthode pour tous les comportements liés à l'état, dans laquelle nous commettons l'horrible atrocité de l'utilisation
goto
pour chaque état. Un déclencheur devient une propriété ou un champ de typeAction
, décoré d'un attribut appeléTrigger
. J'ai commenté le code du premier état et ses transitions ci-dessous; les états suivants suivent le même schéma.Bref et sympa, hein!
Cette machine d'état est contrôlée simplement en lui envoyant des déclencheurs:
Juste pour clarifier, j'ai ajouté quelques commentaires au premier état pour vous aider à comprendre comment l'utiliser.
Cela fonctionne car le compilateur C # a réellement créé une machine d'état en interne pour chaque méthode qui utilise
yield return
. Cette construction est généralement utilisée pour créer des séquences de données paresseusement, mais dans ce cas, nous ne sommes pas réellement intéressés par la séquence retournée (qui est de toute façon nulle), mais par le comportement d'état qui est créé sous le capot.La
StateMachine
classe de base réfléchit à la construction pour attribuer du code à chaque[Trigger]
action, ce qui définit leTrigger
membre et fait avancer la machine d'état.Mais vous n'avez pas vraiment besoin de comprendre les internes pour pouvoir l'utiliser.
la source
goto
méthode entre les deux.goto
de passer d'une méthode à l'autre? Je ne vois pas comment cela pourrait fonctionner. Non,goto
c'est problématique car cela entraîne une programmation procédurale (cela complique en soi de belles choses comme les tests unitaires), favorise la répétition du code (vous avez remarqué commentInvalidTrigger
doit être inséré pour chaque état?) Et rend finalement le flux du programme plus difficile à suivre. Comparez cela à (la plupart) des autres solutions de ce fil et vous verrez que c'est la seule où le FSM entier se produit dans une seule méthode. C'est généralement suffisant pour soulever une préoccupation.goto
assez bien.goto
de sauter entre les fonctions, mais il ne prend pas en charge les fonctions? :) Vous avez raison, la remarque "plus difficile à suivre" est plus ungoto
problème général , en fait pas vraiment un problème dans ce cas.Vous pouvez coder un bloc d'itérateur qui vous permet d'exécuter un bloc de code de manière orchestrée. La façon dont le bloc de code est divisé ne doit vraiment correspondre à rien, c'est simplement la façon dont vous voulez le coder. Par exemple:
Dans ce cas, lorsque vous appelez CountToTen, rien ne s'exécute réellement, pour l'instant. Ce que vous obtenez est en fait un générateur de machine d'état, pour lequel vous pouvez créer une nouvelle instance de la machine d'état. Pour ce faire, appelez GetEnumerator (). L'IEnumerator résultant est en fait une machine d'état que vous pouvez piloter en appelant MoveNext (...).
Ainsi, dans cet exemple, la première fois que vous appelez MoveNext (...), vous verrez "1" écrit sur la console, et la prochaine fois que vous appelez MoveNext (...), vous verrez 2, 3, 4 et puis 5, 6, 7 puis 8, puis 9, 10. Comme vous pouvez le voir, c'est un mécanisme utile pour orchestrer comment les choses doivent se produire.
la source
Je poste une autre réponse ici car il s'agit de machines à états sous un angle différent; très visuel.
Ma réponse originale est un code impératif classique. Je pense que son aspect est assez visuel à cause du tableau qui rend la visualisation de la machine à états simple. L'inconvénient est que vous devez écrire tout cela. La réponse de Remos allège l'effort d'écriture du code de la plaque de la chaudière mais est beaucoup moins visuelle. Il y a la troisième alternative; vraiment dessiner la machine d'état.
Si vous utilisez .NET et pouvez cibler la version 4 de l'exécution, vous avez la possibilité d'utiliser les activités de la machine d'état du workflow . Celles-ci vous permettent essentiellement de dessiner la machine à états (un peu comme dans le diagramme de Juliette ) et de le faire exécuter par WF au moment de l'exécution.
Voir l'article MSDN Building State Machines with Windows Workflow Foundation pour plus de détails, et ce site CodePlex pour la dernière version.
C'est l'option que je préférerais toujours lors du ciblage de .NET car il est facile à voir, à modifier et à expliquer aux non-programmeurs; les images valent mille mots comme on dit!
la source
Il est utile de se rappeler que les machines à états sont une abstraction et que vous n'avez pas besoin d'outils particuliers pour en créer un, mais les outils peuvent être utiles.
Vous pouvez par exemple réaliser une machine à états avec des fonctions:
Cette machine chassait les goélands et tentait de les frapper avec des ballons d'eau. S'il manque, il essaiera d'en tirer un jusqu'à ce qu'il frappe (ce qui pourrait faire avec des attentes réalistes;)), sinon il se réjouira dans la console. Il continue de chasser jusqu'à ce qu'il soit à court de goélands pour harceler.
Chaque fonction correspond à chaque état; les états de début et de fin (ou d' acceptation ) ne sont pas affichés. Cependant, il y a probablement plus d'états que modélisés par les fonctions. Par exemple, après avoir tiré le ballon, la machine est vraiment dans un autre état qu'elle ne l'était avant, mais j'ai décidé que cette distinction n'était pas pratique à faire.
Une manière courante consiste à utiliser des classes pour représenter des états, puis à les connecter de différentes manières.
la source
J'ai trouvé ce super didacticiel en ligne et il m'a aidé à comprendre les machines à états finis.
http://gamedevelopment.tutsplus.com/tutorials/finite-state-machines-theory-and-implementation--gamedev-11867
Le tutoriel est indépendant du langage, il peut donc facilement être adapté à vos besoins en C #.
De plus, l'exemple utilisé (une fourmi à la recherche de nourriture) est facile à comprendre.
Du tutoriel:
la source
Aujourd'hui, je suis profondément dans le modèle de conception d'état. J'ai fait et testé ThreadState, qui équivaut à (+/-) à Threading en C #, comme décrit dans l'image de Threading en C #
Vous pouvez facilement ajouter de nouveaux états, configurer les mouvements d'un état à l'autre est très facile car il est encapsulé dans la mise en œuvre de l'état
Implémentation et utilisation à: Implements .NET ThreadState by State Design Pattern
la source
Je n'ai pas encore essayé d'implémenter un FSM en C #, mais tout cela semble (ou semble) très compliqué à la façon dont je gérais les FSM dans le passé dans des langages de bas niveau comme C ou ASM.
Je crois que la méthode que j'ai toujours connue s'appelle quelque chose comme une "boucle itérative". Vous y trouverez essentiellement une boucle «while» qui se termine périodiquement en fonction des événements (interruptions), puis revient à nouveau à la boucle principale.
Dans les gestionnaires d'interruption, vous passeriez un CurrentState et renverriez un NextState, qui écraserait ensuite la variable CurrentState dans la boucle principale. Vous faites cela à l'infini jusqu'à la fermeture du programme (ou la réinitialisation du microcontrôleur).
Ce que je vois d'autres réponses semblent toutes très compliquées par rapport à la façon dont un FSM est, dans mon esprit, destiné à être mis en œuvre; sa beauté réside dans sa simplicité et FSM peut être très compliqué avec de nombreux états et transitions, mais ils permettent de décomposer et de digérer facilement des processus compliqués.
Je réalise que ma réponse ne devrait pas inclure une autre question, mais je suis obligé de demander: pourquoi ces autres solutions proposées semblent-elles si compliquées?
Ils semblent s'apparenter à frapper un petit clou avec un marteau géant.
la source
Quel combat StatePattern. Cela correspond-il à vos besoins?
Je pense que son contexte est lié, mais ça vaut le coup à coup sûr.
http://en.wikipedia.org/wiki/State_pattern
Cela permet à vos états de décider où aller et non à la classe "objet".
Bruno
la source
À mon avis, une machine à états n'est pas seulement destinée à changer d'états, mais aussi (très importante) à gérer les déclencheurs / événements dans un état spécifique. Si vous voulez mieux comprendre le modèle de conception de la machine à états, une bonne description peut être trouvée dans le livre Modèles de conception en tête, page 320 .
Il ne s'agit pas seulement des états dans les variables mais aussi de la gestion des déclencheurs dans les différents états. Grand chapitre (et non, il n'y a pas de frais pour moi en mentionnant ceci :-) qui contient juste une explication facile à comprendre.
la source
Je viens de contribuer ceci:
https://code.google.com/p/ysharp/source/browse/#svn%2Ftrunk%2FStateMachinesPoC
Voici l'un des exemples de démonstration de l'envoi direct et indirect de commandes, avec des états comme IObserver (de signal), répondant ainsi à une source de signal, IObservable (de signal):
Remarque: cet exemple est plutôt artificiel et vise principalement à démontrer un certain nombre de caractéristiques orthogonales. Il devrait rarement y avoir un réel besoin d'implémenter le domaine de valeurs d'état lui-même par une classe complète, en utilisant le CRTP (voir: http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern ) comme ceci.
Voici un cas d'utilisation d'implémentation certainement plus simple et probablement beaucoup plus courant (en utilisant un type d'énumération simple comme domaine de valeur d'états), pour la même machine d'état et avec le même cas de test:
https://code.google.com/p/ysharp/source/browse/trunk/StateMachinesPoC/WatchingTVSample.cs
«HTH
la source
StateChange
résolu? Par la réflexion? Est-ce vraiment nécessaire?private Television(string moniker, Television value) { Handler<Television, TvOperation, DateTime> myHandler = StateChange; // (code omitted) new { From = Television.Unplugged, When = TvOperation.Plug, Goto = Television.Off, With = myHandler } }
J'ai fait cette machine à états générique à partir du code de Juliette. Cela fonctionne très bien pour moi.
Ce sont les avantages:
TState
etTCommand
,TransitionResult<TState>
pour avoir plus de contrôle sur les résultats de sortie des[Try]GetNext()
méthodesStateTransition
seulement à travers leAddTransition(TState, TCommand, TState)
rendant plus facile de travailler avec elleCode:
Il s'agit du type de retour de la méthode TryGetNext:
Comment utiliser:
Voici comment créer un à
OnlineDiscountStateMachine
partir de la classe générique:Définissez une énumération
OnlineDiscountState
pour ses états et une énumérationOnlineDiscountCommand
pour ses commandes.Définir une classe
OnlineDiscountStateMachine
dérivée de la classe générique à l'aide de ces deux énumérationsDérivez le constructeur de
base(OnlineDiscountState.InitialState)
sorte que l' état initial soit défini surOnlineDiscountState.InitialState
Utilisez
AddTransition
autant de fois que nécessaireutiliser la machine à états dérivée
la source
Je pense que la machine d'état proposée par Juliet a une erreur: la méthode GetHashCode peut renvoyer le même code de hachage pour deux transitions différentes, par exemple:
Pour éviter cette erreur, la méthode doit être comme ceci:
Alex
la source
int
valeurs possibles ). C'est pourquoiHashCode
est toujours mis en œuvre avecEquals
. Si les codes de hachage sont les mêmes, alors les objets sont vérifiés pour une équation exacte en utilisant laEquals
méthode.FiniteStateMachine est une machine d'état simple, écrite en C # Link
Avantages d'utiliser ma bibliothèque FiniteStateMachine:
Télécharger DLL Télécharger
Exemple sur LINQPad:
la source
Je recommanderais state.cs . J'ai personnellement utilisé state.js (la version JavaScript) et j'en suis très content. Cette version C # fonctionne de manière similaire.
Vous instanciez des états:
Vous instanciez certaines transitions:
Vous définissez des actions sur les états et les transitions:
Et c'est à peu près tout. Consultez le site Web pour plus d'informations.
la source
Il existe 2 packages de machines d'état populaires dans NuGet.
Appccelerate.StateMachine (13,6 K téléchargements + 3,82 K de la version héritée (bbv.Common.StateMachine))
StateMachineToolkit (1,56 K téléchargements)
La bibliothèque Appccelerate a une bonne documentation , mais elle ne prend pas en charge .NET 4, j'ai donc choisi StateMachineToolkit pour mon projet.
la source
Une autre alternative dans ce dépôt https://github.com/lingkodsoft/StateBliss utilise une syntaxe fluide, prend en charge les déclencheurs.
la source
Vous pouvez utiliser ma solution, c'est le moyen le plus pratique. C'est aussi gratuit.
Créez une machine à états en trois étapes:
1. Créez un schéma dans l' éditeur de nœuds🔗 et chargez-le dans votre projet à l'aide de la bibliothèque📚
2. Décrivez la logique de votre application sur les événements⚡
3. Exécutez la machine d'état🚘
Liens:
Éditeur de nœuds: https://github.com/SimpleStateMachine/SimpleStateMachineNodeEditor
Bibliothèque: https://github.com/SimpleStateMachine/SimpleStateMachineLibrary
la source