Visitor - MultiDispatch

Petite mise à nue du pattern Visitor, Si vous avez déjà utilisé ce pattern (Visitor) vous vous êtes sans doute demandé comment on est rendu à un pattern aussi compliqué? Déjà un petit exemple, ainsi que la définition du pattern.
Represent an operation to be performed on elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.
En clair, on veux séparer les données de leurs traitements afin de pouvoir réaliser plusieurs traitementd différents sans avoir à retoucher aux données Nous prendrons pour exemple un jeu de cartes distribuées par un dealer.
#include <string> #include <iostream> struct Carte{ std::string couleur_; Carte(std::string couleur) : couleur_(couleur){} virtual std::string description(){ return couleur_; } }; struct Roi : public Carte{ Roi(std::string couleur) : Carte(couleur){} std::string description(){ return "roi de " + Carte::description(); } };
Ici, nous avons notre structure de données, un jeu de cartes composée de cartes standards avec une couleur (pique, cœur, carreau, trèfle) et des cartes spéciales, les rois.
int main(){ Carte *roi = new Roi("pique"); std::cout << roi->description() << std::endl; return 0; }
Nous affiche:
roi de pique
Jusque là, rien de bien compliqué, c'est du polymorphisme standard. Maintenant, en essayant de rajouter un Dealer qui s'occupera d'annoncer les cartes:
struct Dealer{ void annoncer(Carte& carte){ std::cout << "Dealer: " << carte.description() << std::endl; } void annoncer(Roi& carte){ std::cout << "Dealer: oh, un roi! (" << carte.description() << ")" << std::endl; } }; int main(){ Carte *roi = new Roi("pique"); Dealer d; d.annoncer(*roi); return 0; }
Nous obtenons:
Dealer: roi de pique
Ici, on voit directement le problème, le Dealer n'a pas repéré que la carte était un roi (problématique si il est payé par le casino pour compter les cartes ...) Pourquoi cela? C'est assez simple à comprendre, et si vous avez lu le titre vous saurez que ça a un rapport avec le MultiDispatch.
Je vais casser vos rêves, mais dans les méthodes on utilise le type déclaré des paramètres et non le type effectif (il parait que c'est sale sinon...) Comment faire pour obtenir un Dealer qui compte les cartes alors? C'est simple il suffit de lui donner en paramètre un pointeur de type déclaré "(Roi*)"...
... et où est-ce qu'on trouve ça? Je vous laisse chercher : p (pas le droit à l'introspection)
Petit indice: on cherche un pointeur de type (Roi*), il serait logique de penser que les Roi savent qu'ils sont rois, et si vous avez lu le titre vous savez qu'on va parler du pattern Visitor.
J'admets, ce pointeur est un peu caché, il s'agit de "this", dans la classe Roi, "this" est de type (Roi*) donc, comment donner ce pointeur en paramètre de la méthode annoncer? Comme souvent en design pattern, il faut faire de l'inversion de contrôle, c'est-à-dire que le dealer n'annonce plus les cartes, mais les cartes se font annoncer par le dealer.
#include <string> #include <iostream> struct Dealer; struct Carte{ std::string couleur_; Carte(std::string couleur) : couleur_(couleur){} virtual std::string description(){ return couleur_; } virtual void seFaireAnnoncer(Dealer& d); }; struct Roi : public Carte{ Roi(std::string couleur) : Carte(couleur){} std::string description(){ return "roi de " + Carte::description(); } void seFaireAnnoncer(Dealer& d); }; struct Dealer{ void annoncer(Carte& carte){ std::cout << "Dealer: " << carte.description() << std::endl; } void annoncer(Roi& carte){ std::cout << "Dealer: oh, un roi! (" << carte.description() << ")" << std::endl; } }; void Carte::seFaireAnnoncer(Dealer& d){ d.annoncer(*this); } void Roi::seFaireAnnoncer(Dealer& d){ d.annoncer(*this); } int main(){ Carte *roi = new Roi("pique"); Dealer d; d.annoncer(*roi); roi->seFaireAnnoncer(d); }
Et on obtient le résultat voulu:
Dealer: roi de pique Dealer: oh, un roi! (roi de pique)
Nous avons par contre introduit quelques problèmes de dépendances (les Rois doivent connaître le Dealer et le Dealer les Rois) que vous résoudrez vous même avec un code bien construit (des .h et des .cpp séparés ainsi que des classes abstraites). Voilà, maintenant le Visitor Pattern n'a plus de secret pour vous, il ne reste plus qu'à renommer "seFaireAnnoncer" en "accept" et "annoncer" en "visit" et vous avez l'exemple standard du pattern.
Réferences:
http://www.informatix.fr/tutoriels/conception/le-design-pattern-double-d...
http://www.informatix.fr/tutoriels/conception/le-design-pattern-visiteur...