web-dev-qa-db-fra.com

Utilisation de chaînes / nombres magiques

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 sujets suivants:
StackExchange
StackOverflow
Bytes IT Community
Il existe de nombreux autres articles, et après lecture, certains modèles se dessinent.

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.

33
Daniel Gruszczyk

... 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 n'est pas de nommer un espace littéral comme Space mais son mauvais choix de nom pour ses constantes.

Supposons que le travail de votre code consiste à analyser un flux d'enregistrements contenant 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 faire?

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

Les identificateurs des constantes doivent être descriptifs de ce que fait la valeur , et non de ce que la valeur est , et c'est là que votre collègue a tourné à 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 à fouiller pour voir ce qui lui a été fait d'autre.

90
Blrfl

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 "un changement de terminologie, + est le nouveau point-virgule maintenant", et votre collègue se fera un plaisir de se précipiter 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 constante NumberTen ne sera pas utilisée par tout le monde (la plupart des codeurs ne sont pas hors de leur esprit), donc elle ne servira pas à quoi qu'elle soit de toute façon attendue. 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éral 10s dans votre code, alors maintenant le système devient totalement imprévisible même dans le cadre d'une hypothèse révolutionnaire selon laquelle "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.

8
Konrad Morawski
private const char SemiColon = ';';
private const char Space = ' ';
private const int NumberTen = 10;

Votre collègue vise donc une entrée Daily WTF. Ces définitions sont stupides et redondantes. Cependant, comme cela a été souligné par d'autres, les définitions suivantes seraient pas idiotes ou 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 10 a un sens au-delà de "dix choses" (disons comme un code pour une opération spécifique ou une condition d'erreur), c'est alors qu'il devient "magique" et devrait être remplacé par une constante symbolique qui décrit ce sens abstrait.

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" dans 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.

5
John Bode

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.

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

3
Stefan Billiet