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 :

Quand les mutateurs nous mentent


Sujet :

C++

  1. #81
    Membre extrêmement actif

    Profil pro
    Inscrit en
    Février 2006
    Messages
    2 408
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2006
    Messages : 2 408
    Par défaut
    totalement, idéalement on réfléchit avant, mais en réalité, tout comme la nécessité (pour moi) de la doc, responsabiliser correctement le code, etc., en pratique ce n'est que rarement le cas.

    en tout cas je suis d'accord qu'en terme de service, le set n'est pas approprié, mais dans le cas où je fournis des données, je trouve le set plus approprié, vu que c'est moi qui fournis quelquechose, et non pas la cible qui dans ce cas ne me rend pas spécialement service, vu que je lui impose des données.

    je sais pas par contre si ma délimitation est vraiment claire ; le set serait pour moi quand la cible est relativement spectatrice des données, et "autre chose que set" quand la cible à un rôle actif en relation avec sa fonction.

  2. #82
    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
    Citation Envoyé par stardeath Voir le message
    la responsabilité devant revenir à l'auteur et non pas au client en cas de non respect d'un quelconque contrat, selon moi.
    Ca c'est un potentiel fork de sujet.
    J'ai réfléchi à cela il y a peu, et je vois de plus en plus un système à couches.

    En gros:
    1. la couche la plus basse a un contrat, un non respect du contrat est interprété comme une erreur de programmation -> assert
    2. la couche cliente est responsable de s'assurer que le contrat est respecté ;
      1. soit par construction (oui, comme en maths) nous savons que l'état d'entrée dans la couche basse sera valide (e.g. sqrt(abs(g())), le code client n'a alors aucune vérification à faire ;
      2. soit par test quand on ne peut pas entièrement maitriser l'état d'entrée (I/O utilisateur, fichiers, sockets) -- libre alors à ce code de choisir la politique à adopter (recommencer, remonter une erreur, ignorer et passer à la suite, etc)

    Éventuellement, pour faciliter la mise en œuvre du code client 2.2-, on peut fournir des fonctions utilitaires à la sauce Programmation défensive qui vont faire la vérification et lever des exceptions avant d'appeler la couche basse/métier (comme std::vector<>::at()).

    Ce qui m'ennuie avec les setters qui font un check, c'est ce côté programmation défensive dont j'ai appris à me méfier. Certes l'erreur n'est pas ignorée si une exception est levée, mais nous assistons à un joyeux mélange, que je ne trouve pas si heureux (sic).
    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...

  3. #83
    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 stardeath Voir le message
    totalement, idéalement on réfléchit avant, mais en réalité, tout comme la nécessité (pour moi) de la doc, responsabiliser correctement le code, etc., en pratique ce n'est que rarement le cas.
    En fait, le débat sur la documentation du code est certes intéressant, mais je propose néanmoins qu'on le laisse de coté, car, à part l'aspect "auto documenté" des noms de fonctions bien choisi, il est déjà abordé dans une autre discussion (sur laquelle tu es le bienvenu si tu veux donner ton avis, d'ailleurs )
    en tout cas je suis d'accord qu'en terme de service, le set n'est pas approprié, mais dans le cas où je fournis des données, je trouve le set plus approprié, vu que c'est moi qui fournis quelquechose, et non pas la cible qui dans ce cas ne me rend pas spécialement service, vu que je lui impose des données.

    je sais pas par contre si ma délimitation est vraiment claire ; le set serait pour moi quand la cible est relativement spectatrice des données, et "autre chose que set" quand la cible à un rôle actif en relation avec sa fonction.
    Je crois que je comprend ce que tu essayes d'expliquer, mais cela m'amène te poser une question.

    Si ton objet en cours n'est que "spectateur" (pour reprendre ton terme) de la valeur transmise par set, pourquoi ton objet l'utilise-t-il

    Dans le meilleur des cas, c'est une donnée inutile, dans le pire, tu as peut etre mal évalué la responsabilité de ton objet, et, dans ce cas, tu devrais peut etre revenir dessus afin de respecter SRP (Single Responsability Principle)

    Entre les deux, il reste la solution que ton objet maintienne cette donnée pour la garder accessible lors d'une "utilisation ultérieure".
    [EDIT](oupss fausse manoeuvre)

    Mais, à ce moment là, ou bien il va ne maintenir qu'une seule instance de la donnée en question, et le fait d'utiliser changeData en lieu et place de setData n'est pas incohérent (d'autant plus que cela n'implique aucune présomption quant à savoir ce qui se passe de la donnée précédente ), ou bien, il va en maintenir plusieurs, et, dans ce cas, des terme comme push, push_back, insert et autres (en plus de tous les comportements inverses) semblent correspondre beaucoup mieux aux services réellement fournis, à l'instar des collections de la STL non
    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

  4. #84
    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 : 51
    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
    Par défaut
    Je n'ai pas lu tous les détails, mais je m'insurge contre une hypothèse : Celle qui voudrait qu'un set soit "bête et con" et ne fasse rien d'autre qu'une affectation dans l'inconscient collectif.

    Ce n'est pas ce que j'ai pu constater, en particulier dans des langages avec la notion de propriété, et pour de l'IHM. Si je fais un set, je vais déclencher des évènements (c'est le rôle du DP observer, non ?) lesquels pourront à leur tout modifier d'autres valeurs, voire même remodifier la valeur initiale (et ce sans que ce soit sous le contrôle de l'écrivain de la classe, c'est décidé par ses utilisateurs). Pour en avoir mangé pas mal à une époque, je peux dire que jamais ô grand jamais je n'ai une vision d'une fonction set simpliste.


    Autre exemple, que faire si un set vient détruire les invariants d'une classe ? Surtout s'il est possible de les rétablir silencieusement et sans soucis ? Je suis tombé récemment pour une démo sur une classe Fraction. Elle avait comme invariants qu'elle est tout le temps gardée en mémoire sous forme simplifiée, et que le dénominateur est tout le temps positif. De plus, elle étant non invariable.

    Si une fraction vaut 1/2, que faire sur un setNumerateur(2) ? Rompre l'invariant ? Le conserver avec une fraction valant 1/1 ?
    Et si elle fait 2/1, que faire si on fait un setDenominateur(-2) ? Lever une erreur ? Rompre l'invariant ? Ou bien générer la fraction -1/1 ?

    Trouver d'autres noms que setNumerateur ? Pourquoi pas, mais lesquels ?
    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.

  5. #85
    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
    Par défaut
    Selon moi setDenominateur(2) et setNumerateur(-2) sont une exposition pure et simple de la structure interne, et ne correspond pas réellement au service. Un setFraction(2, -2) (voir un operator= pour faire beau) est plus approprié.
    Pourquoi devrait-on setter uniquement le dénominateur ou le numérateur ? ça n'a pas de sens, une fraction n'a de sens que comme un assemblage de deux valeurs (faites l'analogie sur des classes plus évoluées).

    On fait un setFraction(2, -2), un operator*=, un operator/=, +=, -=, etc.
    Ainsi, on laisse libre la structure interne.

  6. #86
    Membre extrêmement actif

    Profil pro
    Inscrit en
    Février 2006
    Messages
    2 408
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2006
    Messages : 2 408
    Par défaut
    bah je dirai les 3 mon bon monsieur.

    pour la donnée inutile dû à une mauvaise conception, je n'ai pas encore suffisamment d'expertise pour éviter ça, je dirai, pour faire une analogie, que c'est le code historique, c'est comme ça mais ça marche, donc on le change pas mais c'est planifié pour ... dans longtemps.

    pour l'objet totalement spectateur face à la donnée, ça arrive très souvent selon moi, comme par exemple toutes les données d'aide au débogage, comme un identifiant, le propriétaire, le thread, etc.
    dans mon code c'est souvent des données sous la macro DEBUG.

    et pour le dernier cas, la commodité, et là le changeData me parait pas adéquat, il indiquerait que la donnée est une donnée essentielle et que l'objet la traite en accord avec son but premier, alors que c'est plutôt une donnée accessoire, remarque que ça peut très facilement dériver vers le premier cas.

  7. #87
    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 JolyLoic Voir le message
    Ce n'est pas ce que j'ai pu constater, en particulier dans des langages avec la notion de propriété, et pour de l'IHM. Si je fais un set, je vais déclencher des évènements (c'est le rôle du DP observer, non ?) lesquels pourront à leur tout modifier d'autres valeurs, voire même remodifier la valeur initiale (et ce sans que ce soit sous le contrôle de l'écrivain de la classe, c'est décidé par ses utilisateurs). Pour en avoir mangé pas mal à une époque, je peux dire que jamais ô grand jamais je n'ai une vision d'une fonction set simpliste.
    J'ai presque envie de dire que le seul fait que tu aies besoin d'un observateur sur un set montre que tu as déjà "loupé quelque chose", surtout s'il doit aller triturer la donnée que tu as fournie comme paramètre pour la "faire rentrer dans le moule"

    J'attendrais beaucoup plus ce genre de comportement vis à vis d'une fonction qui ne prétend pas "fixer" une valeur à celle indiquée

    Autre exemple, que faire si un set vient détruire les invariants d'une classe ? Surtout s'il est possible de les rétablir silencieusement et sans soucis ? Je suis tombé récemment pour une démo sur une classe Fraction. Elle avait comme invariants qu'elle est tout le temps gardée en mémoire sous forme simplifiée, et que le dénominateur est tout le temps positif. De plus, elle étant non invariable.

    Si une fraction vaut 1/2, que faire sur un setNumerateur(2) ? Rompre l'invariant ? Le conserver avec une fraction valant 1/1 ?
    Et si elle fait 2/1, que faire si on fait un setDenominateur(-2) ? Lever une erreur ? Rompre l'invariant ? Ou bien générer la fraction -1/1 ?

    Trouver d'autres noms que setNumerateur ? Pourquoi pas, mais lesquels ?
    C'est bien là tout le problème...

    rompre l'invariant de ton contrat est un coup à être pendu haut et court sur la place du marché... Il vaut donc mieux l'éviter
    modifier le dénominateur alors qu'on veut fixer le numérateur serait mentir à l'utilisateur sur ce qui est réellement fait (en plus, nous aurions toujours un assert qui saute si l'on veut vérifier la valeur de l'opérande modifié)

    La réponse est donc bel et bien : Trouver d'autres noms que setNumerateur

    Mais quant à savoir lesquels... Je ne peux que te conseiller de réfléchir au comportement auquel tu t'attend en invoquant cette fonction
    Citation Envoyé par stardeath Voir le message
    bah je dirai les 3 mon bon monsieur.

    pour la donnée inutile dû à une mauvaise conception, je n'ai pas encore suffisamment d'expertise pour éviter ça, je dirai, pour faire une analogie, que c'est le code historique, c'est comme ça mais ça marche, donc on le change pas mais c'est planifié pour ... dans longtemps.
    Puis on s'étonne qu'après avoir voulu fixer cette valeur (avec un set comme de juste), l'objet n'est absolument pas modifié (en apparence du moins) comme on aurait pu l'espérer

    Joyeuseté de débuggage en perspective
    pour l'objet totalement spectateur face à la donnée, ça arrive très souvent selon moi, comme par exemple toutes les données d'aide au débogage, comme un identifiant, le propriétaire, le thread, etc.
    dans mon code c'est souvent des données sous la macro DEBUG.
    Il m'arrive aussi de rajouter des données de débuggage dans mon code, mais:
    • généralement, je ne donnerai pas accès (surtout en écriture) à ces données
    • je supprime ces informations "dés que possible" (comprend: une fois que la solution au problème a été trouvée )
    • l'ajout de données de débuggage est rarement commité (par moi, du moins) pour garder une base de code propre

    et pour le dernier cas, la commodité, et là le changeData me parait pas adéquat, il indiquerait que la donnée est une donnée essentielle et que l'objet la traite en accord avec son but premier, alors que c'est plutôt une donnée accessoire, remarque que ça peut très facilement dériver vers le premier cas.
    Justement, pour ce cas particulier, il n'y a souvent aucune raison de fournir un mutateur quelconque ni un quelconque comportement publique ayant pour but de modifier cette donnée...

    Cela signifie : ni changeData, ni setData ni quoi que ce soit qui indique d'une quelconque manière que la donnée en question a lieu d'être modifiée
    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

  8. #88
    Membre Expert
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Par défaut
    Citation Envoyé par JolyLoic Voir le message
    Autre exemple, que faire si un set vient détruire les invariants d'une classe ?
    Cas général : une précondition sur ton paramètre pour te prémunir de cela.

    Surtout s'il est possible de les rétablir silencieusement et sans soucis ? Je suis tombé récemment pour une démo sur une classe Fraction. Elle avait comme invariants qu'elle est tout le temps gardée en mémoire sous forme simplifiée, et que le dénominateur est tout le temps positif. De plus, elle étant non invariable.
    Cas particulier. Remarque : l’invariant est un invariant d’implémentation. De ce fait, il ne m’intéresse pas en tant que client. Les seuls invariants qui m’intéressent sont ceux qui ont trait à des propriétés publiques.

    Si une fraction vaut 1/2, que faire sur un setNumerateur(2) ? Rompre l'invariant ? Le conserver avec une fraction valant 1/1 ?
    Et si elle fait 2/1, que faire si on fait un setDenominateur(-2) ? Lever une erreur ? Rompre l'invariant ? Ou bien générer la fraction -1/1 ?

    Trouver d'autres noms que setNumerateur ? Pourquoi pas, mais lesquels ?
    Alors, setNumerateur est un très mauvais nom, car il mélange du français et de l’anglais . Ensuite, j’attaquerai le problème non pas dans le setter, mais dans le getter.

    Change getNumerateur par (get)NumerateurReduit. On aura donc un couple setNumerateur / getNumerateurReduit, et pas de getNumerateur. La dyssimétrie doit mettre sur la piste que non, l’opération n’est pas symétrique. Quant à ton invariant de classe ? En tant que client, je m’en fiche, c’est ton invariant interne. Tu peux choisir celui que tu veux, du moment que getNumerateurReduit respecte son contrat, à savoir me renvoyer le numérateur de la fraction réduite.

  9. #89
    Membre extrêmement actif

    Profil pro
    Inscrit en
    Février 2006
    Messages
    2 408
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2006
    Messages : 2 408
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Justement, pour ce cas particulier, il n'y a souvent aucune raison de fournir un mutateur quelconque ni un quelconque comportement publique ayant pour but de modifier cette donnée...

    Cela signifie : ni changeData, ni setData ni quoi que ce soit qui indique d'une quelconque manière que la donnée en question a lieu d'être modifiée
    prenons un joli objet graphique : un bouton (le classique push button de toute les gui)

    donnée essentielle : aucune.

    données dont il est "spectateur" : son texte, sa couleur, sa position, sa taille ...

    le changeData n'a aucune justification ici, le bouton n'a pas besoin de ces données pour se comporter en tant que bouton, par contre le reste sont des données qu'on lui impose, d'où mon penchant pour le set (et de surcroit chacune de ces propriétés est unique, de ce fait, pas de méthode add).

    après on pourrait toujours rendre le bouton immutable et mettre ces propriétés à la création seulement (ou autre, je suppose qu'il y a des tas de manières de modéliser ça), mais je n'aime pas cette philosophie.

  10. #90
    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 : 51
    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
    Par défaut
    Citation Envoyé par white_tentacle Voir le message
    Cas particulier. Remarque : l’invariant est un invariant d’implémentation. De ce fait, il ne m’intéresse pas en tant que client. Les seuls invariants qui m’intéressent sont ceux qui ont trait à des propriétés publiques.
    Objection votre honneur ! C'est un invariant publique ! C'est lui qui permet de dire que cout << Fraction(2,4) affichera 1/2. Ou que Fraction(2,4).numerateur() == 1, ou encore que Fraction(1,2) == Fraction(2,4) (alors que ces fractions ne sont pas égales au sens mathématiques). Alors, certes, on peut dire qu'il ne s'agit que d'une post condition qui bien que toujours vraie, n'a vraiment de sens que pour certaines fonctions. Mais une telle post-condition, moi j’appelle ça un invariant Je pense qu'on peut d'ailleurs faire la même transformation de tout invariant en série de post-conditions. Ce qui fait que je le vois un invariant, et pas simplement une série de post-conditions, c'est qu'il est cohérent pour toutes les fonctions de ma classe et que par exemple, cet invariant explique l'absence dans ma classe de fonction simplifie, qui n'a pas lieu d'être (alors qu'elle pourrait légitimement faire partie d'une classe fraction avec un autre design).

    Citation Envoyé par white_tentacle Voir le message
    Alors, setNumerateur est un très mauvais nom, car il mélange du français et de l’anglais .
    Citation Envoyé par white_tentacle Voir le message
    Ensuite, j’attaquerai le problème non pas dans le setter, mais dans le getter.

    Change getNumerateur par (get)NumerateurReduit. On aura donc un couple setNumerateur / getNumerateurReduit, et pas de getNumerateur. La dyssimétrie doit mettre sur la piste que non, l’opération n’est pas symétrique. Quant à ton invariant de classe ? En tant que client, je m’en fiche, c’est ton invariant interne. Tu peux choisir celui que tu veux, du moment que getNumerateurReduit respecte son contrat, à savoir me renvoyer le numérateur de la fraction réduite.
    Je n'aime pas trop la présence de la fonction getNumerateurReduit, dans le sens où elle demande implicitement, de par son nom, l'existence d'une fonction getNumerateurNonReduit, alors que cet notion n'existe pas dans une classe "fraction toujours simplifiée" comme la mienne. A l'inverse, si j'avais décidé de gérer des fractions où cout << Fraction(2,4) afficherait 2/4, une fonction getNumerateurReduit() me semblerait plus justifiée.
    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.

  11. #91
    Membre Expert
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Par défaut
    Citation Envoyé par JolyLoic Voir le message
    Objection votre honneur ! C'est un invariant publique ! C'est lui qui permet de dire que cout << Fraction(2,4) affichera 1/2. Ou que Fraction(2,4).numerateur() == 1, ou encore que Fraction(1,2) == Fraction(2,4)
    Je vais faire mal aux drosophiles, mais tant pis . Rien ne t’empêche de stocker en interne les valeurs initiales, et d’opérer la réduction au moment de la sérialisation / affichage. C’est pour ça qu’un invariant sur la « représentation interne » n’est pas vraiment public pour moi.

    (alors que ces fractions ne sont pas égales au sens mathématiques). Alors, certes, on peut dire qu'il ne s'agit que d'une post condition qui bien que toujours vraie, n'a vraiment de sens que pour certaines fonctions. Mais une telle post-condition, moi j’appelle ça un invariant Je pense qu'on peut d'ailleurs faire la même transformation de tout invariant en série de post-conditions. Ce qui fait que je le vois un invariant, et pas simplement une série de post-conditions, c'est qu'il est cohérent pour toutes les fonctions de ma classe et que par exemple, cet invariant explique l'absence dans ma classe de fonction simplifie, qui n'a pas lieu d'être (alors qu'elle pourrait légitimement faire partie d'une classe fraction avec un autre design).
    Une postcondition universelle, j’appelle aussi ça un invariant, oui . Finalement, ton invariant, c’est plutôt « elle est tout le temps visible sous forme simplifiée » (les mouches, toussa…).

    Je n'aime pas trop la présence de la fonction getNumerateurReduit, dans le sens où elle demande implicitement, de par son nom, l'existence d'une fonction getNumerateurNonReduit, alors que cet notion n'existe pas dans une classe "fraction toujours simplifiée" comme la mienne. A l'inverse, si j'avais décidé de gérer des fractions où cout << Fraction(2,4) afficherait 2/4, une fonction getNumerateurReduit() me semblerait plus justifiée.
    C’est bien pour ça que je l’appellerai comme ça. J’y vois plusieurs avantages :
    - la fonction est plus explicite sur ce qu’elle fait, et l’absence de getNumerateurNonReduit peut amener l’utilisateur à se poser des questions sur le comportement de la classe
    - un template qui cherche une fonction getNumerateurReduit va fonctionner, que la fonction soit toujours stockée sous forme réduite ou pas
    - un template qui cherche un couple de fonctions getNumerateur/setNumerateur ne fonctionnera pas, et c’est tant mieux ! Il y a des chances que ce code fasse de mauvaises assomptions sur la sémantique de ces fonctions.

    Quelque part, si c’est un objet différent (au sens, il a un comportement différent), je préfère qu’il soit visiblement différent plutôt qu’il essaie de se faire passer pour un autre.

  12. #92
    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
    Tu ne veux pas prendre un autre exemple qui serait pertinent ?
    Car modifier numérateur et dénominateur l'un indépendamment de l'autre est pour moi un total non sens. Cette classe ne devrait contenir aucun mutateur qui travaille sur un seul des deux nombres -- exactement comme pour les complexes, ou des dates.
    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...

  13. #93
    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 : 51
    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
    Par défaut
    Tu sais à quel point il n'est pas facile de prendre des exemples à la fois courts, que tout le monde comprend sans trop d’ambiguïté, et pertinents... D'ailleurs, mon autre exemple, basé sur l'usage du pattern observer, n'a pas suscité le moindre commentaire...

    Je vais en tenter un autre, qui respecte que get et set sont symétriques, mais pas qu'ils soient unitaires... Une classe couleur qui proposerait des propriétés accessibles en écriture R, G, B, H, S, V.

    Ou encore une classe slider qui possède des propriétés accessibles en écriture Min, Max, et Value. Si l'utilisateur tente de positionner une Value supérieure à Max, silencieusement, on aura Value == Max. Ce n'est pas un exemple abstrait, mais au contraire bien réel, et les concepteurs de cette bibliothèque (WPF, certes, ce n'est pas du C++, mais l'idée est la même) ont choisi de ne pas lever d'erreur dans cette situation, mais de modifier la valeur lors d'un set. Et ils ont considéré ce pattern comme assez important pour définir un framework de coercition de propriétés, avec des validateurs sous forme de callbacks appelés à chaque tentative de modifier la propriété (http://msdn.microsoft.com/en-us/libr...ecallback.aspx).
    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.

  14. #94
    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
    Tu n'as pas eu de réactions pour le pattern observer parce que c'est probablement la raison la plus valide de toutes pour justifier des setters. Raison qui ne casse pas la symétrie.

    Pour les couleurs, je balance entre : "l'intérêt m'échappe" et "attention programmation défensive". L'intérêt m'échappe quand les couleurs sont dans {unsigned char}^3/6. (je n'ai jamais vu des couleurs dans {[0-42]}^3/6). Et si elles sont dans {[0.0-1.0]}^3, certes il faut borner. Mais là, c'est côté interaction avec l'utilisateur qu'il faut opérer la vérification du contrat à mon avis (quand problème d'IHM/IO). Si c'est une erreur de programmation, bonne chance pour la trouver si tout le code est écrit croyant que l'on est dans {[0-255]^3}, ou {[0-2^16]}^3 (par ce qu'elle est absorbée -- oui, je fais une réaction allergique à la programmation défensive).

    Pour le slider, je ne réagis pas non plus car je tends à considérer les GUI comme un métier à part, un des rares qui justifie des écarts de conceptions sur des setters.
    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...

  15. #95
    r0d
    r0d est déconnecté
    Membre expérimenté

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    4 303
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Août 2004
    Messages : 4 303
    Billets dans le blog
    2
    Par défaut
    Pour la couleur, je trouve que le choix de Laurent Gomilla dans sa SFML est le meilleur: r,g,b et a sont des variables membre publiques.
    J'en profite pour aborder un point que je n'ai pas vu dans cette discussion et qui m'intéresse: j'ai l'impression que bien souvent (pas toujours bien évidemment, mais peut-être plus souvent que l'on ne croit), la solution de la variable membre publique est satisfaisante. Car ça a l'avantage d'être clair en terme de contrat, on dit clairement à l'utilisateur: tu fais ce que tu veux avec cette donnée, donc si tu y touches, c'est toi qui en a toute la responsabilité.

  16. #96
    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 : 51
    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
    Par défaut
    Mais du coup, il n'est pas possible de travailler en HLS ou HLV, ou CIELAB... ce qui est très dommage pour tout un tas d'application 3D (probablement pas pour des jeux, mais pour toute application scientifique, clairement).
    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.

  17. #97
    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
    Hum ... Quand on travaille dans cet espace, il me parait plus efficace de projeter toute l'image (ou une tuile) dans cet espace. Car on ne va pas travailler sur un pixel à la fois.
    Une fois fini, on peut reprojeter vers l'espace RVB de départ.
    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...

  18. #98
    Expert éminent

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    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 202
    Par défaut
    Cela dit, dans certains systemes, les couleurs ont une sémantique d'entité…

Discussions similaires

  1. fonctions et classes... quand les utiliser ?
    Par fastmanu dans le forum Langage
    Réponses: 6
    Dernier message: 03/04/2006, 00h39
  2. Quand les tableaux deviènent vos pires enemis...
    Par Zenol dans le forum Balisage (X)HTML et validation W3C
    Réponses: 10
    Dernier message: 13/11/2005, 21h23
  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