Design Pattern : ETATS

Lorsque les if  et les switchs de manière récurrent, c’est souvent qu’une condition, ou un état, est au centre du fonctionnement d’une application. C’est là que peut intervenir le State Pattern.

Le State Pattern est un patron de conception du type Comportement : il se centre donc sur une mécanique de fonctionnement. Son but : faciliter le fonctionnement d’une application centrée sur le changement d’état d’un élément central.

L’élément central sera le seul à être manipulé. Simplement, son comportement ne sera pas le même selon l’état dans lequel il se trouve. Typiquement, l’idée est qu’en apellant Element.DoSomething(), le résultat attendu ne sera pas le même selon l’état dans lequel il se trouve.

Implementation simple

Le Context

Dans ce design Pattern, l’application utilisera un contexte d’execution de l’élément changeant d’état. Le Context se chargera donc de manipuler l’objet dont l’état changera au fur et à mesure de son fonctionnement ou du temps. Toutefois, l’intérêt est avant tout que le contexte n’agira pas sur différent objet, mais sur un seul.

L’abstraction State

Une classe abstraite State déclarera les membres initiaux utilisés quelque soit l’état dans lequel se situera l’élément principal. Ce dernier sera d’ailleurs déclaré comme du type State, simplement l’instanciation se fera à partir d’un état donné

Les états StateX

Enfin, les états viendront surcharger les membres abstraits de la classe State dont ils dérivent et définir plus précisément l’action relative à leur propre état.

Schéma

Dans ce schéma, on voit bien les class d’états ConcreteStateA et ConcreteStateB dérivés de la classe abstraite State. Le Contexte manipule l’element principal State. L’application appellera ce contexte d’exécution.

Exemple

Partons sur un programme d’exemple : le but de ce dernier, pas trop sérieux, serait de simuler de manière simpliste les états d’un humain et leur influence sur l’alimentation.

Prenons une classe HumanState, dont dérive ChildState, TeenagerState et AdultState.
Chacunes de ces classes dérivées implémente la méthode public abstract void FeedMe() et la propriété Protected CaloriesAdded ; enfin un bool Adult viendrait indiqué si le dernier état est renseigné.

La classe FeedContext viendra se charger d’agir sur la propriété HumanState MyHuman. A l’intérieur, on viendra apellé FeedMe() pour nourrir MyHuman.

Si l’on regarde l’implémentation de chaque classe dérivée :

//ChildwoodState
public override void FeedMe(){   
   CaloriesAdded =400;
   Console.WriteLine("I ate fries with steack.\n"
   +CaloriesAdded+" Calories added.\n");
}

//TeenageState
public override void FeedMe(){
   CaloriesAdded =1000;
   Console.WriteLine("I ate Hambuger with fries with icecream.\n"
   +CaloriesAdded+" Calories added.\n");
}

//AdultState
public override void FeedMe(){
   Console.WriteLine("I ate vegetables and not too much meat 
   because greta make me scared.\nMy life is boring.");
   Adult = true;
}

Du côté FoodContext on n’aurait qu’un appel à MyHuman.FeedMe().
Simplement, le reste de l’application (on doit imaginer un jeu de gestion d’humain, du type Sims) se chargerait à un moment de changer l’état de l’humain.

FoodContext foodContext = new FoodContext();

//Dans FoodCOntext : MyHuman = new ChildwoodState();
feedContext.MyHuman.FeedMe();

//Evenement lors du jeu : foodContext.MyHuman = new TeenageState();

MyHuman.FeedMe();

//Evenement lors du jeu : foodContext.MyHuman = new AdultState();

MyHuman.FeedMe();

Des evenement extérieur viendrait donc changer l’état de l’élément central, tandis que le contexte d’execution de ce dernier ne changerait pas. Ainsi, pour notre exemple s’afficherait :

I ate fries with steack. 
400 Calories added.

I ate Hambuger with fries with icecream.
1000 Calories added.

I ate vegetable with meat's piece.
700 Calories added.

De ce fait, les classes dérivées n’auraient comme but que de spécifier les actions et comportements liés à l’état en question.
Le contexte quant à lui intéragirait sur le fonctionnement global de l’élément central (FeedMe, Cook, Drink etc.).
Et dans le reste de l’application, ou depuis le contexte, on pourrait modifier l’état de l’élément à manipuler.

Et pourquoi pas depuis l’état en question ?

En effet, on peut très bien considérer que l’exécution d’un état pourrait déterminer si un autre état doit lui succéder (exemple : un état météorologique « Pluie », pourrait, en fonction de la densité des nuages actuels, considérer qu’il faut passer à un nouvel état « CielDegagé »).

Du coup, une classe dérivée modifierait elle-même l’état de l’objet déclaré via lequel elle a été apellé :

//ChildwoodState
public override void FeedMe(FeedContext context){ 
   CaloriesAdded +=400;
   Console.WriteLine("I ate fries with steack.\n"
   +CaloriesAdded+" total calories.\n");

   if(CaloriesAdded>=10000)
      context.MyHuman = new TeenageState();
}

//TeenageState
public override void FeedMe(FeedContext context){ 
   CaloriesAdded +=1000;
   Console.WriteLine("I ate Hambuger with fries with icecream.\n"
   +CaloriesAdded+" total calories.\n");

   if(CaloriesAdded>=10000)
      context.MyHuman = new AdultState();
}

De cette façon, le comportement de succession des états serait contenu dans le Comportement du design pattern, permettant au contexte d’execution des Etats de fonctionner indépendamment du reste de l’application.

public FeedContext()
{
   MyHuman = new ChildwoodState();

   while (MyHuman.Adult != true)
      MyHuman.FeedMe(this);
}

I ate fries with steack.
400 total calories.

I ate fries with steack.
800 total calories.

I ate fries with steack.
1200 total calories.

I ate fries with steack.
1600 total calories.

I ate fries with steack.
2000 total calories.

I ate Hambuger with fries with icecream.
1000 total calories.

I ate Hambuger with fries with icecream.
2000 total calories.

I ate Hambuger with fries with icecream.
3000 total calories.

I ate Hambuger with fries with icecream.
4000 total calories.

I ate Hambuger with fries with icecream.
5000 total calories.

I ate vegetables and not too much meat because greta make me scared.
My life is boring.

 

Sources :
  • https://fr.wikipedia.org/wiki/%C3%89tat_(patron_de_conception)
  • https://en.wikipedia.org/wiki/State_pattern
  • https://e-naxos.com/Blog/post/Design-Patterns-ou-quand-comment-et-pourquoi-.aspx

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *