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

Langage C++ Discussion :

Remplacer des getter/setter par l'overload de l'opérateur () d'un wrapper


Sujet :

Langage C++

  1. #1
    Membre habitué
    Homme Profil pro
    Doctorant en Astrophysique
    Inscrit en
    Mars 2009
    Messages
    312
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Astrophysique
    Secteur : Enseignement

    Informations forums :
    Inscription : Mars 2009
    Messages : 312
    Points : 176
    Points
    176
    Par défaut Remplacer des getter/setter par l'overload de l'opérateur () d'un wrapper
    Bonjour.

    J'ai déjà posé la question sur SO (http://stackoverflow.com/q/14515224/882932) mais les réponses que j'ai obtenu ne me satisfont pas trop. Pour illustrer le problème je vais prendre un exemple.

    Considérons la classe suivante:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class MyClass1
    {
        public:
            double x() const {return _x;} // getter
            double y() const {return _y;} // getter
            double z() const {return _x*_y;} // getter
            void x(const double var) {_x = var;} // setter
            void y(const double var) {_y = var;} // setter
            void z(const double var) {_x = var; _y = 1;} // setter
        protected:
            double _x;
            double _y;
    };
    D'un point de vue utilisateur, MyClass1 est une classe qui possède 3 propriétés x, y et z interdépendantes qui peuvent être accédées par des getter,et qui peuvent être modifiées par des setter. D'un point de vue de l'utilisateur toujours, le fait qu'en interne il y ait _x, _y mais pas _z est un détail d'implémentation : les getter et les setter permettent de garantir l'accès/la modif comme si on avait toutes les variables en interne.

    La question posée est la suivante: y-a-t-il une façon d'éviter d'avoir à écrire des getter et des setter qui ne font "rien de spécial" comme ceux de x et ceux de y ?

    Une solution que j'ai trouvé consiste à passer par un wrapper et l'overload de l'opérateur ().

    Le wrapper :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    template <typename Type>
    class Wrapper
    {
        public:
            constexpr Wrapper(const Type& value) {_value = value;}
            constexpr Type& operator()() {return _value;}
            constexpr const Type& operator()() const {return _value;}
            constexpr void operator()(const Type& value) {_value = value;}
            constexpr operator Type() const {return _value;}
        protected:
            _value;
    };
    La nouvelle classe:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    class MyClass2
    {
        public:
            Wrapper<double> x;
            Wrapper<double> y;
            double z() const {return x*y;} // getter
            void z(const double var) {x = var; y = 1;} // setter
    };
    L'utilisateur peut toujours utiliser x(), y() et z(), sauf que cette fois je n'ai pas eu à écrire les getter et setter de x et y.

    La question est la suivante : est-ce une pratique "acceptable" ou est-ce à éviter en terme de design (ou encore pire est-ce "dangereux" (et si c'est le cas, pourquoi)) ?

  2. #2
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Je trouve que c'est un design étrange.

    À mon goût, ça a un défaut: x est une variable, pas une fonction. Tu exposes donc directement le champ, et tu ne peux plus avoir de virtualité dessus.

    Par ailleurs, ton Wrapper offre un service en plus: un accesseur en référence non constant.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  3. #3
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut
    Citation Envoyé par Kaluza Voir le message
    Bonjour.

    J'ai déjà posé la question sur SO (http://stackoverflow.com/q/14515224/882932) mais les réponses que j'ai obtenu ne me satisfont pas trop. Pour illustrer le problème je vais prendre un exemple.

    Considérons la classe suivante:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class MyClass1
    {
        public:
            double x() const {return _x;} // getter
            double y() const {return _y;} // getter
            double z() const {return _x*_y;} // getter
            void x(const double var) {_x = var;} // setter
            void y(const double var) {_y = var;} // setter
            void z(const double var) {_x = var; _y = 1;} // setter
        protected:
            double _x;
            double _y;
    };
    D'un point de vue utilisateur, MyClass1 est une classe qui possède 3 propriétés x, y et z interdépendantes qui peuvent être accédées par des getter,et qui peuvent être modifiées par des setter. D'un point de vue de l'utilisateur toujours, le fait qu'en interne il y ait _x, _y mais pas _z est un détail d'implémentation : les getter et les setter permettent de garantir l'accès/la modif comme si on avait toutes les variables en interne.

    La question posée est la suivante: y-a-t-il une façon d'éviter d'avoir à écrire des getter et des setter qui ne font "rien de spécial" comme ceux de x et ceux de y ?

    Une solution que j'ai trouvé consiste à passer par un wrapper et l'overload de l'opérateur ().

    Le wrapper :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    template <typename Type>
    class Wrapper
    {
        public:
            constexpr Wrapper(const Type& value) {_value = value;}
            constexpr Type& operator()() {return _value;}
            constexpr const Type& operator()() const {return _value;}
            constexpr void operator()(const Type& value) {_value = value;}
            constexpr operator Type() const {return _value;}
        protected:
            _value;
    };
    La nouvelle classe:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    class MyClass2
    {
        public:
            Wrapper<double> x;
            Wrapper<double> y;
            double z() const {return x*y;} // getter
            void z(const double var) {x = var; y = 1;} // setter
    };
    L'utilisateur peut toujours utiliser x(), y() et z(), sauf que cette fois je n'ai pas eu à écrire les getter et setter de x et y.

    La question est la suivante : est-ce une pratique "acceptable" ou est-ce à éviter en terme de design (ou encore pire est-ce "dangereux" (et si c'est le cas, pourquoi)) ?
    De la philosophie du langage

    D'un point de vue purement technique, tu définis une classe dont les membres sont publics, et en accès direct - au moins pour x et y, il n'y a strictement aucun traitement à faire pour les setter. Du coup, quel est l'intérêt de faire de MyClass1 ou MyClass2 des classes ? Autant en faire des structures avec tous les membres publics - et rajouter la pseudo-variable z via les accesseurs que tu proposes.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    struct MyClass3
    {
            double x;
            double y;
            double z() const {return x*y;} // getter
            void z(double var) {x = var; y = 1;} // setter
    };
    Ca a l'avantage d'être à peu près 100 fois plus lisible, tout en étant parfaitement identique en terme non seulement de comportement, mais aussi de philosophie.

    De la philosophie des setters

    Changer la valeur de z modifie x et y. x et y sont (à peu de chose près) public. Ça ne sent pas très, très bon. Je ne sais pas ce que représente cette classe - ou même s'il elle a d'autre fonction que nous donner un exemple, mais ce code à une odeur assez peu agréable.

    Cette odeur peu agréable est liée au fait que d'une part tu a des variables publiques ou librement accessible, et d'autre part une fonction qui modifie ces variables de manière nettement moins explicite. Par exemple, ce code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    MyClass4 o;
     
    o.x = 10.0;
    o.y = 5.0;
    o.z(8.0);
     
    assert(double_equals(o.x, 10.0)); // échec
    assert(double_equals(o.y, 5.0)); // echec
    assert(double_equals(o.z, 8.0)); // ok, jusqueà ce que tu change le multiplicateur de y
    Va nécessairement échouer, alors que l'interface de la classe/structure laisse entendre que non seulement il est valide, mais aussi qu'il est censé.

    Il y a deux règles à respecter :
    • Lorsque que une ou plusieurs variables sont sémantiquement fortement liée, alors elle ne peuvent pas être publiques. De plus, aucune de ces variable ne doit avoir de mutateur (setter) (ça, je le rajoute pour faire le buzz. Ou alors je suis super sérieux, mais dans l'idée, il vaut mieux que tu y réfléchisse par toi même).
    • Une variable publique ou librement accessible ne DOIT pas être modifiée autrepart que dans son setter s'il existe (si c'est une variable publique, alors elle ne DOIT pas être modifiée par la classe).

    Dans la classe que tu proposes, c'est particulièrement vrai. Ce dont tu as besoin ici, ce sont de comportement nommés, pas de setters. Pour simplifier : tu n'initialise pas z - tu ramène l'objet sur un axe particulier. De même, tu n'intialiser ni x, ni y, tu les déplaces sur un axe.

    En changeant juste la dénomination des setter, on obtient une sorte de miracle :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    class MyClass5  // et oui, de retour dans une classe...
    {
    private:
      double x, y;
    public:
      double x() const { return x; }
      double y() const { return y; }
      double composed() const { return x * y; } // je pense que z est un très mauvais nom...
      void move_on_x(double v) { x = v; }
      void move_on_y(double v) { y = v; }
      void project_on_x(double v) { x = v; y = 1.0; }
    }
    Le changement de nom rend les opérations beaucoup plus explicite, et les asserts ci-dessous perdent d'un coup tout leur sens.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    MyClass5 o;
     
    o.move_on_x(10.0);
    o.move_on_y(5.0);
    o.project_on_x(8.0);
     
    // n'a plus de sens, puisque la dernière op est un project_on_x
    // assert(double_equals(o.x(), 10.0)); 
    // plus de sens non plus, même raison
    // assert(double_equals(o.y(), 5.0));
    // aucun sens non plus, parce que le fait de projeter sur x ne signifie pas
    // que composed() DOIT être égal à la valeur projetée.
    // assert(double_equals(o.composed(), 8.0));
    Pour plus d'information sur les setter, cf. http://blog.emmanueldeloget.com/inde...des-accesseurs

    Wrapper d'objet

    Il existe des cas où les wrapper tels que tu les présentes peuvent avoir un intérêt - mais pas ici, parce que le cas que tu propose ne le nécessite pas. En fait, on peut tout à fait imaginer pouvoir reproduire le système de propriété de Borland C++ avec une classe inspirée de ces wrappers (pour info, les properties de BC++ sont une extension qui permet d'encapsuler l'accès à une variable. Grosso modo, on définit une propriété ainsi :
    * elle impacte une variable de la classe (ou une pseudo variable)
    * on lui affecte un setter ou un getter ou les deux.

    Exemple (à peu de chose prêt, vous comprendrez que je n'ai pas le temps de compiler, tester, etc):

    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
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
     
    template <class C, class T>
    class property
    {
    public:
      typedef T value_type;
      typedef C class_type;
      typedef value_type (class_type::*getter_type)() const;
      typedef value_type (class_type::*setter_type)(const value_type&);
    private:
      class_type *m_c;
      setter_type m_s;
      getter_type m_g;
     
      property(class_type *c, setter_type s, getter_type g)
      : m_c(c), m_s(s), m_g(g)
      { }
      value_type operator=(const value_type& v)
      {
          return (m_c->*m_s)(v);
      }
      operator value_type()() const
      {
          return (m_c->*m_g)();
      }
    };
     
    struct MyClass4
    {
    private:
      double set_z(double v) { x = v; y = 1.0; ; return x * y; }
      double get_z(double v) const { return x * y; }
    public:
      MyClass4()
      : property(this, &MyClass4::set_z, &MyClass4::get_z)
      { }
     
      double x, y;
      property<MyClass,double> z;
    }
    Pour le cas que tu donnes en exemple, c'est extrêmement dangereux (à cause de la sémantique de l'operator=. On s'attends à ce que l'operator= puisse être chaîne, pour écrire par exemple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    MyClass4 o;
    double saved_z;
     
    saved_z = o.z = 10.0;
    Dans ton cas, ça ne marche que par caprice, parce que tu forces y à 1.0. Si tu te mets à forcer y = 2.0 par exemple, alors ce code ne fait plus ce qu'il semble faire - et là, ça se transforme vite en arrachement de cheveux).
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  4. #4
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 704
    Points
    2 704
    Par défaut
    Citation Envoyé par Emmanuel Deloget Voir le message
    Lorsque que une ou plusieurs variables sont sémantiquement fortement liée, alors elle ne peuvent pas être publiques. De plus, aucune de ces variable ne doit avoir de mutateur (setter).
    Admettons que tu aies une classe modélisant un chemin à parcourir. Elle a une distance, ainsi que le temps pour parcourir ce chemin pour une vitesse donnée.
    La distance est une donnée non modifiable. En revanche, tu peux vouloir faire varier le temps ou la vitesse par un set, mais les deux étant interdépendants, chaque set modifie à la fois le temps et la vitesse.

    En quoi cela serait-il une mauvaise idée ?

  5. #5
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    En réalité, aucune des deux n'est une véritable donnée conservée
    J'imagine plus deux accesseurs: timeAt(speed) et speedFor(time)
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  6. #6
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 704
    Points
    2 704
    Par défaut
    Citation Envoyé par leternel Voir le message
    En réalité, aucune des deux n'est une véritable donnée conservée.
    Si tu modélises une route, la vitesse pourrait être une vitesse statistique, modifiée une fois de temps en temps, mais qui serait caractéristique de la route.
    Toi, tu peux vouloir demander le temps passé sur cette route, sans pour autant connaître cette vitesse statistique.

  7. #7
    Membre habitué
    Homme Profil pro
    Doctorant en Astrophysique
    Inscrit en
    Mars 2009
    Messages
    312
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Astrophysique
    Secteur : Enseignement

    Informations forums :
    Inscription : Mars 2009
    Messages : 312
    Points : 176
    Points
    176
    Par défaut
    J'aurai du préciser que MyClass1 ne représente rien de précis, c'est tout ce que j'ai trouvé pour exemple, et j'avoue qu'elle est assez mal conçu. Je voulais juste une classe qui du point de vue de l'utilisateur a plusieurs propriétés dont certaines sont directement des variables internes tandis que d'autres sont construites à partir de plusieurs variables internes, mais ça l'utilisateur n'a pas à le savoir.

    Le coeur de la question est donc : les propriétés qui correspondent directement à des variables internes (ici x et y, mais pas z), peuvent elles utiliser le wrapper présenté dans le premier post ?

  8. #8
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par Kaluza Voir le message
    Le coeur de la question est donc : les propriétés qui correspondent directement à des variables internes (ici x et y, mais pas z), peuvent elles utiliser le wrapper présenté dans le premier post ?
    Je ne suis pas sûr de bien tout saisir, mais si tu veux que x et y soient accessibles à l’extérieur de ta classe, alors cela n'a aucun sens de les mettre en public et de t'em**der avec des accesseurs/mutateurse t toutes les indirections supplémentaires que tu voudras empiler.
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  9. #9
    Inactif  


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

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Points : 15 620
    Points
    15 620
    Par défaut
    J'imagine que c'est pour avoir une homogénéité d'écriture ? Pour que l'écriture de x, y et z soit identique

    @Kaluza
    Si tu as le choix de l'interface, je trouve que des fonctions libres avec spécialisation serait plus simple. Tu écrirais par exemple
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    MyClass1 value;
    x(value) = 10;
    Tu écris la fonction x() de façon à utiliser la fonction si elle existe, sinon tu utilises la variable membre (ou l'inverse si tu veux)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    template<class T>
    double x(T const& t) { return t.x(); }
     
    template<class T>
    double x(T const& t) { return t.x; }
    (ouch que ce code est pas fonctionnel)

    Idem pour y et z

  10. #10
    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
    Citation Envoyé par oodini Voir le message
    Si tu modélises une route, la vitesse pourrait être une vitesse statistique, modifiée une fois de temps en temps, mais qui serait caractéristique de la route.
    Ah, parce que tu voudrait donner la responsabilité à la route de savoir à quelle vitesse on roule dessus

    Tu pourrais, effectivement, avoir une notion de vitesse maximale sur ta route, mais aucunement la notion de vitesse effective: La vitesse maximale est sans doute clairement définie en fonction du code de la route, et n'est à modifier que de manière particulièrement stricte (décision du ministère compétant en la matière)

    Par contre, la vitesse effective, et donc le temps passé sur la route est de la seule responsabilité du chauffeur : c'est lui qui décide de respecter la limitation de vitesse... ou non
    Toi, tu peux vouloir demander le temps passé sur cette route, sans pour autant connaître cette vitesse statistique.
    A ce moment là, ce n'est pas à la route qu'il faut poser la question, mais à un système qui garde une trace des vitesses effectives

    Citation Envoyé par Kaluza Voir le message
    J'aurai du préciser que MyClass1 ne représente rien de précis, c'est tout ce que j'ai trouvé pour exemple, et j'avoue qu'elle est assez mal conçu. Je voulais juste une classe qui du point de vue de l'utilisateur a plusieurs propriétés dont certaines sont directement des variables internes tandis que d'autres sont construites à partir de plusieurs variables internes, mais ça l'utilisateur n'a pas à le savoir.

    Le coeur de la question est donc : les propriétés qui correspondent directement à des variables internes (ici x et y, mais pas z), peuvent elles utiliser le wrapper présenté dans le premier post ?
    Avant toute chose, tu dois te forcer à envisager la programmation orientée objets sous l'aspect des comportements que tu attends de la part des différents objets que tu manipule.

    Ensuite, évite les get / set au profit de comportements explicites.

    Comme l'a fait remarquer Emmanuel, le fait de renommer le mutateur z en project_on_x enlève toute ambiguïté au problème et montre, effectivement, qu'il est possible d'avoir des comportement qui agissent sur plusieurs valeurs simultanément.

    Une fois que tu auras pris le pli de travailler de la sorte, tu ne te poseras plus de questions qui ne servent qu'à retourner le cerveau ou à se faire du mal inutilement
    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

  11. #11
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Citation Envoyé par Emmanuel Deloget Voir le message
    • Une variable publique ou librement accessible ne DOIT pas être modifiée autrepart que dans son setter s'il existe (si c'est une variable publique, alors elle ne DOIT pas être modifiée par la classe).
    Je suis assez d'accord avec cette règle, j'y mettrais juste un léger bémol. Elle a le droit aussi d'être modifiée par les fonctions techniques assurant la maintenance de la classe (constructeurs, operator=).
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  12. #12
    Membre expert

    Avatar de germinolegrand
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Octobre 2010
    Messages
    738
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : Octobre 2010
    Messages : 738
    Points : 3 892
    Points
    3 892
    Par défaut
    <joke>
    Ah, parce que tu voudrait donner la responsabilité à la route de savoir à quelle vitesse on roule dessus ?
    Y'a des fois ça ferait pas de mal .
    </joke>

    C'est moi ou les sujets sur les accesseurs fleurissent ces temps-ci ?

  13. #13
    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
    Citation Envoyé par germinolegrand Voir le message
    C'est moi ou les sujets sur les accesseurs fleurissent ces temps-ci ?
    Bah, il y a des sujets qui reviennent épisodiquement, et régulièrement des débats qui partent dans tous les sens

    Comme la modération fait correctement son boulot, elle essaye de scinder les débats qui partent "un peu trop" dans tous les sens
    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

  14. #14
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut
    Citation Envoyé par oodini Voir le message
    Si tu modélises une route, la vitesse pourrait être une vitesse statistique, modifiée une fois de temps en temps, mais qui serait caractéristique de la route.
    Toi, tu peux vouloir demander le temps passé sur cette route, sans pour autant connaître cette vitesse statistique.
    Quand bien même ce sont des données conservée (ton exemple est assez bon ; c'est un cas réel que j'ai eu à traiter dans le cadre d'un système GPS) on ne peut pas se permettre de nommer les mutateurs set_qqchose(), parce que ce nom ne dit pas ce que la fonction fait dans ce cadre. En fait, si on a deux données A et B dans une classe, et que des setters sont disponibles pour ces deux données, on doit pouvoir écrire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    object.set_A(a);
    object.set_B(b);
     
    assert(object.get_A() == a);
    assert(object.get_B() == a);
    Tout simplement parce que le code nous le dit. Pour assurer une bonne compréhension de celui-ci, on doit pouvoir compter sur certains détails triviaux de ce type. Sans ça, autant nommer les fonctions toto ou titi (ok, là, j'exagère peut-être un peu ).

    Si deux variables A et B sont liées fortement alors on ne peut pas se permettre de leur donner un mutateur - il faut trouver une autre manière d'exprimer le besoin, un autre verbe qui sera plus adapté à la fonction demandée. Si vous ne voulez pas chercher trop loin, il y a au moins deux autres verbes génériques qui, comparé à set, sont plus ouvert (comprenez : ils ont encore moins de sens, ce qui pose un autre problème sémantique, mais qui a l'avantage de résoudre en partie le problème posé par set). Ces verbes, ce sont change et update. update_speed() ne dit pas que seule la vitesse est concernée par la modification. Encore mieux, histoire de rendre plus explicite le fait que la vitesse n'est peut être pas la seule variable modifiée, on peut utiliser update_for_speed() ou update_with_speed(). Après ça, celui qui ose encore mettre un assert() derrière prend des risques, puisque le code s'éloigne de la notion de pure initialisation proposée par set, pour dire 'je met à jout/ je modifie la vitesse' - ce qui ne prétend pas n'avoir aucune incidence autre part.

    Il faut bien comprendre que mon point de vue ne touche en rien le code lui-même. Le code de la fonction ne change pas. Il concerne uniquement le nommage de cette fonction, ce qui est un point au moins aussi important. Parce que, finalement, sans interface claire, on a du mal à programmer pour une interface. Lire une interface doit nous dire exactement ce que cette interface propose comme service. Dans les meilleures interfaces, les pré-conditions et post-conditions doivent être implicites et compréhensible à la seule lecture de l'interface - le nom des méthodes et des classes est donc quelque chose d'essentiel.

    Donc, pour bien résumer, je me bat contre le mot "set", pas contre ce que fait la fonction
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  15. #15
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Ah, parce que tu voudrait donner la responsabilité à la route de savoir à quelle vitesse on roule dessus

    Tu pourrais, effectivement, avoir une notion de vitesse maximale sur ta route, mais aucunement la notion de vitesse effective: La vitesse maximale est sans doute clairement définie en fonction du code de la route, et n'est à modifier que de manière particulièrement stricte (décision du ministère compétant en la matière)

    Par contre, la vitesse effective, et donc le temps passé sur la route est de la seule responsabilité du chauffeur : c'est lui qui décide de respecter la limitation de vitesse... ou non
    On peut aussi imaginer une vitesse moyenne constatée (en fait, on ne l'imagine pas ; après tout, c'est grâce à ça que les GPS nous disent "vous allez arriver dans 6 heures 18 minutes" sur un trajet de 648 km )
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  16. #16
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    Citation Envoyé par gbdivers Voir le message
    J'imagine que c'est pour avoir une homogénéité d'écriture ? Pour que l'écriture de x, y et z soit identique
    J'avais expérimenté une classe du même genre que celle présenté par l'OP pour avoir cet effet justement.

    Je suis totalement d'accord avec les points de vue d'Emmanuel et de Koala01 par rapport au getter/setter. Et donc j'aurais tendance à laisser une donnée membre en publique si celle-ci représente un service associé à la classe en elle-même. Par contre ca a un effet secondaire qui me gène un peu : certains services s'appellent par obj.service(); et d'autres (ceux représentées par une donnée membre publique) par obj.service;.

    Et dans cette optique là, je pense que ce genre de wrapper (pas forcément écrit comme présenté par l'OP (*)), est loin d'être une mauvaise idée. Elle ne présente pas de défaut supplémentaire à une donnée membre publique (**) mais offre l'avantage d'uniformiser les appels.


    (*) Idéalement, il faudrait pouvoir faire class::service y compris si le service est représenté par une donnée membre publique (typiquement pour un bind).

    (**) Donc aucun défaut si l'utilisation d'une donnée membre publique est justifié.


    NB: Cependant, l'intérêt du développement de cet outil est quand même loin d'être indispensable, AMA.

  17. #17
    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
    Citation Envoyé par Emmanuel Deloget Voir le message
    On peut aussi imaginer une vitesse moyenne constatée (en fait, on ne l'imagine pas ; après tout, c'est grâce à ça que les GPS nous disent "vous allez arriver dans 6 heures 18 minutes" sur un trajet de 648 km )
    Personnellement, je serais d'avis de laisser cette responsabilité à "quelque chose de prévu pour", qui mettrait en relation un tronçon de route d'une part et un ensemble de vitesses de l'autre.

    Mais je ne rendrais pas le tronçon de route responsable du maintien des différentes vitesses effectives auxquelles on a roulé dessus
    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

  18. #18
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 704
    Points
    2 704
    Par défaut
    Citation Envoyé par Emmanuel Deloget Voir le message
    Quand bien même ce sont des données conservée [...] on ne peut pas se permettre de nommer les mutateurs set_qqchose(), parce que ce nom ne dit pas ce que la fonction fait dans ce cadre.

    [...]

    Si deux variables A et B sont liées fortement alors on ne peut pas se permettre de leur donner un mutateur - il faut trouver une autre manière d'exprimer le besoin, un autre verbe qui sera plus adapté à la fonction demandée. Si vous ne voulez pas chercher trop loin, il y a au moins deux autres verbes génériques qui, comparé à set, sont plus ouvert [...]. Ces verbes, ce sont change et update. update_speed() ne dit pas que seule la vitesse est concernée par la modification.
    Il y a vraiment un problème de la compréhension de la langue anglaise, sur ce forum.
    La seule différence sémantique entre set et update est que update indique que le champ/membre avait déjà une valeur (qu'on considérera comme valide).
    Il n'y a aucune différence sémantique sur l'enchaînement de sous-actions qu'impliquent ces deux actions.

  19. #19
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 704
    Points
    2 704
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Personnellement, je serais d'avis de laisser cette responsabilité à "quelque chose de prévu pour", qui mettrait en relation un tronçon de route d'une part et un ensemble de vitesses de l'autre.
    Quel avantage pratique en tires-tu ?

    Citation Envoyé par koala01 Voir le message
    Mais je ne rendrais pas le tronçon de route responsable du maintien des différentes vitesses effectives auxquelles on a roulé dessus
    C'est apparemment pourtant ce que font les gens travaillant dans le domaine.

  20. #20
    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
    Citation Envoyé par oodini Voir le message
    Quel avantage pratique en tires-tu ?
    L'avantage, c'est que je garde des responsabilités simples et uniques à mes différents objets.

    Si j'ai une classe qui représente les différents tronçons de routes, avec les distances et la vitesse maximale autorisée d'un coté, et l'autre qui, faisant référence à ces tronçons de route représente les temps de parcours effectifs, je gère des relations un à plusieurs (un tronçons pour plusieurs vitesses) au lieu de relation plusieurs à plusieurs (plusieurs vitesses sur plusieurs tronçons différents), et je me permet de ne pas connaitre des détails qui peuvent ne pas m'intéresser dans certaines circonstances (par exemple la distance du tronçon si on veut juste des statistiques sur le temps passé dessus )

    D'autant plus que l'on pourrait maintenir dans cette classe supplémentaire plutôt le temps effectif du parcours (en minutes + secondes) que la vitesse effective, qui devrait etre une moyenne calculée à chaque parcours
    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

Discussions similaires

  1. Réponses: 3
    Dernier message: 09/12/2010, 13h38
  2. Inlining des getters / setters auto avec GCC?
    Par _skip dans le forum Débuter
    Réponses: 45
    Dernier message: 17/08/2009, 12h51
  3. [PHP 5.2] Remplacer des balises html par des balises encodées
    Par gtraxx dans le forum Langage
    Réponses: 3
    Dernier message: 28/01/2009, 21h54
  4. [Template] Changer la génération des getter/setter
    Par anthyme dans le forum NetBeans
    Réponses: 2
    Dernier message: 05/07/2007, 09h26
  5. Réponses: 10
    Dernier message: 20/09/2006, 12h53

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