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 :

Les accesseurs et les détails d'implémentation [Tutoriel]


Sujet :

C++

Vue hybride

gbdivers Les accesseurs et les détails... 18/06/2012, 10h25
koala01 Salut, Ce n'est qu'une... 18/06/2012, 11h20
gbdivers Pour la sémantique de valeur,... 18/06/2012, 11h42
koala01 C'est louable, mais le... 18/06/2012, 19h51
gbdivers @koala01 Je me doutais bien... 19/06/2012, 00h55
oodini Je n'ai absolument jamais vu... 18/06/2012, 12h36
gbdivers Une classe Voiture aurait... 18/06/2012, 12h53
oodini Non. Si tu as un polygone... 18/06/2012, 13h20
cob59 Je ne suis pas mécontent de... 18/06/2012, 11h47
gbdivers Oui et non. Très clairement,... 18/06/2012, 11h59
Ekleog Dans gl_traits, tu utilises... 18/06/2012, 12h14
gbdivers Corrigé Pour le internal en... 18/06/2012, 12h28
Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Par défaut Les accesseurs et les détails d'implémentation
    Bonjour à tous

    C'est une discussion qui revient régulièrement sur le chat de Developpez.com. Une personne demande comment fait-on pour accéder aux variables membres privées d'une classe et on lui répond de créer des getter et setter. Viens alors un C++ien moyen (c'est-à-dire un casse-pied, en général moi) qui hurle au scandale et sort l'adage bien connu : "les accesseurs, c'est le mal". S'en suit une discussion sur pourquoi les accesseurs sont à éviter, quand j'ai le temps et l'humeur.
    Dans ce billet, je vais présenter les problèmes que posent les accesseurs concernant l'exposition des détails d'implémentation.

    Les accesseurs et les détails d'implémentation

    Que pensez-vous des problèmes exposés dans cet article et des principes de conception objet ?

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,

    Ce n'est qu'une lecture en diagonale, mais tu pourrais d'ailleurs aussi insister sur le fait que ta classe Point3D présentée dans les premières lignes a, typiquement, sémantique de valeur et que l'idéal serait, au moment où l'on modifie une des coordonnées, de créer un nouvel objet avec les bonnes coordonnées (en gros, que setX(), setY() et setZ() sont encore pis que leurs accesseurs associés)

    Petite faute de grammaire en :
    C'est un problème que connait connaissent beaucoup de débutants
    (ce sont les débutants qui connaissent le problème

    Enfin, mais bon, ce n'est qu'un détail permettant d'avoir une erreur de compilation si la spécialisation de gl_trait n'est pas présente, j'aurais définis les traits sous une forme proche de :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    template<class T> 
    struct gl_trait ; 
     
    template<> 
    struct gl_trait<float> { 
      static inline glVertex  (internal x, internal y, internal z) const { glVertex3f(x, y, z); } 
      static inline glNormal  (internal x, internal y, internal z) const { glNormal3f(x, y, z); } 
      static inline glTexCoord(internal x, internal y, internal z) const { glTexCoord3f(x, y, z); } 
    }; 
     
    template<> 
    struct gl_trait<double> { 
      static inline glVertex  (internal x, internal y, internal z) const { glVertex3d(x, y, z); } 
      static inline glNormal  (internal x, internal y, internal z) const { glNormal3d(x, y, z); } 
      static inline glTexCoord(internal x, internal y, internal z) const { glTexCoord3d(x, y, z); } 
    };
    On perd la valeur par défaut, mais on gagne une erreur de compilation si besoin
    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
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Par défaut
    Pour la sémantique de valeur, j'en parlais initialement, mais j'ai finalement supprimé ce point, pour alléger les explications. Comme je ne fournis pas d'accesseur ni même de constructeur (utilisation des initializer-list) dans les dernières versions, on ne peut pas modifier de point. J'ajoute un commentaire

    Pour la faute, merci

    Pour la classe de traits, c'est parce que j'aime bien donner le prototype de la classe de traits, pour que celui qui veut spécialiser puisse directement le faire sans aller chercher le code d'une autre spécialisation... mais je peux mettre le code en commentaire

    Merci

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par gbdivers Voir le message
    Pour la classe de traits, c'est parce que j'aime bien donner le prototype de la classe de traits, pour que celui qui veut spécialiser puisse directement le faire sans aller chercher le code d'une autre spécialisation...
    C'est louable, mais le problème, c'est que si un utilisateur décide d'écrire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Point3D<unsigned int> monPoint; // ou n'importe quel autre type pour lequel il n'y a pas de spécialisation
    il n'y a strictement rien qui lui fasse remarquer que la spécialisation n'existe pas, surtout que tu fournis un implémentation pour les fonctions

    Si tu ne donne pas de corps aux fonctions de la version non spécialisée, tu retarde la découverte du fait qu'il n'existe pas de spécialisation pour le type en question au moment de... l'édition de liens (qui peut survenir très tard, en fonction du nombre de fichiers à compiler)

    Par contre, si tu te contente carrément d'une déclaration anticipée de ta classe (qui suffit amplement ), l'erreur arrivera dés le premier fichier qui tentera de créer une instance de ton Point3D, et donc, cela occasionnera du gain de temps pour tout le monde

    mais je peux mettre le code en commentaire
    A vrai dire, c'est carrément tout le corps de la classe (accolades ouvrantes et fermantes comprises) que je mettrais en commentaire
    Merci
    de rien
    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
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Par défaut
    @koala01
    Je me doutais bien qu'aborder le problème des sémantiques aller poser des problèmes

    @Ekleog
    Oui, tu as raison, si on modifie qu'un point, on aura une différence importante
    Pour la spécialisation, on peut sans problème ajouter une nouvelle fonction dans dedans

    @oodini
    As tu lu http://blog.emmanueldeloget.com/inde...C3%A9placement ?

  6. #6
    Expert confirmé
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 296
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 296
    Par défaut
    Je crains aussi que la partie sur les sémantiques complexifie le message -- peut-être, j'avoue parce qu'il s'agit d'un angle d'approche que je n'avais pas encore considéré.

    Quand j'aborde le sujet, les points qui me viennent en premier (et qui sont les plus abordables pour faire passer le message à des non théoriciens du C++), c'est la dichotomie penser services vs penser données. Et que donner accès aux données, c'est dé(en)capsuler l'accès à ces dernières.
    J'aime bien prendre des exemples à base de linge à laver/étendre: dans un monde données, on va à la laverie automatique et on fait tout soit même. Dans un monde objet, on demande à un voisin/pressing/mère si on peut lui confier notre linge, et lui se chargera de savoir comment fonctionne sa machine, ou comment mélanger les diverses affaires.

    S'il y a de la théorie qui me vient en tête quand on parle d'accesseurs & cie, c'est la Loi de Déméter qui présente pas mal de similitudes avec ce côté d'encapsulation.
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  7. #7
    Membre éprouvé
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 766
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 766
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Ce n'est qu'une lecture en diagonale, mais tu pourrais d'ailleurs aussi insister sur le fait que ta classe Point3D présentée dans les premières lignes a, typiquement, sémantique de valeur et que l'idéal serait, au moment où l'on modifie une des coordonnées, de créer un nouvel objet avec les bonnes coordonnées
    Je n'ai absolument jamais vu une telle méthode dans du code 3D. Je n'en vois d'ailleurs pas l'intérêt. Si on a une voiture rouge et qu'on souhaite en changer sa couleur, on ne va pas construire une nouvelle voiture uniquement pour satisfaire ce désir de changement de couleur.

  8. #8
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Par défaut
    Citation Envoyé par oodini Voir le message
    Je n'ai absolument jamais vu une telle méthode dans du code 3D. Je n'en vois d'ailleurs pas l'intérêt. Si on a une voiture rouge et qu'on souhaite en changer sa couleur, on ne va pas construire une nouvelle voiture uniquement pour satisfaire ce désir de changement de couleur.
    Une classe Voiture aurait clairement une sémantique d'entité et non de valeur
    Si tu repeints une voiture, tu as toujours la même voiture. Si tu modifies les coordonnées d'un point, tu as bien un point différents.
    Cela à une importance par rapport aux opérateurs et fonctions que l'on peut définir.

    Mais il est vrai que l'on rencontre régulièrement du code qui ne respect pas les sémantiques de valeur ou entité (par exemple une classe avec sémantique de valeur et des fonctions virtuelles)

  9. #9
    Membre éprouvé
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 766
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 766
    Par défaut
    Citation Envoyé par gbdivers Voir le message
    Si tu modifies les coordonnées d'un point, tu as bien un point différents.
    Non. Si tu as un polygone constitué de points, que ceux-ci sont référencés sous forme de tableau, et que tu modifie la valeur d'un des points, cela signifie que tu modifies la position du sommet.

    La problème de la sémantique, c'est que c'est finalement très subjectif.

  10. #10
    Membre émérite
    Avatar de Ekleog
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2012
    Messages
    448
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2012
    Messages : 448
    Par défaut
    Mais lorsque tu modifies la position d'un point d'un polygone, tu obtiens un autre polygone.

    La question est surtout : où est-ce que ça s'arrête ?

  11. #11
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Par défaut
    Citation Envoyé par oodini Voir le message
    La problème de la sémantique, c'est que c'est finalement très subjectif.
    Bof, pas trop je trouve.
    On dit d'une classe qu'elle a une sémantique de valeur si deux objets situés à des adresses différentes, mais au contenu identique, sont considérés égaux.
    A l'inverse des classes à sémantique de valeur, une classe a une sémantique d'entité si toutes les instances de cette classe sont nécessairement deux à deux distinctes, même si tous les champs de ces instances sont égaux.
    On est bien d'accord qui si tu veux tester si 2 polygones sont identiques, tu ne peux pas te contenter de vérifier qu'il ont la même adresse, mais tu dois vérifier que tous les points sont identiques.
    Pour une voiture, même en vérifiant toutes les caractéristiques (modèle, couleur, etc.), ce n'est pas suffisant pour dire que c'est bien la même voiture.
    On a bien une sémantique de valeur dans le premier cas et une sémantique d'entité dans le second.

    La sémantique d'entité/valeur a un intérêt (ne pas faire n'importe quoi avec les classes), mais comme toutes les règles, on peut ne pas les respecter. Le point important, je pense, est qu'on doit le faire en connaissance de cause, pas par méconnaissance des principes (ce qu'on voit souvent)

    Effectivement, il est classique d'avoir un buffer de points en 3D que l'on passe directement au gpu et que donc, pour des raisons de simplification, on va directement modifier les coordonnées des points dans le buffer. Mais c'est clairement un non respect de la sémantique (et du principe d'encapsulation par la même occasion) Et a priori, on pourrait faire correctement en respectant la sémantique (mais je fais pas non plus )

  12. #12
    Membre émérite

    Profil pro
    Inscrit en
    Décembre 2008
    Messages
    533
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2008
    Messages : 533
    Par défaut
    Je ne suis pas mécontent de lire ça.
    J'ai souvent constaté les 2 mêmes écueils chez les développeurs habitués au procédural et qui tentent de faire de l'objet : un bon gros God-Object qui appelle séquentiellement ses propres méthodes, et des get/set sur tout ce qui bouge parce que "ça fait POO".

    En fait, dès qu'une classe présente trop de "propriétés" (ie. des attributs public ou quasi-public via get/set), j'ai tendance à la scinder en deux :
    • Une classe POD qui décrit des propriétés (donc des attributs publics) mais n'a aucun comportement.
    • Une classe qui décrit un comportement et agrège un POD, ou une hiérarchie d'objets basé sur ce POD.


    Exemple : des N-uplets de coordonnées qui décrivent des points, que je passe à un afficheur Drawer, qui manipule ces points.

    A ce propos, dans tes exemples, je me demande si la méthode draw() ne confie pas des responsabilités inappropriées pour une classe Point ?

  13. #13
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Par défaut
    Citation Envoyé par cob59 Voir le message
    A ce propos, dans tes exemples, je me demande si la méthode draw() ne confie pas des responsabilités inappropriées pour une classe Point ?
    Oui et non. Très clairement, il n'est pas approprié qu'une classe Point soit chargée de se dessiner elle même en plus de ses fonctionnalités classiques... mais j'ai pris soins ici de ne pas lui donner d'autres responsabilité que de se dessiner elle même, donc respect du principe de responsabilité unique

    Pour une vraie classe Point3D, il faudrait en effet soit renommer la classe (DrawablePoint ?), soit séparer ce qui correspond réellement à la sémantique d'un point et l'affichage

  14. #14
    Membre émérite
    Avatar de Ekleog
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2012
    Messages
    448
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2012
    Messages : 448
    Par défaut
    pour ceux, qui comme LittleWhite bloquebloquent sur l'utilisation de glVertex
    Dans gl_traits, tu utilises internal comme arguments aux fonctions, et tu l'as mis en commentaire dans la version non spécialisée. D'ailleurs, pourquoi utiliser internal et non directement float/double dans les versions spécialisées ?

    Sinon, intéressant !

  15. #15
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Par défaut
    Citation Envoyé par Ekleog Voir le message
    Dans gl_traits, tu utilises internal comme arguments aux fonctions, et tu l'as mis en commentaire dans la version non spécialisée. D'ailleurs, pourquoi utiliser internal et non directement float/double dans les versions spécialisées ?

    Sinon, intéressant !
    Corrigé
    Pour le internal en commentaire, c'est suite à la remarque de koala01, j'ai mis en commentaire trop vite

    Pourquoi internal plutôt que T/float/double ? Par fainéantise
    Sans typedef, on devrait effectivement utiliser T pour le template non spécialisé et float/double pour les autres
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    template<class T = float> 
    struct gl_trait { 
      // static inline glVertex  (T x, T y, T z) const {} 
    }; 
     
    template<> 
    struct gl_trait<float> { 
      // static inline glVertex  (float x, float y, float z) const { glVertex3f(x, y, z); } 
    };
    comme j'écris ma spécialisation en copiant-collant le code de la version non spécialisée, utiliser internal me permet de modifier moins de code (j'ai juste le contenu de la fonction à modifier, pas sa définition)

Discussions similaires

  1. Réponses: 4
    Dernier message: 11/09/2006, 16h55
  2. Les polices dans les tables et les requêts
    Par zooffy dans le forum Access
    Réponses: 3
    Dernier message: 21/06/2006, 11h06
  3. Outils pour creer les accesseurs et les mutateurs
    Par MarieMtl dans le forum MFC
    Réponses: 3
    Dernier message: 03/10/2005, 17h03

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