web-dev-qa-db-fra.com

Les constantes à un caractère sont-elles meilleures que les littéraux?

J'ai récemment rencontré une classe qui fournit à peu près tous les caractères uniques comme constante; tout de COMMA à BRACKET_OPEN. Vous vous demandez si cela était nécessaire; J'ai lu un "article" qui suggère qu'il peut être utile de tirer des littéraux à caractère unique dans des constantes. Je suis donc sceptique.

Le principal attrait de l'utilisation des constantes est qu'elles minimisent la maintenance lorsqu'un changement est nécessaire. Mais quand allons-nous commencer à utiliser un symbole différent de "," pour représenter une virgule?

La seule raison pour laquelle je vois l'utilisation de constantes au lieu de littéraux est de rendre le code plus lisible. Mais est city + CharacterClass.COMMA + state (par exemple) vraiment plus lisible que city + ',' + state?

Pour moi les inconvénients l'emportent sur les avantages, principalement que vous introduisez une autre classe et une autre importation. Et je crois en moins de code si possible. Je me demande donc quel est le consensus général ici.

128
Austin Day

Tautologie :

Il est très clair si vous lisez la toute première phrase de la question que cette question ne concerne pas les utilisations appropriées comme éliminer nombres magiques , il s'agit au mieux d'une cohérence stupide et stupide . C'est à cela que répond cette réponse

Le bon sens vous dit que const char UPPER_CASE_A = 'A'; Ou const char A = 'A' N'ajoute rien d'autre que de la maintenance et de la complexité à votre système. const char STATUS_CODE.ARRIVED = 'A' Est un cas différent.

Les constantes sont censées représenter des choses qui sont immuables au moment de l'exécution, mais peuvent devoir être modifiées à l'avenir au moment de la compilation. Quand const char A = Équivaudrait-il correctement à autre chose que A?

Si vous voyez public static final char COLON = ':' Dans Java, trouvez celui qui a écrit cela et cassez ses claviers. Si la représentation de COLON change jamais de : vous aurez un cauchemar d'entretien.

Obfuscation:

Que se passe-t-il lorsque quelqu'un le change en COLON = '-' Parce que là où il l'utilise, il faut un - À la place partout? Allez-vous écrire des tests unitaires qui disent essentiellement assertThat(':' == COLON) pour chaque référence const pour vous assurer qu'ils ne sont pas modifiés? Seulement pour que quelqu'un répare le test quand il les change?

Si quelqu'un prétend que public static final String EMPTY_STRING = ""; Est utile et bénéfique, vous venez de qualifier ses connaissances et de les ignorer en toute sécurité.

La disponibilité de chaque caractère imprimable avec une version nommée démontre simplement que celui qui l'a fait n'est pas qualifié pour écrire du code sans surveillance.

Cohésion:

Il réduit également artificiellement la cohésion, car il éloigne les choses de celles qui les utilisent et leur sont liées.

Dans la programmation informatique, la cohésion fait référence au degré auquel les éléments d'un module vont ensemble. Ainsi, la cohésion mesure la force de la relation entre les éléments de fonctionnalité au sein d'un module donné. Par exemple, dans les systèmes hautement cohésifs, la fonctionnalité est fortement liée.

Couplage:

Il associe également de nombreuses classes non liées, car elles finissent toutes par référencer des fichiers qui ne sont pas vraiment liés à ce qu'ils font.

Un couplage étroit est lorsqu'un groupe de classes dépend fortement les uns des autres. Ce scénario se produit lorsqu'une classe assume trop de responsabilités ou lorsqu'une préoccupation est répartie sur plusieurs classes plutôt que d'avoir sa propre classe.

Si vous utilisiez un meilleur nom comme DELIMITER = ',', Vous auriez toujours le même problème, car le nom est générique et sans sémantique. Réaffecter la valeur n'aide pas plus à effectuer une analyse d'impact que de rechercher et de remplacer le littéral ','. Parce que quel code l'utilise et a besoin du , Et qu'un autre code l'utilise mais a besoin de ; Maintenant? Il faut encore regarder chaque utilisation manuellement et les changer.

Dans la nature:

J'ai récemment refactorisé une application 1,000,000+ LOC Qui avait 18 ans. Il y avait des choses comme public static final COMMA = SPACE + "," + SPACE;. Ce n'est en aucun cas mieux que de simplement inclure " , " Là où cela est nécessaire.

Si vous voulez discuter de la lisibilité , vous devez vous apprendre à configurer votre IDE pour afficher whitespace des personnages où vous pouvez les voir ou quoi que ce soit, c'est juste une raison extrêmement paresseuse pour introduire l'entropie dans un système.

Il avait également défini , Plusieurs fois avec plusieurs fautes d'orthographe du mot COMMA dans plusieurs packages et classes. Avec des références à toutes les variations entremêlées dans le code. Ce n'était rien de moins qu'un cauchemar pour essayer de réparer quelque chose sans casser quelque chose de complètement indépendant.

De même pour l'alphabet, il y avait plusieurs UPPER_CASE_A, A, UPPER_A, A_UPPER Que la plupart du temps étaient égaux à A mais dans certains cas ne l'étaient pas . Pour presque tous les personnages, mais pas pour tous les personnages.

Et à partir des historiques d'édition, il ne semble pas qu'un seul d'entre eux ait été édité ou modifié au cours des 18 années, à cause de ce qui devrait maintenant être une raison évidente, c'est qu'il casserait trop de choses qui étaient introuvables, donc vous avez une nouvelle variable les noms pointant vers la même chose qui ne peuvent jamais être modifiés pour la même raison.

En aucun cas, vous ne pouvez affirmer que cette pratique ne fait rien d'autre que de commencer à l'entropie maximale.

J'ai refactorisé tout ce gâchis et j'ai souligné toutes les tautologies et les nouveaux employés du collège étaient beaucoup plus productifs car ils n'avaient pas à traquer à travers plusieurs niveaux d'indirection ce que ces références const indiquaient réellement, car elles n'étaient pas fiable dans ce qu'ils ont été nommés vs ce qu'ils contenaient.

182
user7519

Le principal attrait de l'utilisation des constantes est qu'elles minimisent la maintenance lorsqu'un changement est nécessaire.

ABSOLUMENT PAS. C'est pas du tout la raison d'utiliser des constantes car les constantes ne changent pas par définition. Si jamais une constante change alors ce n'était pas une constante, n'est-ce pas?

L'attrait de l'utilisation de constantes n'a rien à voir avec la gestion du changement et tout à voir avec rendre les programmes susceptibles d'être écrits, compris et maintenus par les gens. Si je veux savoir partout dans mon programme où un deux-points est utilisé comme séparateur d'URL, je peux le savoir très facilement si j'ai la discipline pour définir un URLSeparator constant, et je ne peux pas le savoir facilement si je dois chercher : et obtenez chaque place dans le code où : est utilisé pour indiquer une classe de base ou ?:, ou autre.

Je suis totalement en désaccord avec les autres réponses qui affirment qu'il s'agit d'une perte de temps inutile. Les constantes nommées ajoutent ce qui signifie à un programme, et cette sémantique peut être utilisée par les humains et les machines pour comprendre un programme plus profondément et le maintenir plus efficacement.

L'astuce ici n'est pas d'éviter les constantes, mais plutôt de les nommer avec leurs propriétés sémantiques plutôt que leurs propriétés syntaxiques. À quoi sert la constante? Ne l'appelez pas Comma sauf si le domaine d'activité de votre programme est la typographie, l'analyse syntaxique en anglais ou similaire. Appelez-le ListSeparator ou quelque chose de ce genre, pour clarifier la sémantique de la chose.

145
Eric Lippert

Non, c'est idiot.

Ce qui est pas nécessairement stupide, c'est de tirer des choses comme ça dans des étiquettes nommées pour des raisons de localisation. Par exemple, le délimiteur des milliers est une virgule en Amérique (1 000 000), mais pas une virgule dans d'autres paramètres régionaux. Tirer cela dans une étiquette nommée (avec un nom non virgule approprié) permet au programmeur d'ignorer/d'abstraire ces détails.

Mais faire une constante parce que "les cordes magiques sont mauvaises" n'est que du fret.

61
Telastyn

Il y a quelques caractères qui peuvent être ambigus ou utilisés à plusieurs fins différentes. Par exemple, nous utilisons '-' comme trait d'union, signe moins ou même tiret. Vous pouvez faire des noms séparés comme:

static const wchar_t HYPHEN = '-';
static const wchar_t MINUS = '-';
static const wchar_t EM_DASH = '-';

Plus tard, vous pouvez choisir de modifier votre code pour lever l'ambiguïté en les redéfinissant comme:

static const wchar_t HYPHEN = '-';
static const wchar_t MINUS = '\u2122';
static const wchar_t EM_DASH = '\u2014';

Cela pourrait être une raison pour laquelle vous devriez envisager définir des constantes pour certains caractères uniques. Cependant, le nombre de caractères ambigus de cette manière est petit. Tout au plus, il semble que vous ne le feriez que pour ceux-là. Je dirais également que vous pouvez attendre d'avoir réellement besoin de distinguer les caractères ambigus avant de factoriser le code de cette manière.

Comme les conventions typographiques peuvent varier selon la langue et la région, il vaut probablement mieux charger une telle ponctuation ambiguë à partir d'une table de traduction.

29
Adrian McCarthy

ne constante doit ajouter du sens.

Définir COMMA comme une virgule n'ajoute pas de sens, car nous savons qu'une virgule est une virgule. Au lieu de cela, nous détruisons le sens, car maintenant COMMA pourrait ne plus être une virgule.

Si vous utilisez une virgule dans un but et que vous souhaitez utiliser une constante nommée, nommez-la d'après son objectif. Exemple:

  • city + CharacterClass.COMMA + state = Mauvais
  • city + CITY_STATE_DELIMITER + state = Bon

tiliser les fonctions de formatage

Personnellement, je préfère FormatCityState(city, state) et je ne me soucie pas de l'apparence du corps de cette fonction tant qu'elle est courte et passe les cas de test.

22
Peter

L'idée qu'un COMMA constant vaut mieux que ',' ou "," est assez facile à démystifier. Bien sûr, il y a des cas où cela a du sens, par exemple en faisant final String QUOTE = "\""; économise beaucoup sur la lisibilité sans toutes les barres obliques, mais à l'exception des caractères de contrôle de langue comme \' et " Je ne les ai pas trouvés très utiles.

En utilisant final String COMMA = "," n'est pas seulement une mauvaise forme, c'est dangereux! Quand quelqu'un veut changer le séparateur de "," à ";" ils pourraient changer le fichier de constantes en COMMA = ";" parce que c'est plus rapide pour eux de le faire et ça fonctionne. Sauf, vous savez, toutes les autres choses qui utilisaient maintenant COMMA sont également des points-virgules, y compris les choses envoyées aux consommateurs externes. Il passe donc tous vos tests (car tout le code de marshalling et unmarshalling utilisait également COMMA) mais les tests externes échoueront.

Ce qui est utile, c'est de leur donner des noms utiles. Et oui, parfois plusieurs constantes auront le même contenu mais des noms différents. Par exemple final String LIST_SEPARATOR = ",".

Votre question est donc "les constantes à un seul caractère sont-elles meilleures que les littéraux" et la réponse est sans équivoque non, elles ne le sont pas. Mais encore mieux que les deux est un nom de variable à portée étroite qui dit explicitement quel est son but. Bien sûr, vous dépenserez quelques octets supplémentaires sur ces références supplémentaires (en supposant qu'elles ne sont pas compilées sur vous, ce qu'elles feront probablement), mais dans la maintenance à long terme, où se trouve la majeure partie du coût d'une application, elles valent le temps de faire.

17
corsiKa

J'ai fait du travail d'écriture de lexers et d'analyseurs et j'ai utilisé des constantes entières pour représenter les terminaux. Les terminaux à un seul caractère se sont avérés avoir le code ASCII comme valeur numérique pour des raisons de simplicité, mais le code aurait pu être autre chose entièrement. Donc, j'aurais un T_COMMA qui aurait reçu l'ASCII -code pour ',' comme valeur constante. Cependant, il y avait aussi des constantes pour les non terminaux auxquels des nombres entiers étaient assignés au-dessus de l'ensemble ASCII. En regardant les générateurs d'analyseurs tels que yacc ou bison, ou analyseurs écrit en utilisant ces outils, j'ai eu l'impression que c'est essentiellement ainsi que tout le monde l'a fait.

Donc, alors que, comme tout le monde, je pense qu'il est inutile de définir des constantes dans le but exprès d'utiliser les constantes au lieu des littéraux dans tout votre code, je pense qu'il y a des cas Edge (analyseurs, par exemple) où vous pourriez rencontrer du code criblé de constantes telles que vous décrivez. Notez que dans le cas de l'analyseur, les constantes ne sont pas seulement là pour représenter des littéraux de caractères; ils représentent des entités qui pourraient simplement se produire être des littéraux de caractères.

Je peux penser à quelques cas plus isolés où il pourrait être judicieux d'utiliser des constantes au lieu des littéraux correspondants. Par exemple, vous pouvez définir NEWLINE comme le littéral '\ n' sur une boîte Unix, mais '\ r\n' ou '\ n\r' si vous êtes sur Windows ou Mac Box. Il en va de même pour l'analyse des fichiers qui représentent des données tabulaires; vous pouvez définir des constantes FIELDSEPARATOR et RECORDSEPARATOR. Dans ces cas, vous définissez en fait une constante pour représenter un personnage qui remplit une certaine fonction. Pourtant, si vous étiez un programmeur débutant, vous nommeriez peut-être votre constante de séparateur de champ COMMA, ne réalisant pas que vous auriez dû l'appeler FIELDSEPARATOR, et au moment où vous vous en êtes rendu compte, le code serait en production et vous seriez sur le prochain afin que la constante mal nommée reste dans le code pour que quelqu'un puisse plus tard trouver et secouer la tête.

Enfin, la pratique que vous décrivez pourrait est logique dans certains cas où vous écrivez du code pour gérer les données codées dans un codage de caractères spécifique, par exemple iso-8859-1, mais attendez-vous à ce que le codage change plus tard. Bien sûr, dans un tel cas, il serait beaucoup plus logique d'utiliser des bibliothèques de localisation ou d'encodage et de décodage pour le gérer, mais si pour une raison quelconque vous ne pouviez pas utiliser une telle bibliothèque pour gérer les problèmes d'encodage pour vous, en utilisant des constantes vous ne feriez que devoir redéfinir dans un seul fichier au lieu de littéraux codés en dur jonché partout dans votre code source pourrait être un chemin à parcourir.

En ce qui concerne l'article auquel vous avez lié: je ne pense pas qu'il essaie de justifier le remplacement des littéraux de caractères par des constantes. Je pense qu'il essaie d'illustrer une méthode pour utiliser des interfaces pour extraire des constantes dans d'autres parties de votre base de code. Les constantes d'exemple utilisées pour illustrer cela sont très mal choisies, mais je ne pense pas qu'elles importent en aucune façon.

3
Pascal

En plus de toutes les bonnes réponses ici, je voudrais ajouter comme matière à réflexion, qu'une bonne programmation consiste à fournir des abstractions appropriées qui peut être construit par vous-même et peut-être par d'autres, sans avoir à répéter le même code encore et encore.

De bonnes abstractions rendent le code facile à utiliser d'une part et facile à maintenir d'autre part.

Je suis totalement d'accord avec le DELIMITER=':' en soi est une mauvaise abstraction, et seulement mieux que COLON=':' (puisque ce dernier est totalement appauvri).

Une bonne abstraction impliquant des chaînes et des séparateurs comprendrait un moyen de compresser un ou plusieurs éléments de contenu individuels dans la chaîne et de les décompresser également de la chaîne compressée, avant tout, avant de vous dire ce qu'est le délimiteur. Une telle abstraction serait regroupée en tant que concept, dans la plupart des langues en tant que classe; par exemple, afin que son utilisation soit pratiquement auto-documentée, en ce sens que vous pouvez rechercher tous les endroits où cette classe est utilisée et être sûr de l'intention du programmeur concernant le format des chaînes compressées dans chaque cas où une abstraction est utilisée.

Une fois qu'une telle abstraction est fournie, elle serait facile à utiliser sans jamais avoir à consulter la valeur de DELIMITER ou COLON, et la modification des détails d'implémentation serait généralement limitée à la la mise en oeuvre. Donc, en bref, ces constantes devraient vraiment être des détails d'implémentation cachés dans une abstraction appropriée.

Le principal attrait de l'utilisation des constantes est qu'elles minimisent la maintenance lorsqu'un changement est nécessaire.

Les bonnes abstractions, qui sont généralement des compositions de plusieurs capacités associées, permettent de minimiser la maintenance. Premièrement, ils séparent clairement le fournisseur des consommateurs. Deuxièmement, ils masquent les détails de l'implémentation et fournissent plutôt des fonctionnalités directement utiles. Troisièmement, ils documentent à un niveau élevé quand et où ils sont utilisés.

3
Erik Eidt

La seule fois où j'ai vu de telles constantes utilisées efficacement est de faire correspondre une API ou un document existant. J'ai vu des symboles tels que COMMA utilisés parce qu'un logiciel particulier était directement connecté à un analyseur qui utilisait COMMA comme balise dans une arborescence de syntaxe abstraite. Je l'ai également vu utilisé pour correspondre à une spécification formelle. dans les spécifications formelles, vous verrez parfois des symboles comme COMMA plutôt que ',' parce qu'ils veulent être aussi clairs que possible.

Dans les deux cas, l'utilisation d'un symbole nommé comme COMMA aide à assurer la cohésion d'un produit autrement disjoint. Cette valeur peut souvent l'emporter sur le coût des notations trop verbeuses.

2
Cort Ammon

Observez que vous essayez de faire une liste.

Donc, refactorisez-le comme suit: String makeList(String[] items)

En d'autres termes, factorisez la logique au lieu des données .
Les langues peuvent être différentes dans la façon dont elles représentent les listes, mais les virgules sont toujours des virgules (c'est une tautologie). Donc, si la langue change, changer le caractère virgule ne vous aidera pas - mais cela le sera.

2
user541686

S'il s'agit d'une classe écrite dans le cadre d'une application par votre collègue développeur, c'est presque certainement une mauvaise idée. Comme d'autres l'ont déjà souligné, il est logique de définir des constantes telles que SEPARATOR = ',' où vous pouvez changer la valeur et la constante est toujours logique, mais encore moins les constantes dont le nom ne décrit que leur valeur.

Cependant, il existe au moins deux cas où il est logique de déclarer des constantes dont le nom décrit exactement leur contenu et où vous ne pouvez pas modifier la valeur sans changer de manière appropriée le nom de la constante:

  • Constantes mathématiques ou physiques, par ex. PI = 3.14159. Ici, le rôle de la constante est d'agir comme un mnémonique puisque le nom symbolique PI est beaucoup plus court et plus lisible que la valeur qu'il représente.
  • Listes exhaustives de symboles dans un analyseur ou de touches sur un clavier. Il pourrait même être judicieux d'avoir une liste de constantes avec la plupart ou tous les caractères Unicode et c'est là que votre cas peut tomber. Certains caractères tels que A sont évidents et clairement reconnaissables. Mais pouvez-vous facilement dire А et A à part? Le premier est Lettre cyrillique А tandis que le dernier est Lettre latine A. Ce sont des lettres différentes, représentées par différents points de code Unicode, même si graphiquement elles sont presque identiques. Je préfère avoir des constantes CYRILLIC_CAPITAL_A et LATIN_CAPITAL_A dans mon code que deux caractères d'aspect presque identique. Bien sûr, cela est inutile si vous savez que vous ne travaillerez qu'avec des caractères ASCII qui ne contiennent pas de cyrillique. De même: j'utilise l'alphabet latin au jour le jour, donc si j'écrivais un programme qui avait besoin d'un caractère chinois, je préférerais probablement utiliser une constante plutôt que d'insérer un caractère que je ne comprends pas. Pour quelqu'un qui utilise quotidiennement des caractères chinois, un caractère chinois peut être évident mais un latin peut être plus facile pour représenter comme une constante nommée. Donc, comme vous le voyez, cela dépend du contexte. Pourtant, une bibliothèque peut contenir des constantes symboliques pour tous les caractères car les auteurs ne peuvent pas savoir à l'avance comment la bibliothèque sera utilisée et quels caractères peut avoir besoin de constantes pour améliorer la lisibilité dans une application spécifique.

Cependant, de tels cas sont généralement traités par des classes système ou des bibliothèques spécialisées et leur occurrence dans le code écrit par les développeurs d'applications devrait être très rare, sauf si vous travaillez sur un projet très spécial.

0
Michał Kosmulski