Utilisation de chaînes / nombres magiques [fermé]

31

C'est un sujet quelque peu controversé, et je suppose qu'il y a autant d'opinions que de programmeurs. Mais pour le plaisir, je veux savoir quelles sont les pratiques courantes en entreprise (ou dans vos lieux de travail).

Sur mon lieu de travail, nous avons des directives de codage strictes. Une section est consacrée aux chaînes / nombres magiques. Il indique (pour C #):

N'utilisez pas de valeurs littérales, numériques ou chaînes, dans votre code autrement que pour définir des constantes symboliques. Utilisez le modèle suivant pour définir les constantes:

public class Whatever  
{  
   public static readonly Color PapayaWhip = new Color(0xFFEFD5);  
   public const int MaxNumberOfWheels = 18;  
}

Il existe des exceptions: les valeurs 0, 1 et null peuvent presque toujours être utilisées en toute sécurité. Très souvent, les valeurs 2 et -1 sont également correctes. Les chaînes destinées à la journalisation ou au traçage sont exemptées de cette règle. Les littéraux sont autorisés lorsque leur signification est claire par rapport au contexte et non sujets à de futurs changements.

mean = (a + b) / 2; // okay  
WaitMilliseconds(waitTimeInSeconds * 1000); // clear enough

Une situation idéale serait un document de recherche officiel montrant les effets sur la lisibilité / maintenabilité du code lorsque:

  • Les nombres / cordes magiques sont partout
  • Les chaînes / nombres magiques sont remplacés par des déclarations constantes raisonnablement (ou à différents degrés de couverture) - et s'il vous plaît ne me criez pas pour utiliser "raisonnablement", je sais que tout le monde a une idée différente de ce qui est "raisonnablement"
  • Les chaînes / nombres magiques sont remplacés en excès et dans des endroits où ils ne devraient pas être (voir mon exemple ci-dessous)

Je voudrais le faire pour avoir des arguments scientifiquement fondés lorsque je me dispute avec un de mes collègues, qui va au point de déclarer des constantes comme:

private const char SemiColon = ';';
private const char Space = ' ';
private const int NumberTen = 10;

Un autre exemple serait (et celui-ci est en JavaScript):

var someNumericDisplay = new NumericDisplay("#Div_ID_Here");

Collez-vous les ID DOM au-dessus de votre fichier javascript si cet ID n'est utilisé qu'à un seul endroit?

J'ai lu les rubriques suivantes:
StackExchange
StackOverflow
Bytes IT Community
Il y a beaucoup plus d'articles, et après avoir lu ces quelques modèles émergent.

Donc, ma question est d'utiliser les chaînes et les nombres magiques dans notre code? Je recherche spécifiquement des réponses d'experts qui sont appuyées par des références si possible.

Daniel Gruszczyk
la source
2
Une variable magique est une variable qui contient une signification qui n'est pas reflétée par son contenu. La valeur entière «10» reflète la signification du nombre 10, il n'est donc pas nécessaire d'en faire une constante. Il en va de même pour l'espace et le point-virgule. D'un autre côté, si vous avez une valeur '%% ?? %%' et qu'il s'agit d'un délimiteur personnalisé, celui-ci DOIT être placé en tant que constante car son contenu ne reflète pas le fait qu'il s'agit d'un délimiteur.
Jeroen Vannevel
23
NumberTen = 10C'est inutile car le chiffre 10 ne sera pas redéfini. MaxRetryCount = 10Cela a un point a, nous pouvons vouloir changer le nombre maximal de nouvelles tentatives. private const char SemiColon = ';'; Stupide. private const char LineTerminator = ';'; Intelligent.
Mike
1
La vraie question n'est pas claire.
Tulains Córdova

Réponses:

89

... en discutant avec un de mes collègues, qui va jusqu'à déclarer des constantes comme:

private const char SemiColon = ';';
private const char Space = ' ';
private const int NumberTen = 10;

L'argument que vous devez faire valoir avec votre collègue ne concerne pas le fait de nommer un espace littéral, Spacemais son mauvais choix de nom pour ses constantes.

Supposons que le travail de votre code consiste à analyser un flux d'enregistrements qui contiennent des champs séparés par des points-virgules ( a;b;c) et sont eux-mêmes séparés par des espaces ( a;b;c d;e;f). Si la personne qui a rédigé votre spécification vous appelle dans un mois et vous dit: "nous nous sommes trompés, les champs des enregistrements sont séparés par des symboles de tuyau ( a|b|c d|e|f)", que faites-vous?

Dans le cadre de la valeur en tant que nom que préfère votre collègue, vous devez modifier la valeur de literal ( SemiColon = '|') et vivre avec du code qui continue à être utilisé SemiColonpour quelque chose qui n'est plus vraiment un point-virgule. Cela conduira à des commentaires négatifs dans les revues de code . Pour réduire cela, vous pouvez changer le nom du littéral en PipeSymbolet parcourir et changer chaque occurrence de SemiColonen PipeSymbol. À ce rythme, vous pourriez tout aussi bien utiliser un point-virgule littéral ( ';') en premier lieu, car vous devrez évaluer chaque utilisation individuellement et vous apporterez le même nombre de modifications.

Identifiants pour les constantes doivent être descriptif de ce que la valeur ne , pas ce que la valeur est , et c'est où votre collègue a fait un virage à gauche dans les mauvaises herbes. Dans l'application de séparation de champs décrite ci-dessus, le point-virgule a pour fonction de séparer les champs et les constantes doivent être nommées en conséquence:

private const char FieldSeparator = ';';    // Will become '|' a month from now
private const char RecordSeparator = ' ';
private const int MaxFieldsPerRecord = 10;

De cette façon, lorsque le séparateur de champs change, vous modifiez exactement une ligne de code, la déclaration de la constante. Quelqu'un qui regarde le changement ne verra qu'une seule ligne et comprendra immédiatement que le séparateur de champ est passé d'un point-virgule à un symbole de tuyau. Le reste du code, qui n'a pas eu besoin de changer car il utilisait une constante, reste le même, et le lecteur n'a pas à le fouiller pour voir ce qui lui a été fait d'autre.

Blrfl
la source
Je suis tout à fait d'accord. Il y a quelques décennies, j'ai travaillé sur un projet où les messages étaient envoyés en segments, chacun utilisant 8 registres généraux. Quelqu'un avait déclaré #define one 1 #define two 2 etc (ou l'équivalent dans le UK Post Office Coral, alors la langue de choix). Le mot est venu d'en haut qu'à l'avenir le champ de longueur serait le nombre d'octets, pas de segments, donc évidemment le code a été changé en #define one 8 #define two 16 etc
Mawg
3
Aussi bête que des noms tels que ou PipeSymbol semblent Semicolon, changer l' un à l'autre à l' aide d' un script sera beaucoup plus facile que de changer tous les touchés ;à |.
Brandin
Qu'en est-il du cas où un littéral String donné est utilisé plusieurs fois dans un fichier, mais n'a d'autre signification que sa valeur? Par exemple, si vous testez que vous pouvez recevoir une certaine clé dans une carte dans 20 scénarios de différence, dois-je définir une constante comme ça?: public static final String MY_KEY_NAME = "MyKeyName"
Jordan McQueen
1
@JordanMcQueen Il y a lieu de justifier l'utilisation de littéraux nus si (et seulement si) chacun est utilisé une seule fois et n'est nécessaire nulle part ailleurs. Si elle est quelque chose comme chaque scénario code qui traite un être format de fichier différent, chaque format doit définir sa propre constante (par exemple CSV_RECORD_SEPARATOR, TSV_RECORD_SEPARATORetc.).
Blrfl
8

Définir le point-virgule comme une constante est redondant, car le point - virgule est déjà constant par lui-même . Cela ne changera jamais.

Ce n'est pas comme si un jour quelqu'un annoncerait "changement de terminologie, + c'est le nouveau point-virgule maintenant", et votre collègue se précipitera avec plaisir juste pour mettre à jour la constante (ils se sont moqués de moi - regardez-les maintenant).

Il y a aussi une question de cohérence. Je garantis que sa NumberTenconstante ne sera pas utilisée par tout le monde (la plupart des codeurs ne sont pas fous), donc cela ne servira pas à quoi que ce soit qu'on attendait de toute façon. Lorsque l'apocalypse arrive et que "dix" est globalement redimensionné à 9, la mise à jour de la constante ne fera PAS l'affaire, car elle vous laissera toujours un tas de littéraux 10dans votre code, donc maintenant le système devient totalement imprévisible même dans la portée d'une hypothèse révolutionnaire que "dix" signifie "9".

Le stockage de tous les paramètres en tant que consts est quelque chose que j'aurais aussi une seconde réflexion. Il ne faut pas faire cela à la légère.

Quels exemples de ce type d'utilisation avons-nous rassemblés jusqu'à présent? Terminateur de ligne ... nombre maximal de nouvelles tentatives ... nombre maximal de roues ... sommes-nous sûrs qu'elles ne changeront jamais?

Le coût est que la modification des paramètres par défaut nécessite la recompilation d'une application, et dans certains cas, même ses dépendances (car les valeurs de const numériques peuvent être codées en dur lors de la compilation).

Il y a aussi l'aspect test et moquerie. Vous avez défini la chaîne de connexion comme une constante, mais maintenant oups, vous ne pouvez pas simuler l'accès à la base de données (établir une fausse connexion) dans votre test unitaire.

Konrad Morawski
la source
4
"Cela ne changera jamais." Je pensais cela à propos de l'apostrophe (toujours liée à la valeur ASCII 39). Certaines anciennes applications utilisées pour freiner l'apostrophe. Mais maintenant, les applications modernes traitent cette valeur ASCII comme une apostrophe droite compatible avec les anciennes applications, et à la place, les gens utilisent souvent (guillemet simple gauche, Unicode 8217 ) pour les applications compatibles avec l'affichage d'un glyphe différent pour la marque recourbée. Étant donné que l'Europe utilise des virgules comme les Américains utilisent les points comme point décimal, je me trouve un peu hésitant à déclarer "pas ... jamais".
TOOGAM
@TOOGAM bien, votre exemple justifie d'avoir une DecimalPointconstante - mais pas Commaou des Periodconstantes. C'est toute une différence: le premier dénote une fonction , un rôle ou un but de la valeur. "Point-virgule" ou "virgule" ne relèvent pas de cette catégorie.
Konrad Morawski
C'est vrai pour l'exemple du point décimal. Cependant, l'exemple d'apostrophe semble être une catégorie similaire (ou identique) comme une virgule (ou point-virgule).
TOOGAM
@KonradMorawski Point-virgule peut être utilisé à de nombreuses fins, telles que le fractionnement de la chaîne ou la fin de la ligne. C'est sa signification (et non sa valeur) qui devrait être utilisée pour nommer la constance. Considérez le changement futur, c'est-à-dire que demain nous autoriserons le traitement de 20 enregistrements, donc la constance nommée NumberTen est hors contexte, tandis que maxRecord serait toujours bien.
MaxZoom
5
private const char SemiColon = ';';
private const char Space = ' ';
private const int NumberTen = 10;

Votre collègue vise donc une entrée WTF quotidienne. Ces définitions sont stupides et redondantes. Cependant, comme cela a été souligné par d'autres, les définitions suivantes ne seraient ni idiotes ni redondantes:

private const char StatementTerminator = ';';
private const char Delimiter = ' ';
private const int  BalanceInquiryCode = 10;

Les nombres et les chaînes «magiques» sont des constantes qui ont une signification au-delà de leur valeur littérale immédiate. Si la constante 10a une signification au-delà de "dix choses" (par exemple, comme un code pour une opération spécifique ou une condition d'erreur), c'est alors qu'elle devient "magique" et doit être remplacée par une constante symbolique qui décrit cette signification abstraite.

Au-delà de la description claire de l'intention, les constantes symboliques vous épargnent également quelques maux de tête lorsque vous orthographiez mal un littéral. Une simple transposition de "CVS" à "CSV" en une seule ligne de code a traversé les tests unitaires et l'AQ et a été mise en production, où elle a provoqué l'échec d'une opération particulière. Oui, évidemment, les tests unitaires et d'AQ étaient incomplets et c'est son propre problème, mais l'utilisation d'une constante symbolique aurait évité ce peu de brûlures d'estomac.

John Bode
la source
3

Il ne devrait y avoir rien de controversé à ce sujet. Il ne s'agit pas de savoir si les nombres magiques doivent être utilisés ou non, il s'agit d'avoir un code lisible.
Considérez la différence entre: if(request.StatusCode == 1)et if(request.HasSucceeded). Dans ce cas, je dirais que ce dernier est beaucoup plus lisible, mais cela ne signifie pas que vous ne pouvez jamais avoir de code comme int MaxNumberOfWheels = 18.

PS: C'est pourquoi je déteste absolument les directives de codage. Les développeurs doivent être suffisamment matures pour être capables de porter des jugements comme celui-ci; ils ne devraient pas le laisser à un morceau de texte formé par Dieu sait qui.

Stefan Billiet
la source
13
Les conducteurs doivent être suffisamment matures pour être capables de juger de quel côté de la route ils conduisent;)
Konrad Morawski
2
Le résultat d'un appel de jugement peut varier même entre les développeurs matures, donc même les directives de codage arbitraires sont censées améliorer la lisibilité grâce à la cohérence. Cela n'est pas lié au fait que la création d'un NumberTen constant n'a aucun sens.
Mike Partridge
1
Je n'insisterais pas sur le fait qu'ils doivent être formels, tamponnés, etc., ils peuvent être informels, mais ils doivent être convenus, et cela va déjà au-delà de la simple utilisation de la maturité individuelle de jugement. Mais vous avez supprimé votre commentaire maintenant Stefan :)
Konrad Morawski
1
@StefanBilliet - pas du tout. Mon point est que la lisibilité est améliorée grâce à la cohérence. Le problème ici n'est pas la directive de codage elle-même, mais une directive poussée à l'extrême par un malentendu.
Mike Partridge
@MikePartridge J'aurais peut-être dû élaborer; les directives de codage que j'ai vues sont davantage dans la tendance d'un livre de règles générales sur la façon dont quelqu'un quelque part pensait que les logiciels devraient être écrits, plutôt que des accords comme vous et Konrad envisagez probablement :-)
Stefan Billiet