Commentaire correct à mettre pour les arguments de fonction booléenne qui sont «faux»?

19

À partir de certains projets open source, j'ai rassemblé le style de codage suivant

void someFunction(bool forget);

void ourFunction() {
  someFunction(false /* forget */);
}    

J'ai toujours un doute sur ce que cela falsesignifie ici. Cela signifie-t-il "oublier", ou "oublier" fait-il référence à son paramètre correspondant (comme dans le cas ci-dessus), et "faux" est-il censé le nier?

Quel style est utilisé le plus souvent et quel est le meilleur moyen (ou certains des meilleurs moyens) pour éviter l'ambiguïté?

Johannes Schaub - litb
la source
38
utiliser des énumérations (même s'il n'y a que 2 options) au lieu des bools
Esailija
21
Certaines langues prennent en charge les arguments nommés. Dans ces langues, vous pouvez utilisersomeFunction(forget: true);
Brian
3
Je me sens obligé de fournir les arguments de Martin Fowler sur les arguments de drapeau (parle également des colons booléens). En général, essayez de les éviter.
FGreg
3
Pour comprendre l'évidence, des commentaires peuvent mentir. Est donc toujours préférable de faire votre auto-documentant code, car certains vont changer trueà falseet mettre à jour le commentaire. Si vous ne pouvez pas modifier l'API, alors la meilleure façon de commenter cela estsomeFunction( false /* true=forget, false=remember */)
Mark Lakata
1
@Bakuriu - en fait, j'aurais probablement encore l'API publique avec deux méthodes distinctes ( sortAscendinget sortDescending, ou similaire). Maintenant, à l' intérieur , ils peuvent tous deux appeler la même méthode privée, qui pourrait avoir ce type de paramètre. En fait, si la langue le supportait, ce que je passerais probablement serait une fonction lambda qui contiendrait le sens du tri ...
Clockwork-Muse

Réponses:

33

Dans l'exemple de code que vous avez publié, il ressemble à forgetun argument indicateur. (Je ne peux pas en être certain car la fonction est purement hypothétique.)

Les arguments de drapeau sont une odeur de code. Ils indiquent qu'une fonction fait plus d'une chose et qu'une bonne fonction ne devrait faire qu'une seule chose.

Pour éviter l'argument indicateur, divisez la fonction en deux fonctions qui expliquent la différence dans le nom de la fonction.

Argument indicateur

serveIceCream(bool lowFat)

Aucun argument de drapeau

serveTraditionalIceCream()
serveLowFatIceCream()

edit: Idéalement, vous n'avez pas du tout besoin de rester autour d'une fonction avec le paramètre flag. Il existe des cas dans le sens de ce que Fowler appelle une implémentation enchevêtrée où la séparation complète des fonctions crée du code dupliqué. Cependant, plus la complexité cyclomatique de la fonction paramétrée est élevée, plus l'argument pour s'en débarrasser est fort.


Ce n'est qu'une intuition, mais un paramètre nommé forgetsonne comme l'envie de fonctionnalités. Pourquoi un appelant dirait-il à un autre objet d'oublier quelque chose? Il peut y avoir un problème de conception plus important.

Aaron Kurtzhals
la source
4
+17, casque. Qu'est-ce que les corps de serveTraditionalIceCreamet serveLowFatIceCreamressembler? J'ai une énumération avec 14 types de crème glacée.
JohnMark13
13
Pour les méthodes publiques, cette convention est bonne, mais comme JohnMark le mentionne, l'autre moitié de SRP est "une bonne méthode devrait être la seule méthode qui fasse ce qu'elle fait". Je vois qu'il y a N + 1 variantes de cette méthode; N public sans paramètres, qui appellent tous une méthode privée avec le paramètre que vous essayez d'éviter d'exposer. À un moment donné, vous abandonnez et exposez le fichu paramètre. Les odeurs de code ne signifient pas nécessairement que le code doit être refactorisé; ce sont juste des choses qui méritent un second regard dans une revue de code.
KeithS
@Aaron Par "fonctionnalité envie" ce que vous vouliez dire?
Geek
27

Dans les mots du profane:

  • false est un littéral.
  • vous passez un littéral false
  • tu dis someFunctionde ne pas oublier
  • vous dites someFunctionque le paramètre oublier estfalse
  • tu dis someFunctionde te souvenir

À mon avis, il serait préférable que la fonction soit comme ceci:

void someFunction(bool remember);

le vous pouvez l'appeler

void ourFunction() {
  someFunction(true);
} 

ou conservez l'ancien nom mais changez votre fonction wrapper en

void ourFunctionWithRemember() {
  someFunction(false);
} 

ÉDITER:

Comme l'a indiqué @Vorac, efforcez-vous toujours d'utiliser des mots positifs. La double négation prête à confusion.

Tulains Córdova
la source
15
+1 pour que l'idée s'efforce toujours d'utiliser des mots positifs. La double négation prête à confusion.
Vorac
1
D'accord, bonne idée. Dire au système de ne pas faire quelque chose est déroutant. Si vous acceptez un paramètre booléen, exprimez-le positivement.
Brandon
Dans la plupart des cas, je pense que vous voudriez également être plus précis que remember, à moins que le nom de la fonction ne fasse le sens deremember très évident. rememberToCleanUp* or *persistou quelque chose.
itsbruce
14

Le paramètre peut être bien nommé; c'est difficile à dire sans connaître le nom de la fonction. Je suppose que le commentaire a été écrit par l'auteur original de la fonction, et ce fut un rappel de ce qui passe falsedans les someFunctionmoyens, mais pour ceux qui viennent le long après, il est un peu difficile de prime abord.

L'utilisation d'un nom de variable positif (suggéré dans Code Complete ) peut être le changement le plus simple qui rendrait cet extrait plus facile à lire, par exemple

void someFunction(boolean remember);

ourFunctiondevient alors :

void ourFunction() {
    someFunction(true /* remember */);
}

Cependant, l'utilisation d'une énumération rend l'appel de fonction encore plus facile à comprendre, au détriment de certains codes de support:

public enum RememberFoo {
    REMEMBER,
    FORGET
}

...

void someFunction(RememberFoo remember);

...

void ourFunction() {
    someFunction(RememberFoo.REMEMBER);
}

Si vous ne pouvez pas modifier la signature de someFunctionpour une raison quelconque, l'utilisation d'une variable temporaire facilite également la lecture du code, un peu comme la simplification des conditions en introduisant une variable pour aucune autre raison que pour rendre le code plus facile à analyser par les humains .

void someFunction(boolean remember);

...

void ourFunction() {
    boolean remember = false;
    someFunction(remember);
}
Mike Partridge
la source
1
Mettre remembersur vrai signifie oublier (dans votre exemple de someFunction(true /* forget */);)?
Brian
2
C'est enumde loin la meilleure solution. Ce n'est pas parce qu'un type peut être représenté comme bool- c'est-à-dire qu'il est isomorphe - qu'il doit être représenté comme tel. Le même argument s'applique à stringet même int.
Jon Purdy
10

Renommez la variable pour qu'une valeur booléenne ait un sens.

C'est un million de fois mieux que d'ajouter un commentaire pour expliquer les arguments d'une fonction car le nom est ambigu.

enderland
la source
3
Cela ne répond pas à la question. Tout est évident lorsque la méthode est définie et appelée dans le même fichier à 4 lignes d'intervalle. Mais que se passe-t-il si tout ce que vous voyez en ce moment est l'appelant? Et s'il a plusieurs booléens? Parfois, un simple commentaire en ligne va très loin.
Brandon
@Brandon Ce n'est pas un argument contre l'appel du booléen doNotPersist (ou, mieux, Persist). L'appeler «oublier» sans dire quoi oublier est franchement inutile. Oh, et une méthode qui prend plusieurs booléens en option pue au paradis.
itsbruce
5

Créez un booléen local avec un nom plus descriptif et affectez-lui la valeur. De cette façon, le sens sera plus clair.

void ourFunction() {
    bool takeAction = false;  /* false means to forget */
    someFunction( takeAction );
}    

Si vous ne pouvez pas renommer la variable, alors le commentaire devrait être un peu plus expressif:

void ourFunction() {
    /* false means that the method should forget what was requested */
    someFunction( false );
}    

la source
1
Certainement un bon conseil, mais je ne pense pas qu'il résout le problème que le /* forget */commentaire est là pour résoudre, à savoir que sans la déclaration de fonction juste devant vous, il peut être difficile de se rappeler ce qui est réglé false. (C'est pourquoi je pense que les conseils de @ Esailija pour ajouter une énumération sont meilleurs, et pourquoi j'aime les langages qui autorisent les paramètres nommés.)
Gort the Robot
@StevenBurnap - merci! Vous avez raison, mon ancienne réponse n'était pas suffisamment claire pour répondre à la question du PO. Je l'ai édité pour le rendre plus clair.
3

Il y a un bon article qui mentionne cette situation exacte car elle se réfère aux API Qt-Style. Là, ça s'appelle le piège des paramètres booléens et il vaut la peine d'être lu.

L'essentiel est:

  1. Surcharger la fonction pour que le bool ne soit pas nécessaire est mieux
  2. Il est préférable d'utiliser une énumération (comme le suggère Esailija)
Steven Evers
la source
2

Ceci est un commentaire bizarre.

Du point de vue du compilateur, someFunction(false /* forget */);est en fait someFunction(false);(le commentaire est supprimé). Donc, tout ce que cette ligne fait est d'appeler someFunctionavec le premier (et le seul) argument défini surfalse .

/* forget */est juste le nom du paramètre. Ce n'est probablement rien de plus qu'un rappel rapide (et sale), qui n'a pas vraiment besoin d'être là. Utilisez simplement un nom de paramètre moins ambigu et vous n'aurez pas du tout besoin du commentaire.

user2590712
la source
1

Un des conseils du code Clean est de minimiser le nombre de commentaires inutiles 1 (car ils ont tendance à pourrir) et de nommer correctement les fonctions et les méthodes.

Après cela, je voudrais simplement supprimer le commentaire. Après tout, les IDE modernes (comme eclipse) font apparaître une boîte avec du code lorsque vous placez une souris sur la fonction. Voir le code devrait lever toute ambiguïté.


1 Commenter du code complexe est correct.

BЈовић
la source
btw Qui a dit quelque chose comme ceci: "le pire problème du programmeur est de savoir comment nommer une variable et la compenser par une"?
BЈовић
4
Vous recherchez probablement martinfowler.com/bliki/TwoHardThings.html comme source. J'ai entendu dire: "Il n'y a que deux choses difficiles en informatique: l'invalidation du cache, la dénomination des choses et une erreur".
1

Pour comprendre l'évidence, des commentaires peuvent mentir. Il est donc toujours préférable de rendre votre code auto-documenté sans recourir à des commentaires pour l'expliquer, car une personne (peut-être vous) changera truepourfalse et mettre à jour le commentaire.

Si vous ne pouvez pas changer l'API, je recourt à 2 options

  • Modifiez le commentaire afin qu'il soit toujours vrai, quel que soit le code. Si vous n'appelez cela qu'une seule fois, c'est une bonne solution, car elle maintient la documentation locale.
     someFunction (false / * true = oublie, false = souviens * /); `
  • Utilisez #defines, surtout si vous l'appelez plusieurs fois.
     #define FORGET true
     #define REMEMBER false
     someFunction (REMEMBER);
Mark Lakata
la source
1

J'aime la réponse pour rendre le commentaire toujours vrai , mais bien que je pense qu'il manque le problème fondamental avec ce code - il est appelé avec un littéral.

Vous devez éviter d'utiliser des littéraux lors de l'appel de méthodes. Variables locales, paramètres facultatifs, paramètres nommés, énumérations - la meilleure façon de les éviter dépendra de la langue et de ce qui est disponible, mais essayez de les éviter. Les littéraux ont des valeurs, mais ils n'ont pas de sens.

jmoreno
la source
-1

En C #, j'ai utilisé des paramètres nommés pour rendre cela plus clair

someFunction(forget: false);

Ou enum:

enum Memory { Remember, Forget };

someFunction(Memory.Forget);

Ou surcharges:

someFunctionForget();

Ou polymorphisme »:

var foo = new Elephantine();

foo.someFunction();
Jay Bazuzi
la source
-2

La dénomination doit toujours résoudre l'ambiguïté des booléens. Je nomme toujours quelque chose de booléen comme 'isThis' ou 'shouldDoThat', par exemple:

void printTree(Tree tree, bool shouldPrintPretty){ ... }

etc. Mais lorsque vous référencez le code de quelqu'un d'autre, il est préférable de laisser un commentaire lors du passage de valeurs.

dchhetri
la source
comment cela répond-il à la question posée?
moucher
@gnat Je répondais à sa question sur la résolution de l'ambiguïté avec les paramètres booléens. J'ai peut-être mal lu sa question.
dchhetri