Des objets dans tous leurs états

On a déjà tous rencontrés des projets, ayant un lourd passif, aux règles métiers complexes souvent non documentés, composés de plusieurs grosses classes ou fichiers aux milliers de lignes. Oui, ce genre de projet où, du moins pour le commun des mortels, c’est uniquement grâce à de la magie noire qu’il est encore debout.

La complexité apparente de ce code est notamment induite par des composants à rôles multiples, à comportement multiples, incluant de nombreux tests imbriqués, à exécuter en fonction de différents états des composants mis en scène.

Ce sont des productions peu maintenable: compléter ou modifier son comportement est une opération sensible. Elles sont difficiles à tester, de très nombreux cas à tester.


Patron Etat

Il existe un patron de conception qui permet d’isoler chaque comportement dans une section dédiée tout en permettant aux objets d’adopter un comportement désiré, ainsi que d’en changer durant l’exécution. C’est le patron “Etat”. [Lire l’article sur Wikipedia]


Prenons comme exemple une personne bilingue, parlant le français et l’anglais. Elle doit, en fonction du pays où elle se situe, donner dans la bonne langue son état civil.

Ci-dessus, en bleu le code responsable du comportement français, en rouge le comportement anglais. Ils sont répartis dans chacune des méthodes de notre objet. En rose celui de la sélection du comportement adéquate.


Appliquons maintenant le patron “Etat” pour regrouper les sorties françaises et anglaises dans deux états différents. Les méthodes hello() et sayBirthday() feront appel directement à ces états.

Les deux comportements ont été isolés dans deux sections dédiées. Elles sont invisibles pour les objets externes, qui dialoguent toujours avec notre objet Person. C’est ce dernier qui, en fonction du contexte va transférer l’appel à l’état actif.

La sélection des états actifs n’est pas propre à la classe gérant le contexte (Ici la classe Person). Les états peuvent être activés soit :

  • Via un appel externe explicite à l’objet Person.
  • Au début d’un appel à une méthode de l’objet Person.
  • Lors de l’exécution d’une méthode de l’un de ses états.
  • Automatiquement en fonction des valeurs d’attributs de notre objet Person.

Dans notre exemple, à chaque appel, l’objet Person va déterminer l’état courant en fonction du premier paramètre.

Astuce

Un des effets de bord avec ce patron de conception, c’est qu’il permet de réaliser du code “defective by design”. Nous pouvons ajouter une méthode au comportement anglais, pour savoir s’il fait beau (car c’est bien entendu qu’il fait toujours beau en France).
Appeler cette méthode en France provoquera une erreur, qui interrompra l’opération, mais qui se déroulera parfaitement en Angleterre. Dans notre exemple, cela n’est pas trivial. Mais pour implémenter un système de droits utilisateurs, l’approche peut-être intéressante.

Dans le prochain article je vous présenterais l’utilisation de la bibliothèque States permettant d’implémenter ce design pattern en PHP.