Un modèle de conception est un modèle qui résout un problème récurrent dans la conception de logiciels.

Le modèle d'état est un modèle de comportement qui permet à un objet de modifier son comportement lorsque son état interne change.

Ici, vous apprendrez à utiliser le modèle d'état dans TypeScript.

Qu'est-ce que le modèle d'état ?

Le modèle de conception d'état est étroitement lié à une machine à états finis, qui décrit un programme qui existe dans un fini nombre d'états à un moment donné et se comporte différemment au sein de chaque état.

Il existe des règles limitées et prédéterminées - les transitions - qui régissent les autres états vers lesquels chaque état peut basculer.

Pour le contexte, dans une boutique en ligne, si la commande d'un client a été "livrée", elle ne peut pas être "annulée" car elle a déjà été "livrée". "Livré" et "Annulé" sont des états finis de la commande, et la commande se comportera différemment en fonction de son état.

Le modèle d'état crée une classe pour chaque état possible, avec un comportement spécifique à l'état contenu dans chaque classe.

instagram viewer

Un exemple d'application basée sur l'état

Par exemple, supposons que vous créez une application qui suit les états d'un article pour une maison d'édition. Un article peut être en attente d'approbation, rédigé par un écrivain, édité par un éditeur ou publié. Ce sont les états finis d'un article à publier; dans chaque état unique, l'article se comporte différemment.

Vous pouvez visualiser les différents états et transitions de l'application de l'article avec le diagramme d'état ci-dessous :

Pour implémenter ce scénario dans le code, vous devez d'abord déclarer une interface pour l'article :

interfaceInterface de l'article{
terrain(): annuler;
brouillon(): annuler;
modifier(): annuler;
publier(): annuler;
}

Cette interface aura tous les états possibles de l'application.

Ensuite, créez une application qui implémente toutes les méthodes d'interface :

// Application
classeArticlemet en oeuvreInterface de l'article{
constructeur() {
ce.showCurrentState();
}

privéshowCurrentState(): annuler{
//...
}

publicterrain(): annuler{
//...
}

publicbrouillon(): annuler{
//...
}

publicmodifier(): annuler{
//...
}

publicpublier(): annuler{
//...
}
}

Le privé showCurrentState est une méthode utilitaire. Ce didacticiel l'utilise pour montrer ce qui se passe dans chaque état. Ce n'est pas une partie obligatoire du modèle d'état.

Gestion des transitions d'état

Ensuite, vous devrez gérer les transitions d'état. La gestion de la transition d'état dans votre classe d'application nécessiterait de nombreuses expressions conditionnelles. Cela entraînerait un code répétitif plus difficile à lire et à maintenir. Pour résoudre ce problème, vous pouvez déléguer la logique de transition pour chaque état à sa propre classe.

Avant d'écrire chaque classe d'état, vous devez créer une classe de base abstraite pour vous assurer que toute méthode appelée dans un état invalide génère une erreur.

Par exemple:

abstraitclasseÉtat de l'articlemet en oeuvreInterface de l'article{
pas (): ArticleState {
lancernouveauErreur("Opération non valide: impossible d'exécuter la tâche dans état actuel");
}

brouillon(): ÉtatArticle {
lancernouveauErreur("Opération non valide: impossible d'exécuter la tâche dans état actuel");
}

edit(): ÉtatArticle {
lancernouveauErreur("Opération non valide: impossible d'exécuter la tâche dans état actuel");
}

publier (): ArticleState {
lancernouveauErreur("Opération non valide: impossible d'exécuter la tâche dans état actuel");
}
}

Dans la classe de base ci-dessus, chaque méthode génère une erreur. Maintenant, vous devez remplacer chaque méthode en créant des classes spécifiques qui s'étend la classe de base pour chaque état. Chaque classe spécifique contiendra une logique spécifique à l'état.

Chaque application a un état inactif, qui initialise l'application. L'état d'inactivité de cette application définira l'application sur le brouillon État.

Par exemple:

classeÉtat de projet en attentes'étendÉtat de l'article{
pas (): ArticleState {
retournouveau ÉtatBrouillon();
}
}

Le terrain La méthode de la classe ci-dessus initialise l'application en définissant l'état actuel sur BrouillonÉtat.

Ensuite, remplacez le reste des méthodes comme suit :

classeBrouillonÉtats'étendÉtat de l'article{
brouillon(): ÉtatArticle {
retournouveau EditingState();
}
}

Ce code remplace le brouillon méthode et renvoie une instance de la EditingState.

classeEditingStates'étendÉtat de l'article{
edit(): ÉtatArticle {
retournouveau ÉtatPublié();
}
}

Le bloc de code ci-dessus remplace le modifier méthode et renvoie une instance de État publié.

classeÉtat publiés'étendÉtat de l'article{
publier (): ArticleState {
retournouveau En attenteDraftState();
}
}

Le bloc de code ci-dessus remplace le publier méthode et remet l'application dans son état inactif, État de projet en attente.

Ensuite, vous devez autoriser l'application à modifier son état en interne en référençant l'état actuel via une variable privée. Vous pouvez le faire en initialisant l'état d'inactivité dans votre classe d'application et en stockant la valeur dans une variable privée :

privé état: ÉtatArticle = nouveau En attenteDraftState();

Ensuite, mettez à jour le showCurrentState méthode pour imprimer la valeur de l'état actuel :

privéshowCurrentState(): annuler{
console.enregistrer(ce.État);
}

Le showCurrentState La méthode enregistre l'état actuel de l'application dans la console.

Enfin, réaffectez la variable privée à l'instance d'état actuelle dans chacune des méthodes de votre application.

Par exemple, mettez à jour vos applications terrain méthode au bloc de code ci-dessous :

publicterrain(): annuler{
ce.état = ce.state.pitch();
ce.showCurrentState();
}

Dans le bloc de code ci-dessus, le terrain La méthode change l'état de l'état actuel à l'état de hauteur.

De même, toutes les autres méthodes changeront l'état de l'état actuel de l'application à leurs états respectifs.

Mettez à jour vos méthodes d'application avec les blocs de code ci-dessous :

Le brouillon méthode:

publicbrouillon(): annuler{
ce.état = ce.state.draft();
ce.showCurrentState();
}

Le modifier méthode:

publicmodifier(): annuler{
ce.état = ce.state.edit();
ce.showCurrentState();
}

Et le publier méthode:

publicpublier(): annuler{
ce.état = ce.state.publish();
ce.showCurrentState();
}

Utilisation de l'application terminée

Votre classe d'application terminée doit être similaire au bloc de code ci-dessous :

// Application
classeArticlemet en oeuvreInterface de l'article{
privé état: ÉtatArticle = nouveau En attenteDraftState();

constructeur() {
ce.showCurrentState();
}

privéshowCurrentState(): annuler{
console.enregistrer(ce.État);
}

publicterrain(): annuler{
ce.état = ce.state.pitch();
ce.showCurrentState();
}

publicbrouillon(): annuler{
ce.état = ce.state.draft();
ce.showCurrentState();
}

publicmodifier(): annuler{
ce.état = ce.state.edit();
ce.showCurrentState();
}

publicpublier(): annuler{
ce.état = ce.state.publish();
ce.showCurrentState();
}
}

Vous pouvez tester les transitions d'état en appelant les méthodes dans le bon ordre. Par exemple:

constante docs = nouveau Article(); // État du brouillon en attente: {}

docs. pitch(); // État du brouillon: {}
docs.draft(); // ÉtatÉdition: {}
docs.edit(); // État publié: {}
docs.publish(); // État du brouillon en attente: {}

Le bloc de code ci-dessus fonctionne car les états de l'application ont été transférés de manière appropriée.

Si vous essayez de changer l'état d'une manière qui n'est pas autorisée, par exemple, de l'état de hauteur à l'état d'édition, l'application génère une erreur :

constante docs = nouveau Article(); // État du brouillon en attente: {}
docs. pitch() // État du brouillon: {}
docs.edit() // Opération invalide: impossible d'exécuter la tâche dans l'état actuel

Vous ne devez utiliser ce modèle que lorsque :

  • Vous créez un objet qui se comporte différemment selon son état actuel.
  • L'objet a plusieurs états.
  • Le comportement spécifique à l'état change fréquemment.

Avantages et compromis du modèle d'État

Ce modèle élimine les instructions conditionnelles volumineuses et maintient la responsabilité unique et les principes ouverts/fermés. Mais cela peut être exagéré si l'application a peu d'états ou si ses états ne sont pas particulièrement dynamiques.