Sans m'étendre sur les bienfaits du Visitor pattern, dont le but est de séparer un algorithme de sa structure de données, je souhaite proposer ici une implémentation paresseuse mais possible en PHP.
En théorie ce pattern repose sur 2 interfaces qui permettent de faire un double dispatch (appeler depuis un objets visitable la fonction lui correspondant dans le visiteur) :
- Visitable qui contient une fonction apply(Visitor $v)
- Visitor qui contient une fonction visit(Visitable $v)
Puisqu'en PHP il n'est pas possible de faire de la surcharge de paramètres de fonction (polymorphisme), il faut donc appeler les fonctions différemment selon les types de structure de données.
En Java :
interface Visitor {
void visit(Wheel wheel);
void visit(Engine engine);
void visit(Body body);
void visit(Car car);
}
abstract class Visitable {
void apply(Visitor visitor) {
visitor.apply(this);
}
}
En PHP :
interface Visitor {
public function visitWheel($wheel);
public function visitEngine($engine);
public function visitBody($body);
public function visitCar($car);
}
interface Visitable {
public function apply(Visitor $visitor);
}
On remarque donc qu'en PHP on devra réécrire toutes les fonctions
apply pour qu'elles appellent la fonction
visit___ correspondant à leur type.
Mais paresseux comme je suis, la redéfinition de la fonction
apply pour chaque
visitable me parait un tâche trop répétitive et ennuyeuse. L'idée sous-jacente est de faire un
dispatch dynamique en fonction du type du
visitable, de la façon suivante :
abstract class Visitable {
public function visit(Visitable $visitable)
{
$func = array(&$this, 'visit' . get_class($visitable));
if (!is_callable($func)) {
trigger_error('[' . get_class($this) . '] Class ' . get_class($this) . ' has no visit' . get_class($visitable) . ' function', E_USER_ERROR);
}
call_user_func($func, $visitable);
}
}
Ainsi le visiteur devrait implémenter les fonctions standards
visitWheel,
visitEngine, ... mais leur
dispatching devient automatique !
Mis à part la lourdeur de la fonction
call_user_func, le reste de cette implémentation dynamique reste intéressante.
Références :
http://en.wikipedia.org/wiki/Visitor_pattern
http://en.wikipedia.org/wiki/Double_dispatch#Double_dispatch_is_more_than_function_overloading
Plus d'information pour les intéressés :
http://www.polyglotinc.com/reflection.html
http://www.javaworld.com/javaworld/javatips/jw-javatip98.html