Utiliser une machine à état pour améliorer les ViewModels et les UI (version Xamarin.Forms)

DĂ©coupler la logique des Ă©tats et transitions d’un ViewModel en faisant gĂ©rer les Commandes par une Machine Ă  Etats Finis apporte un nouveau niveau d’abstraction aussi important que l’est MVVM lui-mĂȘme. Êtes-vous prĂȘt Ă  gĂ©rer correctement le workflow de vos applications et en amĂ©liorer l’ergonomie ?

Nouvelle Version Xamarin.Forms

J’ai dĂ©jĂ  dĂ©veloppĂ© ce sujet Ă  propos de WPF il y a cinq ans
 Le sujet est toujours aussi passionnant, toujours aussi rĂ©volutionnaire et toujours aussi peu populaire ! Beaucoup de raisons qui justifient de vous en prĂ©senter aujourd’hui une version rĂ©novĂ©e, utilisant les derniĂšres versions des outils de dĂ©veloppement et le tout sous Xamarin.Forms ! Bonne lecture, en espĂ©rant que cette fois-ci l’idĂ©e fasse encore un autre bout de chemin vers son adoption


Le problĂšme des commandes

Avant d’entrer dans les dĂ©tails posons le problĂšme :

GĂ©rer des commandes en MVVM est trĂšs simple. Une commande n’est qu’une propriĂ©tĂ© de type ICommand du ViewModel que l’on attache (binding) Ă  une propriĂ©tĂ© de mĂȘme type d’un objet d’UI (type bouton par exemple). Lorsque l’utilisateur dĂ©clenche la commande via l’objet d’UI la propriĂ©tĂ© ICommand du ViewModel dĂ©clenche sa mĂ©thode Execute().

Tout est simple et clair grĂące Ă  XAML, son binding et le pattern MVVM. Les librairies annexes comme MVVM Light simplifient encore plus la tĂąche et proposent en gĂ©nĂ©ral un objet RelayCommand plus complet et plus pratique Ă  utiliser que l’interface brute ICommand. L’implĂ©mentation d’origine a Ă©tĂ© créée par Josh Smith et on la retrouve dans de nombreuses librairies MVVM dont Prism sous ce nom ou un autre. Xamarin.Forms vient avec sa propre implĂ©mentation appellĂ©e Command, supportant ICommand de la mĂȘme façon, il n’y a donc ici aucune toolbox supplĂ©mentaire Ă  utiliser.

L’interface ICommand est fort simple et se compose de peu de choses. Dont une mĂ©thode CanExecute(Object).

Mais tout cela ne change rien Ă  la vĂ©ritable question qu’on doit se poser pour crĂ©er un logiciel bien programmĂ© : comment gĂ©rer proprement le CanExecute ?

Je vois beaucoup de code dans lequel CanExecute n’est tout simplement pas gĂ©ré  J’en conclue que beaucoup d’entre vous se diront “mais pourquoi se prend-t-il la tĂȘte avec ce ‘dĂ©tail’ ?”

Parce que ce n’est pas un dĂ©tail justement
 CanExecute reflĂšte de façon assez fidĂšle le workflow du ViewModel, ce qui est possible de faire ou non Ă  un moment donnĂ©, donc l’état de l’automate qu’est un logiciel


CanExecute est donc l’émanation programmatique de l’essence mĂȘme de ce qu’est l’informatique : crĂ©er des automates, des machines de Turing. Comment cela pourrait-il ĂȘtre un simple “dĂ©tail” ?

Plus important encore car c’est la partie visible pour l’utilisateur, la bonne gestion de CanExecute permet Ă  l’UI de mieux le guider et donc d’amĂ©liorer l’ergonomie de vos applications.

Quand CanExecute n’est pas gĂ©rĂ©

Quand le dĂ©veloppeur ne gĂšre pas le CanExecute des commandes celles-ci peuvent donc s’exĂ©cuter Ă  tout moment dans n’importe quel ordre quel que soit l’état de l’automate porte ouverte Ă  tous les bogues. Autant dire que c’est le boxon pour ĂȘtre poli. A moins que le dĂ©veloppeur ne barde en entrĂ©e les mĂ©thodes exĂ©cutĂ©es de tests divers et variĂ©s pour vĂ©rifier si le code peut ou non ĂȘtre exĂ©cutĂ© justement. Ce n’est pas l’endroit oĂč le faire, le CanExecute sert Ă  cela
 Ă  moins de travailler par Aspect mais c’est un autre dĂ©bat. Pour l’utilisateur c’est forcĂ©ment le chaos Ă  l’arrivĂ©e aprĂšs un chemin semĂ© de doutes
 Sans parler des incohĂ©rences, plantages, et autres dĂ©synchronisation code/UI.

Quand CanExecute est géré

Bien ! Vous gĂ©rez le CanExecute de vos commandes je vous en fĂ©licite ! Mais de ce que je constate le plus souvent, mĂȘme dans ce cas oĂč le dĂ©veloppeur Ă  fait son job, c’est qu’hĂ©las les tests de CanExecute sont soit trop succincts soit se compliquent tellement qu’on en perd le fil. Quant Ă  comprendre ce qui est vraiment autorisĂ© ou non dans tel ou tel Ă©tat et comment maintenir ces tests 
 il faudrait lire en un bloc tous les tests de tous les CanExecute et resynthĂ©tiser tout cela en un schĂ©ma. D’ailleurs quels sont les Ă©tats d’un ViewModel donnĂ© ? Il est rarissime qu’un dĂ©veloppeur envisage les choses sous cet angle et des incohĂ©rences apparaissent vite entrainant une UX pĂ©nible pour l’utilisateur et des bogues difficiles Ă  supprimer (sans parler de la dette technique qui augmente !). Le plus souvent on teste donc ce qui semble interdire une commande mais sans avoir fait l’effort de lister tous les Ă©tats possibles du ViewModel
 Bref mĂȘme quand le CanExecute est gĂ©rĂ© on est loin de la perfection. TrĂšs loin.

Il ne s’agit bien entendu pas d’acadĂ©misme. La recherche d’une perfection illusoire, d’une orthodoxie tatillonne. Non, il s’agit bien d’une prĂ©occupation majeure, un programme est une machine Ă  Ă©tats et ne pas matĂ©rialiser clairement ces Ă©tats c’est prendre de gros risques dont le plus grave de tous est de perdre l’utilisateur dans un dĂ©dale illogique. Tout comme MVVM et le dĂ©couplage fort permettent de minimiser les erreurs et les mĂ©langes entre UI et logique, entre services et utilisateurs de services, gĂ©rer les Ă©tats d’un ViewModel minimise un risque majeur, celui de pondre du code spaghetti au cƓur mĂȘme de la logique de l’application !

Comment gérer correctement CanExecute ?

La critique est facile, l’art est plus difficile, c’est connu. Mais vous me connaissez, plus je critique plus ma rĂ©ponse est longue ! Donc comment gĂ©rer correctement CanExecute ? En gĂ©rant bien les Ă©tats de l’automate qu’est le ViewModel. Certes. Et comment ?

En utilisant un outil adapté à cette situation : une machine à états finis.

Il y a de nombreux avantages Ă  utiliser cette stratĂ©gie, notamment celui de crĂ©er un dĂ©couplage entre d’une part la logique des Ă©tats possibles et d’autre part les transitions entre ceux-ci ainsi que le code du ViewModel. Cette abstraction supplĂ©mentaire simplifie le ViewModel, le rend plus maintenable tout en rendant lisible, cohĂ©rent et facilement modifiable le workflow. Le ViewModel devient l’emplacement du code des actions, la machine Ă  Ă©tats finis le mode d’emploi des commandes. L’UI dĂ©jĂ  dĂ©couplĂ©e par MVVM tire profit de ce nouveau niveau de dĂ©couplage de façon automatique grĂące Ă  la machine Ă  Ă©tats finis qui gĂšre automatiquement le CanExecute() qui guide l’utilisateur dans sa progression. DĂ©veloppeur et utilisateur sont gagnants


Qu’est-ce qu’une machine Ă  Ă©tats finis ?

Une Finite State Machine, ou Machine Ă  Etats Finis modĂ©lise le comportement sĂ©quentiel d’un objet. Une telle machine possĂšde donc un nombre fini d’états et ne peut avoir qu’un seul Ă©tat Ă  la fois. Les machines hiĂ©rarchiques autorisent la notion de sous-Ă©tats et sont trĂšs utilisĂ©es car plus conformes Ă  la complexitĂ© des mĂ©canismes modĂ©lisĂ©s en gĂ©nĂ©ral (l’hĂ©ritage de la configuration de l’état maitre par un sous-Ă©tat simplifie son paramĂ©trage et donc celui de toute la machine).

On notera qu’on appelle Automate Fini ce type de machine mais j’utilise ici la traduction littĂ©rale de l’anglais “Machine Ă  Ă©tats finis” dans le sens d’un code spĂ©cifique (une librairie) gĂ©rant les Ă©tats d’une partie d’application afin de le diffĂ©rencier de l’Automate Fini qu’est l’application elle-mĂȘme prise comme un tout. Cette nuance est artificielle et n’a de sens que dans le contexte de cet article afin de sĂ©parer les deux choses.

Une telle machine se définie donc par :

  • Des Ă©tats gĂ©nĂ©ralement nommĂ©s par un adjectif indiquant une action en cours (“en train de”, le “ing” Ă  la fin d’un verbe anglais), par exemple : Listening, Waiting, Editing, Printing, etc
 (on peut le faire en français aussi bien que je conseille par cohĂ©rence avec le langage de rester toujours en anglais dans les noms de champs, mĂ©thodes etc, sinon on obtient un franglais abominable je trouve).
  • Des Ă©vĂšnements qui sont des conditions extĂ©rieures comme une action utilisateur. Ces Ă©vĂšnements sont eux aussi nommĂ©s avec des conventions diverses mais indiquant une action comme Save, Refresh ou Print ou autre dans cet esprit. Ces Ă©vĂšnement peuvent dĂ©clencher une sortie de l’état courant vers un nouvel Ă©tat dit final.

Au final l’association de l’état courant, de l’état final, de l’évĂšnement et de l’action forme ce qu’on appelle une transition.

On retrouve cette notion de machine Ă  Ă©tats finis dans toute l’informatique puisqu’elle en est la base mĂȘme et c’est sans surprise que le langage de modĂ©lisation UML propose un diagramme Ă©tats-transitions dans le groupe des diagrammes comportementaux par exemple. Mais sans aller chercher loin, tout le monde connait les logigrammes ou ordinogrammes qui servent Ă  dĂ©crire un algorithme Ă  l’aide de boites, de flĂšches et de losanges de prise de dĂ©cision. Sans ĂȘtre tout Ă  fait identiques ces diagrammes “primitifs” reprĂ©sentent eux aussi en quelque sorte des Ă©tats et des transitions d’un automate.

Stateless State Machine

Stateless, “sans Ă©tat”, est un code créé par Nicholas Blumhardt qui permet facilement de coder une machine Ă  Ă©tats finis hiĂ©rarchique. Le nom est Ă©trange mais on peut certainement l’expliquer par le fait que justement c’est cette machine qui gĂšre les Ă©tats et non plus le code principal de l’application qui devient alors “sans Ă©tats”. Mais bon c’est une supposition et cela reste tirĂ© par les cheveux ! Heureusement ce n’est pas le nom qui nous intĂ©resse mais ce que fait “Stateless”.

Stateless se prĂ©sente sous la forme de paquet Nuget Ă  installer dans la partie Xamarin.Forms, pas besoin de le placer dans les parties natives puisqu’il s’agit de pur code C# destinĂ© Ă  l’App (pas de renderer par exemple). Les derniĂšres versions sont des 5.x. On trouve le package dans Nuget :

image

Code minimaliste d’une machine Ă  Ă©tats finis

Pour mieux comprendre Stateless commençons par coder “à la main” (sans utiliser le paquet donc) une machine Ă  Ă©tats finis vraiment minimale. Elle ne connait que 2 Ă©tats, On et Off, qu’un seul dĂ©clencheur dans la mĂ©thode Transition, la commande “on” (le reste Ă©tant compris comme la commande “off”) :

public class BasicStateMachine
    {
        public string State { get; private set; }

        public void Transition(string state)
        {
            switch (state)
            {
                case "on":
                    State = "ON";
                    OnSwitchedOn();
                    break;
                default:
                    State = "OFF";
                    OnSwitchedOff();
                    break;
            }
        }

        private void OnSwitchedOn()
        {
            // Do something
        }

        private void OnSwitchedOff()
        {
            // Stop doing something
        }
    }

Ce code est rĂ©ellement direct et dĂ©jĂ  contient les germes de la problĂ©matique des commandes, sujet principal de cet article ne l’oublions pas mĂȘme si je suis obligĂ© de vous prĂ©senter tout cela avant d’y arriver !

En effet, un choix a Ă©tĂ© fait ici dans l’implĂ©mentation, le switch bascule vers On dans le cas oĂč la commande “on” est envoyĂ©e et renvoie vers l’état Off dans tous les autres cas. C’est un choix qu’il faut assumer. La machine doit-elle s’éteindre si je lui commande “toto” ou _uniquement_ si je lui commande “off”  ? Un autre aspect est nĂ©gligĂ© :  dans quel Ă©tat se trouve l’automate au dĂ©part ?

Sans diagramme on se rend compte que mĂȘme dans un cas aussi simple les problĂšmes se posent. Alors imaginez un peu ce qui se passe dans le code rĂ©el d’un ViewModel un peu complexe oĂč cet aspect n’est pas mĂȘme gĂ©rĂ© !!!

Schématisons un peu le code ci-dessus :

image

L’état initial n’est pas prĂ©cisĂ©, premiĂšre erreur qui devient Ă©vidente quand on reprĂ©sente le schĂ©ma. L’état final aussi n’est pas prĂ©cisĂ©, la machine est donc Ă  fonctionnement infini, il n’y pas de sortie, ce qui n’est pas une erreur mais un choix.

Enfin on note les deux Ă©tats On et Off et les transitions. La premiĂšre va de On vers Off et n’est pas dĂ©finie, n’importe quel Ă©vĂšnement fera donc basculer vers Off. La seconde va de Off Ă  On et ne fonctionne que sur l’envoi de la commande “on” ce qui est trĂšs restrictif mais qui peut s’admettre.

Bref, une fois le diagramme posĂ© on s’aperçoit des manques et incohĂ©rences.

On notera au passage que je parle de “commandes” pour passer d’un Ă©tat Ă  l’autre ce n’est pas une terminologie tout Ă  fait exacte (on parle d’évĂšnements ou de dĂ©clencheurs) mais cela est Ă  mettre en rapport avec le sujet de l’article qui concerne les commandes qui sont bien Ă  la source des Ă©vĂšnements qui causent les transitions. Pour approfondir toute la rigueur de la notation UML des diagrammes d’états transitions je renvoie le lecteur intĂ©ressĂ© Ă  la nombreuse littĂ©rature technique sur le sujet.

Si on modélise la machine de façon plus sensée (en y réfléchissant donc) on voit les états, les transitions, ce qui donne plutÎt cela :

image

Comme on visualise le diagramme on s’aperçoit qu’il faut bien un Ă©tat initial, et ici il est Ă  “Off”. Ensuite s’il faut une commande “on” pour passer Ă  “On”, il faut aussi pour ĂȘtre rigoureux une commande “off” pour passer Ă  “Off”. Ce n’est pas innocent comme choix


Si n’importe quoi fait basculer Ă  Off comme dans le code de dĂ©part, cela peut ĂȘtre voulu, une machine outil par exemple est dangereuse, dans la prĂ©cipitation d’un accident l’opĂ©rateur peut par affolement appuyer n’importe oĂč et il est prĂ©fĂ©rable dans ce contexte que cela passe Ă  “Off”, mieux vaut arrĂȘter la machine pour rien qu’arracher un bras... Si maintenant c’est un systĂšme de circulation extracorporelle utilisĂ© durant les pontages coronariens (qui remplace le cƓur pendant qu’on le “dĂ©monte”), il est prĂ©fĂ©rable que la commande “Off” soit au contraire hyper verrouillĂ©e avec mĂȘme une clĂ© Ă  tourner, un code Ă  saisir, etc.

il est vraiment essentiel de comprendre que toutes ces petites choses apparemment de l’ordre du dĂ©tail ont en rĂ©alitĂ© un impact gigantesque sur l’application et sa raison d’ĂȘtre, sa finalitĂ©, ses contraintes. Et que tout cela OBLIGE a se poser des questions qui ne viendrait jamais en tĂȘte sans cet effort de formalisation !

Il n’y a pas d’état final dans l’exemple corrigĂ©, c’est une machine Ă  Ă©tats finis mais Ă  fonctionnement infini, pourquoi pas. On pourrait en revanche pousser plus loin le diagramme en indiquant un Ă©tat “Error” si une commande diffĂ©rente de “on” ou “off” est envoyĂ©e ou bien reprĂ©senter une boucle sur les Ă©tats “On” et “Off” qui indiquerait que toute commande diffĂ©rente de celle attendue fait rester la machine dans son Ă©tat courant.

LĂ  encore rien n’est de l’ordre du dĂ©tail, les questions soulevĂ©es par ces deux boites, ce point et ces deux flĂšches vont creuser trĂšs loin dans l’analyse fonctionnelle du logiciel, bien plus loin qu’on ne l’imagine au dĂ©part. Les diagrammes en gĂ©nĂ©ral, mĂȘme de SGBD sont des outils essentiels en cela que la prĂ©sence ou non d’une flĂšche, d’une orientation, d’une cardinalitĂ©, aussi insignifiant que cela paraisse soulĂšve des questions d’une grande profondeur dans la comprĂ©hension du mĂ©canisme modĂ©lisĂ©, questions qui sont rarement posĂ©es autrement.

Nous n’irons pas trop loin dans la modĂ©lisation de notre exemple et regardons simplement l’impact du diagramme corrigĂ© succinctement sur le code :

public class BasicStateMachine
    {
        const string offState = "OFF";
        const string onState = "ON";
        private string state = offState;
        public string State { 
                              get { return state;} 
                              private set { state=value;}
                             }

        public void Transition(string state)
        {
            switch (state.ToUpper())
            {
                case "ON":
                    State = onState;
                    OnSwitchedOn();
                    break;
	        case "OFF":
                    State = offState;
                    OnSwitchedOff();
                    break;
            }
        }

        private void OnSwitchedOn()
        {
            // Do something
        }

        private void OnSwitchedOff()
        {
            // Stop doing something
        }
    }

La machine ainsi codĂ©e possĂšde un Ă©tat initial, “Off”, et ne bascule de l’un Ă  l’autre que si les commandes “on” et “off”, peu importe la casse, sont envoyĂ©es. Tout autre commande laisse la machine dans son Ă©tat courant.

Finalement nous avons un code plus rigoureux mais s’il fallait reprĂ©senter une vĂ©ritable machine Ă  Ă©tats finis et la faire Ă©voluer il faut avouer que cela deviendrait vite fastidieux avec une telle programmation “manuelle”.

Stateless pour coder la machine à états

Avec Stateless la mĂȘme machine se codera de la façon suivante :

public class StatelessStateMachine
    {
        private readonly StateMachine stateMachine;

        public enum Trigger
        {
            TurnOn,
            TurnOff
        }

        public enum State
        {
            On,
            Off
        }

        public State Current => stateMachine.State;

        public StatelessStateMachine()
        {
            stateMachine = new StateMachine(State.Off);
            stateMachine.Configure(State.Off)
                .Permit(Trigger.TurnOn, State.On)
                .OnEntry(OnSwitchedOff);

            stateMachine.Configure(State.On)
                .Permit(Trigger.TurnOff, State.Off)
                .OnEntry(OnSwitchedOn);
        }

        public bool Transition(Trigger trigger)
        {
            if (!stateMachine.CanFire(trigger))
                return false;

            stateMachine.Fire(trigger);

            return true;
        }
        private void OnSwitchedOn()
        {
            // Do something clever
        }

        private void OnSwitchedOff()
        {
            // Stop doing something clever
        }

    }

C’est un peu long pour si peu vous direz-vous et c’est normal, notre machine est tellement bĂȘte et simple que le code pour l’exprimer est forcĂ©ment long par rapport Ă  son utilitĂ© rĂ©elle, ce n’est qu’une sorte de Hello Word pour Stateless


Mais si vous lisez attentivement ce code vous vous apercevrez rapidement qu’il est bien plus clair et bien plus structurĂ©. Et surtout qu’il est bien plus souple, chaque Ă©tat est dĂ©fini, chaque Ă©vĂšnement aussi, et l’API de type “fluent” qui permet de dĂ©finir tout cela est aussi simple que puissante.

Modifier un Ă©tat, une transition, ajouter des actions d’entrĂ©es et de sorties Ă  une transition, etc, tout cela devient limpide.

Stateless utilise des méthodes génériques et le type des états ou des triggers est totalement libre. Ici on utilise des énumérations mais des objets plus complexes sont utilisables si cela le demande.

Et le rapport avec les commandes en MVVM ?

C’est en fait tout le sujet de l’article.

Maintenant vous avez une idĂ©e de ce qu’est une machine Ă  Ă©tats finis et de ce qu’est la librairie Stateless et comment elle s’utilise (au moins vous en avez une idĂ©e).

C’était un prĂ©ambule nĂ©cessaire. Le “vĂ©ritable” article commence ici.

“La continuitĂ© c’est maintenant !” (enfin un slogan politique qui ne ment pas !).

Retour à la problématique des commandes

Revenons sur le problĂšme que pose les commandes. On a bien compris qu’elles se fondent sur l’interface ICommand (mĂȘme si on utilise un RelayCommand ou une Command) et que les mĂ©thodes principales sont Execute() qui exĂ©cute l’action, CanExecute() qui teste si la commande peut s’exĂ©cuter et CanExecuteChanged() un Ă©vĂšnement qui permet Ă  l’UI de savoir si CanExecute() Ă  changĂ© de valeur.

L’intĂ©rĂȘt d’une classe comme RelayCommand ou Command est de proposer non pas une interface qu’il faut implĂ©menter Ă  chaque fois mais une classe dont on peut crĂ©er des instances immĂ©diatement ce qui est bien plus pratique. Autre apport de RelayCommand / Command, la mĂ©thode RaiseCanExecuteChanged() qui dĂ©clenche l’évĂšnement CanExecuteChanged(). En effet, si les possibilitĂ©s d’exĂ©cution changent et bien qu’il existe un Ă©vĂšnement ad hoc auquel s’abonner dans ICommand rien ne permet dans cette interface de forcer cet Ă©vĂšnement et donc de prĂ©venir l’UI que “l’exĂ©cutabilitĂ©â€ de la commande vient de changer.

Je n’irai pas trop loin sur ce point bien qu’essentiel car j’ai dĂ©jĂ  Ă©crit de trĂšs nombreux articles et livres sur MVVM et notamment sur la façon de gĂ©rer les commandes. Le lecteur intĂ©ressĂ© saura retrouver tout cela sur Dot.Blog et dans la collection “All Dot.Blog” j’en suis certain.

Comme toujours l’enfer se loge dans les fameux dĂ©tails. Le vrai chalenge n’est pas dans l’exĂ©cution de la commande, le code derriĂšre Execute(), mais dans le fait de s’assurer qu’il s’exĂ©cute quand le ViewModel est dans un Ă©tat valide pour l’action donnĂ©e.

Typiquement le code du CanExecute(), quand il est gĂ©rĂ©, est implĂ©mentĂ© sous la forme d’expressions conditionnelles qui utilisent des variables locales de type “isLoading” “isPrinting” “CanDoxxx” “objectMachin!=null” etc


Chaque nouvel Ă©tat nĂ©cessite de coder des conditions supplĂ©mentaires qui nĂ©cessitent une Ă©valuation, de nouvelles variables locales, etc, ce qui trĂšs rapidement devient d’une complexitĂ© impossible Ă  maitriser.

Sous XAML on dispose d’un outil qui permet de gĂ©rer les Ă©tats visuels, le Visual State Manager, c’est une aide prĂ©cieuse pour clarifier les choses. Mais si l’UI est dĂ©couplĂ©e du code, ses Ă©tats reposent sur ceux du code. Il ne peut y avoir une reprĂ©sentation de l’état “attente” dans le visuel XAML s’il n’y a pas un Ă©tat correspondant dans le ViewModel. Tout tient donc sur la bonne gestion des Ă©tats de la machine Ă  Ă©tats finis qui permet de construire un code solide et cohĂ©rent aussi bien en C# que cĂŽtĂ© UI en XAML. Une raison de plus d’adopter l’approche que je vous propose dans le prĂ©sent article !

Le rÎle de la machine à états finis

La machine Ă  Ă©tats finis va permettre de modĂ©liser tous les Ă©tats du ViewModel, donc ce qui est autorisĂ© ou non de faire selon l’état en cours.

En construisant des commandes Ă  partir du mĂ©canisme de la machine et en laissant cette derniĂšre dĂ©cider des actions et de la gestion du CanExecute il sera mĂȘme possible d’automatiser les rĂ©actions de l’UI puisque les objets supportant ICommand savent rĂ©agir au CanExecute en modifiant leur aspect visuel (qui peut de plus ĂȘtre retravaillĂ© grĂące Ă  la grande souplesse de XAML, Xamarin.Forms n’implĂ©mentant quelque chose que pour le Button Ă  l’heure actuelle).

On créé ainsi un nouveau dĂ©coupage intĂ©ressant : l’UI d’une part dont le rĂŽle ne change pas, le ViewModel qui devient un magasin de code (les actions) et d’autre part la machine Ă  Ă©tats finis qui joue le chef d’orchestre afin de garantir la cohĂ©rence de l’état du ViewModel et de l’UI.

Un exemple simple

Pour tenter d’ĂȘtre concret nous allons partir d’un exemple simple. Une pseudo gestion du personnel qui permet de faire des recherches, d’afficher la liste des personnes filtrĂ©es et bien entendu la modification d’une fiche sĂ©lectionnĂ©e. Pour rendre tout cela encore plus Ă©vident et Ă©viter d’avoir trop de code parasite le mode de recherche se limitera Ă  faire un refresh de la liste et simulera un temps d’attente (pour voir l’état visuel de l’application dans son mode “busy”).

Afin de comprendre les changements d’états et bien que cela sera absent d’une application rĂ©elle, nous allons adjoindre l’affichage de l’état courant ainsi qu’une liste qui va se remplir au fur et Ă  mesure de tous les Ă©tats qui se succĂšdent.

Il serait un peu stupide de recoder un nouvel exemple puisque les principes sont identiques, je renvoie ainsi le lecteur à l’exemple WPF original.

Téléchargez directement le Projet WPF VS 2013 du Code Démo (pointe le répertoire partagé des exemples Dot.Blog, répertoire Dropbox, chercher le fichier DemoMachineEtats.7z).

Un bon exercice sera de le transformer en Xamarin.Forms, mais l’utilisation de la Datagrid qui n’existe pas de base ni Ă  l’identique, le ruban supĂ©rieur qu’il faudra simuler, la mise en page forcĂ©ment pas adaptĂ©e Ă  un smartphone tout cela fait qu’il y aura du travail ! Le mieux serait de penser une nouvelle dĂ©mo adaptĂ©e Ă  un smartphone. Si cela pourrait me prendre plus de temps que d'Ă©crire l'article, en revanche cela vous ferait un bon entraĂźnement, que vous pourriez Ă©ventuellement partager avec nous ! Un courageux ou une courageuse pour se dĂ©vouer ?
Pour ceux qui ne veulent pas télécharger le code WPF et le faire tourner, voici un gif animé qui en fait une démonstration assez complÚte :

 

Conclusion

Il faut bien terminer un jour
 J’ai passĂ© trois jours sur l’article original et malgrĂ© la similitude je viens de passer tout un dimanche aprĂšs-midi Ă  crĂ©er cette version Xamarin.Forms, conclure prend donc ici toute sa signification !

J’espĂšre que vous avez compris le principe et surtout les intĂ©rĂȘts nombreux qu’il y a Ă  suivre ce pattern de l’Automate Fini en l’appliquant Ă  MVVM et XAML.

Non seulement les Ă©tats du ViewModel deviennent clairs et maintenables, cohĂ©rents et inviolables, mais cette approche force Ă  penser le workflow, les Ă©tats et transitions du ViewModel trĂšs tĂŽt dans le dĂ©veloppement de celui-ci. C’est un gain Ă©norme qui permet de lever des loups avant mĂȘme de coder, de s’apercevoir que le cahier des charges ne prĂ©voit rien dans telle ou telle situation par exemple. Car noyĂ©e dans une prose rĂ©barbative une analyse cache souvent beaucoup de choses !

Le code exemple complet est tĂ©lĂ©chargeable en fin d’article, amusez-vous bien ! (car dĂ©velopper correctement avec les bonnes mĂ©thodes est un plaisir).

Le sujet est vaste alors voici quelques références intéressantes :

Tout d’abord Tarquin Vaughan-Scott Ă  qui j’ai empruntĂ© originellement une partie de l’article et du code de dĂ©mo qu’il avait publiĂ© dans un article en 2014 dans MSDN qui m’avait sĂ©duit et dont je voulais absolument vous parler. Mieux vaut tard que jamais ! Et j’ai rĂ©ussi Ă  faire mieux que de vous en parler, en tout cas je l’espĂšre, en Ă©crivant ce long article qui enrichit beaucoup l’original et le rend plus accessible Ă  ceux d’entre-vous qui ne sont pas des fans de l’anglais


Cet article de Bob Nystrom (en anglais) qui présente assez bien le concept de machines à états.

Tom Anderson et sa rapide introduction à “Stateless” dont est issu le premier exemple de l’article.

On pourrait certainement encore allonger cette liste car le sujet est vaste dĂšs qu’on parle de MVVM, de son systĂšme de commande et lorsqu’on y ajoute les automates finis ! Mais j’ajouterais forcĂ©ment la collection All.Dot.Blog dont certains tomes sont particuliĂšrement orientĂ©s XAML ou MVVM.

Bref, il y a de quoi lire. Et surtout de quoi réfléchir


Stay Tuned !