Chaque numéro du code est-il considéré comme un «nombre magique»?

21

Donc, chaque nombre dans le code que nous envoyons à une méthode comme argument est considéré comme un nombre magique? Pour moi, ça ne devrait pas. Je pense que si un certain nombre est disons que c'est pour la longueur minimale du nom d'utilisateur et que nous commençons à utiliser "6" dans le code ... alors oui nous avons un problème de maintenance et ici "6" est un nombre magique .... mais si nous appelons une méthode que l'un de ses arguments accepte par exemple un entier comme le ième membre d'une collection et que nous passons ensuite "0" à cet appel de méthode, dans ce cas je ne vois pas "0" comme une magie nombre. Qu'est-ce que tu penses?

Blake
la source
4
Dans votre exemple, que représente le 0?
Aaron Kurtzhals
2
Dans le cas que vous illustrez, ce "0" n'a absolument aucune propriété magique.
Tulains Córdova
4
Tout sauf 0,1 et 42 est magique
Mawg dit réintégrer Monica

Réponses:

43

Si la signification du nombre est très claire dans le contexte, je ne pense pas que ce soit un problème de "nombre magique".

Exemple: disons que vous essayez d'obtenir la sous-chaîne d'une chaîne, du début à un jeton et que le code ressemble à ceci (langage et bibliothèque imaginaires):

s := substring(big_string, 0, findFirstOccurence(SOME_TOKEN, big_string));

Dans ce contexte, la signification du nombre 0 est suffisamment claire. Je suppose que vous pouvez le définir START_OF_SUBSTRINGet le mettre à 0, mais dans ce cas, je pense que ce serait exagéré (bien que ce soit la bonne approche si vous saviez que le début de votre sous-chaîne pourrait ne pas être 0, mais cela dépend des spécificités de ta situation).

Un autre exemple pourrait être si vous essayez de déterminer si un nombre est pair ou impair. L'écriture:

isEven := x % 2;

n'est pas aussi étrange que:

TWO := 2;
isEven := x % TWO;

Test des nombres négatifs comme

MINUS_ONE := -1;
isNegativeInt := i <= MINUS_ONE;

me semble aussi bizarre, je préfère de loin

isNegativeInt := i <= -1;
FrustratedWithFormsDesigner
la source
6
Pour lancer un autre exemple là-bas, dans le code où vous travaillez explicitement avec des degrés sur un cercle, il serait juste d'utiliser un nombre tel que 360pour marquer une rotation complète en sachant que la plupart des gens sauront ce que cela signifie (bien que cela est un cas où cela ne ferait pas de mal de fournir une constante)
KChaloux
11
KChaloux: Si je le pouvais, j'aimerais -1 votre commentaire. 360 est un nombre magique. Si 360 se trouve être une valeur pour une autre constante, alors vous avez 2 jeux pour 360 qui ne sont pas liés et ne se distinguent pas. Junior arrive, allez "C'est un nombre magique", recherchez globalement et remplacez 360 par "Degrees_in_Circle", lancez tous les tests unitaires et de régression, tout passe - délivre le code correctif. Code maintenant un petit déjeuner pour chiens, et nous savons tous ce qui arrive à cela après un court laps de temps .......
mattnz
4
@mattnz: Espérons que ce type de changement de code à grande échelle sera rapidement détecté (espérons-le lors de la révision du code, s'il est aussi junior) bien avant qu'il ne soit mis en production. Je pense que quelqu'un qui ferait cela dans ce contexte remplacerait probablement aussi 0dans le contexte de l'exemple ma sous-chaîne. Dans ce cas, cela pourrait être le moins de dégâts possible. Cela fait longtemps que je n'ai fait aucun codage qui a fait des calculs géométriques, mais généralement, les valeurs 15, 30, 45, 60, 90, 180, 360 étaient des constantes qui ont été acceptées. Je n'ai jamais vu personne définir FIFTEEN_DEGREES, ...
FrustratedWithFormsDesigner
5
@KChaloux L'exemple peut en fait s'effondrer s'il y a un changement de degrés en radians. En 360, vous exprimez 1 rotation complète. Puisqu'il existe plusieurs représentations pour la même valeur, elle doit être retirée. Surtout si l'on considère que 360PI pourrait ressembler à 2PI (180 rotations mais toujours dans la même direction à la fin), ou 360 rotations comme 1 rotation, mais les effets secondaires peuvent être différents.
Chris
14
Un peu paille là-bas, TWO et MINUS_ONE sont tout à fait mauvais parce que remplacer un nombre magique par son rendu dans le texte est bien sûr idiot. Le nom de la constante doit transmettre sa signification. Sauf que vos exemples concernent des faits fondamentaux sur les chiffres, étroitement liés à ces chiffres spécifiques, il n'y a donc pas vraiment de sens au-delà.
Michael Borgwardt
17
bool hasApples = apples > 0;

Il est évident que zéro signifie absence. Je trouve 0 plus facile à comprendre qu'une variable nommée "absenceValue".


for(int i=0; i < arr.length; i++)

Il est évident que 0 est la position de départ. Je serais confus par une variable nommée "firstPosition". Une telle variable me ferait me demander si la position de départ pouvait changer.

mike30
la source
14

Je suggérerais trois facteurs clés pour décider si quelque chose devrait être une déclaration constante:

  1. Le nombre est-il quelque chose de représentable avec précision et concision
  2. Existe-t-il des scénarios plausibles dans lesquels la valeur devrait changer, mais le code ne devrait pas être réécrit
  3. Est-ce que quelqu'un qui voit le nombre serait capable de le reconnaître plus rapidement ou moins rapidement que quelqu'un qui voit une constante nommée

Quelque chose comme pi devrait probablement être écrit comme une constante nommée, plutôt que comme un littéral numérique, car un littéral numérique est susceptible d'être inutilement verbeux, inutilement imprécis, ou les deux. Quelque chose comme le nombre d'emplacements dans un cache devrait probablement être une constante nommée (bien que voir la note ci-dessous) pour permettre la possibilité d'étendre le cache sans avoir à modifier tout le code qui l'utilise. Des choses comme les nombres «4», «28» et «29» dans l'instruction if ((year % 4)==0) FebruaryDays = 29; else FebruaryDays = 28;ne devraient probablement pas être nommées constantes, car l'expression est presque certainement plus lisible que if ((year % YearsBetweenLeapYears)==0) FebruaryDays = FebruaryDaysInLeapYear; else FebruaryDays = FebruaryDaysInNonLeapYear;. Notez que les responsables des normes ont indiqué que la durée du 21 février de cette année ne correspondra pas à la formule ci-dessus, obstacle au traitement correct de ces dates (c'est-à-dire que le code ne sera pas déclenché par un débordement d'entier ou d'autres problèmes de ce type)

Une mise en garde importante avec la règle n ° 2 est que, dans certains cas, le code peut s'appuyer sur des nombres codés en dur d'une manière qui ne peut pas être facilement représentée par une constante nommée. Par exemple, une méthode qui calcule un produit croisé de deux vecteurs passés comme paramètres discrets n'aura de sens que lorsqu'elle est utilisée sur des vecteurs tridimensionnels. Le nombre requis de dimensions n'est pas une valeur qui pourrait être modifiée de manière significative sans réécrire complètement la routine. Même si l'on prévoyait un besoin éventuel de calculer le produit croisé de trois vecteurs à quatre dimensions, l'utilisation d'une constante nommée pour la valeur "3" ne contribuerait guère à faciliter la satisfaction de ce besoin.

supercat
la source
4

Comme tous les principes, c'est une question de degré. De manière générale, les littéraux numériques dans le code source sont d'autant plus suspects qu'ils sont grands. Une longueur maximale comme 10 ou une adresse mémoire comme 0x587FB0 est évidemment une mauvaise pratique - il est presque certain que tôt ou tard vous devrez répéter ces valeurs plus d'une fois, créant un risque d'incompatibilité et d'erreurs subtiles introduites dans des endroits qui n'étaient pas modifié.

0 est à l'autre extrémité de l'échelle; c'est encore suspect mais pas autant. Utilisez-vous 0 comme valeur sentinelle? Ensuite, vous devriez probablement utiliser une constante symbolique à la place, simplement parce que la constante peut expliquer ce que cela signifie. S'agit-il d'un accord culturel extrêmement enraciné tel que «0 signifie une réussite»? C'est probablement OK. Cela signifie-t-il "le premier article d'une collection"? Cela peut être inoffensif, mais s'il existe une autre méthode telle que first()je préférerais probablement cela.

Kilian Foth
la source
1
"Utilisez-vous 0 comme valeur sentinelle?" <- Pouvez-vous expliquer ce que vous entendez par "sentinelle" ici? Je ne trouve pas de définition qui semble correspondre.
rory.ap
3

Chaque nombre sans nom qui n'est pas immédiatement évident du contexte est un nombre magique. C'est un peu idiot de définir des nombres qui ont une signification immédiatement évidente du contexte.

Dans django (framework web python), je peux définir un champ de base de données avec un nombre brut comme:

firstname = models.CharField(max_length=40)
middlename = models.CharField(max_length=40)
lastname =  models.CharField(max_length=40) 

qui est plus clair (et le pratique recommandée ) que de dire

MAX_LENGTH_NAME = 40
...
firstname = models.CharField(max_length=MAX_LENGTH_NAME)
middlename = models.CharField(max_length=MAX_LENGTH_NAME)
lastname =  models.CharField(max_length=MAX_LENGTH_NAME) 

car je ne vais probablement jamais avoir besoin de changer la longueur (et je peux toujours la comparer à celle max_lengthdu champ). Si j'ai besoin de modifier la longueur du champ après avoir déployé l'application initialement, je dois le changer à exactement un emplacement par champ dans mon code django, puis écrire en outre une migration pour modifier le schéma de la base de données. Si j'ai besoin de référencer max_lengthun champ défini d'un type d'objet, je peux le faire directement - si ces champs définissaient une Personclasse, je peux l'utiliser Person._meta.get_field('firstname').max_lengthpour obtenir lemax_lengthutilisé (qui est défini en un seul endroit). Le fait que le même 40 ait été utilisé pour plusieurs champs n'est pas pertinent car je peux vouloir les changer indépendamment. La longueur du prénom ne doit jamais dépendre de la longueur du prénom ou du nom de famille; ce sont des valeurs distinctes et peuvent changer indépendamment.

Souvent, les indices matriciels peuvent utiliser des nombres sans nom; comme si j'avais un fichier CSV de données que je voulais mettre dans un dictionnaire python, avec le premier élément de la ligne comme dictionnaire keyj'écrirais:

mydict = {}
for row in csv.reader(f):
    mydict[row[0]] = row[1:]

Bien sûr, je pourrais nommer index_column = 0et faire quelque chose comme:

index_col = 0
mydict = {}
for row in csv.reader(f):
    mydict[row[index_col]] = row[:index_col] + row[index_col+1:]

ou pire définir after_index_col = index_col + 1pour se débarrasser de index_col+1, mais cela ne rend pas le code plus clair à mon avis. De plus, si je donne index_colun nom, je ferais mieux de faire fonctionner le code même si la colonne n'est pas 0 (d'où la row[:index_col] +partie).

dr jimbob
la source
7
En fait, max_lngth=40vs max_length=MAX_LENGTH_NAMEest un exemple classique d'un nombre magique qui crie pour être un symbole. Le jour viendra où vous voudrez prendre en charge 45 noms de caractères, et maintenant chaque utilisation de "40" est suspecte et doit être soigneusement examinée.
Ross Patterson
1
@RossPatterson - Ce n'est pas C où nous comparons constamment contre une var globale MAX_ARRAY_SIZE, mais un cadre web décent. Le seul endroit où le nombre magique apparaît est l'endroit où vous déclarez le modèle de base de données; tout le reste est comparé à cette valeur (par exemple, 40 n'apparaît nulle part ailleurs dans le code). Notez également que vous ne pouvez pas modifier cette variable facilement sans effectuer des migrations de schéma car elle est liée à une base de données. Si je voulais changer de dire 1 caractères prénoms son immédiatement évident que le seul endroit au changement dans le code 40à 1. Vous devez penser au contexte.
dr jimbob
2
Désolé, vous vous trompez sur deux points. Tout d'abord, le PO a posé une question sur les "pratiques de programmation" qui ne spécifie aucun langage. Ils ont dit "méthode", pas "fonction", supposons donc quelque chose orienté objet, mais cela ne nous fait pas sortir du domaine des données magiques. Deuxièmement, si le nombre magique est intégré dans la base de données ( par exemple , le schéma), il est encore pire de l'avoir dans le code. La bonne chose à faire est d'obtenir la magie presque une constante de sa source - soit la base de données elle-même, soit un module de schéma qui centralise toutes ces constantes qui varieront au cours de la vie du code.
Ross Patterson