/** * Interface Context */interfaceContext{// write a value in memorypublicfunctionwrite($name,$value);// get a value from the memorypublicfunctionread($name);//return all the valuepublicfunctiongetAll()}/** * A Memory */classMemoryimplementsContext{private$memory=array();// write a value in memorypublicfunctionwrite($name,$value){$this->memory[$name]=$value;return$this;}// get a value from the memorypublicfunctionread($name){return$this->memory[$name];}publicfunctiongetAll(){return$this->memory;}}
Il ne nous reste plus qu’à implémenter la variable.
On peux rajouter plein d’autre expression. L’avantage est qu’il suffit de rajouter une méthode ->interpret(..) pour chaque objet.
mais si on change le cahier des charges…
Changeons le cahier des charges. Je souhaite transformer mon Expression en chaine de caractères. Je peux m’en sortir en surchargeant la méthode __tostring
Par exemple :
12
$expression=newAddition(newAddition(newConstant(3),newConstant(4)),newConstante(4));$expression->__toString()// me donne ((3 + 4) + 4);
12345678910
// pour la constantepublicfunction__toString(){return$this->value;}// pour l'additionpublicfunction__toString(){// this->left->__toString()return'('.$this->left.' + '.$this->right.')';}
Rechangeons le cahier des charges : je veux la traduction en Php
12
$expression=newAddition(newAddition(newVariable('i'),newConstant(4)),newConstante(4));$expression->__toPhp()// me donne (($i + 4) + 4);
je suis un peu bloqué, je dois rajouter à chaque fois une méthode dans chaque Object. Je perd un peu de la simplicité du pattern..
Visiteur Pattern à la rescousse !
Je vais définir une méthode accept(Visitor $visitor)
en pratique. On appelle la méthode accept. Celle-ci appelle la methode visit($this). la méthode visit détermine la fonction à appeller.
Si c’est une constante alors visistConstant() celle-ci résout la valeur. pour une addition c’est un plus compliqué on ré-appelle récursivement accept sur chaque partie de l’addition.
Voici comment s’en servir
123456789
// j'ai besoin d'une mémoire$memory=newMemory();$memory->write('i',10);// j'ai besoin d'un visiteur$ve=newVisitorEvaluation($memory);// une expression$expression=newAddition(newConstant(10),newVariable('i'));// appelle le visiteurecho$expression->accept($ve);// 20
On se rend compte qu’il n’y a plus de logique dans mes objet. Tout est sous-traité dans le visiteur.
L’avantage de cette méthode est qu’il est très simple de changer le visiteur sans changer la logique.
$memory=newMemory();$memory->write('i',10);$ve=newVisitorEvaluation($memory);// une expression$expression=newAddition(newConstant(10),newVariable('i'));// appelle le visiteur evaluation simpleecho$expression->accept($ve);// 20// evaluation conversion php $php=newVisitorToPhp($memory);echo$expression->accept($php);// (10 + $i)$js=newVisitorToJs($memory);echo$expression->accept($js);// (10 + i)// j'ai rajouté une méthode translate qui est un raccourciecho$php->translate($expression);// $i = 10;echo(10+$i)echo$js->translate($expression);// var i = 10;console.log(10+i)
Les limitations du visiteur pattern
toute la logique est sur le visiteur. s’il y a un beaucoup de type d’expression (dans notre cas Addition, Constant, Variable, Abso, Multiplication ..) c’est autant de ligne à rajouter dans celui-ci.
rajouter un type, oblige à le ré-implementer partout.
Les avantages du visiteur pattern.
On peut parfaitement imaginer un type document, et lui ajouter un visiteur toJson, toPdf, toEbook, toHtml. sans jamais changer le modèle.
Nous continuerons avec le visiteur pattern dans un prochain post. Nous ajouterons un visiteur pour les expressions booléenes. puis nous ajouterons un visiteur pour des instructions. nous allons créer un mini-langage..
Ce projet vient des notes que j’avais prise quand j’étais au CNAM sur le cours de Design-Pattern en Java. J’avais adoré!