IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

Implémentation alternative d'un visiteur


Sujet :

C++

  1. #1
    Membre régulier
    Profil pro
    Inscrit en
    Août 2006
    Messages
    199
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 199
    Points : 116
    Points
    116
    Par défaut Implémentation alternative d'un visiteur
    Bonjour à tous,

    J'étais sur le point d'utiliser la version acyclique du design pattern visitor (la version générique proposée par alexandrescu dans "Modern C++ Design-Generic Programming and Design Patterns Applied") cependant comme il ne me convient pas à 100% je cherche quand même une implémentation alternative.

    Avec la version d'alexandrescu (et d'ailleurs avec la plupart des implémentation du DP visitor) je fais :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    maClasse->Accept(monVisitor);
    La problématique étant d'appeler le bon visiteur sur la classe malgré l'utilisation du polymorphisme et la résolution statique des paramètres de fonction.

    Je me demandais donc si je pouvais me passer du double dispatching en récupérant le bon visiteur via une factory, ie. en demandant à l'instance maClasse un identifiant (ex un string donnant le nom de sa classe) qui serait associé dans une std::map à un visiteur.

    En fait ma question n'est pas tant de savoir si c'est faisable mais plutôt si selon vous c'est valable d'un point de vue temps de traitement par rapport au DP visitor acylique qui fait un dynamic_cast et utilise le double dispatching (=> plusieurs appels de fonctions)?

    D'avance merci

  2. #2
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Salut,

    Une chose est sure, c'est qu'il n'est vraiment pas intéressant d'utiliser une comparaison de chaine de caractères pour travailler: c'est une comparaison qui prend énormément de temps (car, si le seul caractère différent entre deux chaines est le dernier, la comparaison passera par... tous les caractères)

    Une deuxième chose qui est sure, c'est que, si tu veux éviter le double dispatch, tu en arrivera à une situation "bancale" dans laquelle:
    1. tu donnera trop de responsabilité à ta classe "visitée"
    2. tu introduira des dépendances importantes et "mal venues" entre ta classe "visitée" et la classe "visiteuse"
    3. tu multipliera les fonctions dans la classe "visitée", avec, comme résultat, l'obligation de modifier toute la hiérarchie si, d'aventure, tu veux rajouter une manière de la visiter supplémentaire, en contradiction complète avec le principe "Open closed code" (un code doit être "ouvert" à l'évolution, mais "fermé" à la modification)
    Bref, tu l'auras compris: il serait possible d'envisager quelque chose, mais cela me choque à ce point que je n'ai pas envie d'en parler
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  3. #3
    Membre régulier
    Profil pro
    Inscrit en
    Août 2006
    Messages
    199
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 199
    Points : 116
    Points
    116
    Par défaut
    Merci pour ta réponse koala01 mais je ne vois pas pourquoi ma proposition d'implémentation va entraîner les 3 défauts que tu cite.
    En effet, mon but est tout le contraire c'est à dire que ma classe visitée ne sache même pas qu'elle va l'être et donc par suite ne connaisse rien de son(ses) visiteur(s).
    Pour moi ça se déroulerait comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    MaClasseVisitee mcv;
    maFactoryDeVisiteur.visite(mcv);
    et au sein de la fonction "visite()" on aurait :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    std::string id = classeVisitee.getId();
    //création d'une instance du visiteur adéquate via une map associant les identifiants et les visiteurs
    //Visite de la classe
    Ma classe visitée n'a donc nulle besoin d'implémenter la fonction "accept()" et ne connait rien de la classe qui la visite.

  4. #4
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Le fait est que, si tu base, d'une manière ou d'une autre, le choix de la méthode de visite de ton objet sur le résultat d'une fonction membre de celui-ci, tu va à l'encontre de l' Open Close Principle.

    En effet, tu en arrivera, au mieux (si la valeur renvoyée par getId(), selon ton exemple, renvoie une valeur numérique) à quelque chose comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    switch(ref.getId())
    {
        case 1:
            /*ce qu'il faut faire */
            break;
        /*...*/
        case n: 
            /* ce qu'il faut faire  */
            break;
    }
    qui pourra encore être relativement optimisé en utilisant un tableau d'adresses pour le branchement conditionnel, et, au pire, si tu veux réellement renvoyer une chaine de caractères avec getId() quelque chose proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    std::string str=ref.getId();
    if(str=="valeur 1")
        /* ce qu'il faut faire */
    /*...*/
    else if(str=="valeur n")
        /* ce qu'il faut faire */
    qui:
    - n'aura aucune chance d'être optimisé aussi bien qu'un switch case pourrait l'être

    - sera, par nature, moins efficace qu'une comparaison de valeur numérique, étant donné la méthode de comparaison des chaines de caractères, surtout si les chaines de caractères ont de nombreuses premières lettres identiques.

    Le Open Close Principle serait baffoué dans le sens où, si tu viens plus tard à rajouter un type susceptible d'être visité, tu sera dans l'obligation de modifier la fonction qui sélectionne le visiteur, ce qu'il est préférable d'éviter.

    Tu aurais enfin la possibilité de travailler avec une std::map, dont la clé serait la valeur renvoyée par getId().

    Cependant, là encore, il faut prendre en compte que la complexité de la recherche dans un tableau associatif est, au mieux, en O(log n).

    Outre le problème relatif au type manipulé comme clé, tu te trouverais encore une fois devant l'obligation de modifier du code, même si c'est sans doute une autre fonction que tu devrais modifier (celle qui ... permet à la std::map de contenir les différents visiteurs)

    Tout cela pour dire que, en toute circonstance, le double dispatch reste la "moins mauvaise" solution: tu ne dois modifier aucune fonction existante, mais tu peux éventuellement rajouter une fonction surchargée.

    Tout code utilisant une "ancienne version" de ton travail pourra sans encombre continuer à travailler avec la "nouvelle version" qui permet de manipuler de nouveaux types, même si c'est au prix de... la non prise en compte des nouveaux types (qui n'existent de toutes façons pas dans l'ancienne version)
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  5. #5
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Salut,
    Ta question de départ était de savoir si ce serait moins 'couteux'.
    Avec le visiteur classique, tu as accès à 2 vtables et 2 appels de fonctions.
    Avec ta méthode associative, tu as un tableau associatif, un appel pour retrouver l'association et un appel vers la bonne fonction.
    Au final, je ne pense pas que tu ne gagnes grand chose.

  6. #6
    Membre régulier
    Profil pro
    Inscrit en
    Août 2006
    Messages
    199
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 199
    Points : 116
    Points
    116
    Par défaut
    Ok merci pour ces réponses, je vais en rester au visiteur acyclique.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. alterner les couleurs dans un tableau avec xsl
    Par Eithelgul dans le forum XSL/XSLT/XPATH
    Réponses: 14
    Dernier message: 03/05/2015, 23h29
  2. Réponses: 11
    Dernier message: 04/03/2011, 05h04
  3. Réponses: 3
    Dernier message: 12/01/2011, 14h10
  4. Réponses: 1
    Dernier message: 20/07/2007, 10h05
  5. Implémentation des fonctions mathématiques
    Par mat.M dans le forum Mathématiques
    Réponses: 9
    Dernier message: 17/06/2002, 16h19

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo