La valeur d'une constante peut-elle être modifiée au fil du temps?

28

Pendant la phase de développement, certaines variables doivent être corrigées dans le même cycle, mais peuvent devoir être modifiées au fil du temps. Par exemple, booleanpour signaler le mode de débogage, nous faisons donc des choses dans le programme que nous ne ferions pas normalement.

Est-ce un mauvais style de contenir ces valeurs dans une constante, c'est- final static int CONSTANT = 0à- dire en Java? Je sais qu'une constante reste la même pendant l'exécution, mais est-elle également censée être la même pendant tout le développement, sauf pour les changements imprévus, bien sûr?

J'ai cherché des questions similaires, mais je n'ai rien trouvé qui correspondait exactement à la mienne.

GregT
la source
11
Je suis curieux, pourquoi croiriez-vous que ce soit un mauvais style de changer cela?
Vincent Savard
36
Sauf si vous modélisez des propriétés physiques avec des constantes qui ont des valeurs mathématiques connues, tout peut changer à un moment donné.
Berin Loritsch
19
le logiciel est doux .
Erik Eidt
10
@GregT Je ne serais pas d'accord. finalvous donne une garantie imposée par le compilateur que le programme ne modifiera pas la valeur. Je ne m'en passerais pas juste parce que le programmeur pourrait vouloir modifier la valeur attribuée dans le code source.
Alexander - Rétablir Monica le
10
Pas beaucoup de temps pour formuler une réponse complète, mais je soupçonne que vos collègues ne se préoccupent pas tant des constantes, mais de l'insertion de code dans les valeurs de configuration, qui pourraient avoir tendance à se manifester sous forme de constantes. ... Les constantes vous protègent contre les erreurs stupides, comme l'assignation accidentelle en cours gravityde partie / en cours de partie. Ils ne signifient pas nécessairement, gravityc'est la même chose sur toutes les planètes ... Cela dit, la solution saine est de faire gravityune constante, mais de la retirer d'un planetfichier ou d'une base de données au début du périmètre concerné.
svidgen

Réponses:

6

En Java, les constantes finales statiques peuvent être copiées, par le compilateur, comme leurs valeurs, dans le code qui les utilise . Par conséquent, si vous publiez une nouvelle version de votre code et qu'il existe une dépendance en aval qui a utilisé la constante, la constante de ce code ne sera pas mise à jour à moins que le code en aval ne soit recompilé. Cela peut être un problème s'ils utilisent ensuite cette constante avec du code qui attend la nouvelle valeur, car même si le code source est correct, le code binaire ne l'est pas.

C'est une verrue dans la conception de Java, car c'est l'un des très rares cas (peut-être le seul cas) où la compatibilité source et la compatibilité binaire ne sont pas les mêmes. À l'exception de ce cas, vous pouvez échanger une dépendance avec une nouvelle version compatible API sans que les utilisateurs de la dépendance n'aient à recompiler. De toute évidence, cela est extrêmement important compte tenu de la manière dont les dépendances Java sont généralement gérées.

Pour aggraver les choses, le code ne fera que silencieusement la mauvaise chose plutôt que de produire des erreurs utiles. Si vous deviez remplacer une dépendance par une version avec des définitions de classe ou de méthode incompatibles, vous obtiendriez des erreurs de chargeur de classe ou d'invocation, qui fourniraient au moins de bons indices sur le problème. À moins que vous n'ayez modifié le type de la valeur, ce problème apparaîtra simplement comme une inconduite d'exécution mystérieuse.

Plus ennuyeux est que les JVM d'aujourd'hui pourraient facilement aligner toutes les constantes au moment de l'exécution sans pénalité de performance (autre que la nécessité de charger la classe définissant la constante, qui est probablement chargée de toute façon), malheureusement la sémantique du langage date des jours précédant les JIT . Et ils ne peuvent pas changer la langue car le code compilé avec les compilateurs précédents ne sera pas correct. La compatibilité avec les bogues recommence.

À cause de tout cela, certaines personnes conseillent de ne jamais changer du tout une valeur finale statique. Pour les bibliothèques qui pourraient être largement distribuées et mises à jour de manière inconnue à des moments inconnus, c'est une bonne pratique.

Dans votre propre code, en particulier au sommet de la hiérarchie des dépendances, vous vous en sortirez probablement. Mais dans ces cas, déterminez si vous avez vraiment besoin que la constante soit publique (ou protégée). Si la constante ne concerne que la visibilité du package, il est raisonnable, selon vos circonstances et les normes de code, que le package entier soit toujours recompilé en même temps, et le problème disparaît ensuite. Si la constante est privée, vous n'avez aucun problème et pouvez la changer quand vous le souhaitez.

peluches
la source
85

Tout élément de votre code source, y compris constles constantes globales déclarées, peut être sujet à changement avec une nouvelle version de votre logiciel.

Les mots clés const(ou finalen Java) sont là pour signaler au compilateur que cette variable ne changera pas pendant l'exécution de cette instance du programme . Rien de plus. Si vous voulez envoyer des messages au responsable suivant, utilisez un commentaire dans la source, c'est pour ça qu'ils sont là.

// DO NOT CHANGE without consulting with the legal department!
// Get written consent form from them before release!
public const int LegalLimitInSeconds = ...

Est une meilleure façon de communiquer avec votre futur.

nvoigt
la source
11
J'ai vraiment aimé ce commentaire pour le futur moi.
GregT
4
TaxRatel'être publicme rend nerveux. Je voudrais savoir avec certitude que seul le service commercial est impacté par ce changement et pas aussi nos fournisseurs qui nous facturent une taxe. Qui sait ce qui s'est passé dans la base de code depuis que ce commentaire a été écrit.
candied_orange
3
@IllusiveBrian ne critiquait pas l'utilisation des constantes. Avertissait de ne pas faire confiance à un commentaire pour être à jour. Assurez-vous toujours de la façon dont quelque chose est utilisé avant de le changer.
candied_orange
8
C'est un bon conseil pour Java . Cela peut être différent dans d'autres langues. Par exemple, en raison de la valeur de façon const sont liés au site d'appel en C #, les public constchamps doivent uniquement être utilisés pour des choses qui ne changent jamais de jamais, comme Math.pi. Si vous créez une bibliothèque, les choses qui pourraient changer pendant le développement ou avec une nouvelle version devraient l'être public static readonly, afin de ne pas causer de problèmes avec les utilisateurs de votre bibliothèque.
GrandOpener
6
Vous devriez choisir un autre exemple ... les valeurs monétaires ne doivent jamais être en virgule flottante!
corsiKa
13

Nous devons distinguer deux aspects des constantes:

  • les noms de valeurs connues au moment du développement, que nous introduisons pour une meilleure maintenabilité, et
  • valeurs disponibles pour le compilateur.

Et puis il y a un troisième type connexe: les variables dont la valeur ne change pas, c'est-à-dire les noms d'une valeur. La différence entre une de ces variables immuables et une constante est lorsque la valeur est déterminée / affectée / initialisée: une variable est initialisée au moment de l'exécution, mais la valeur d'une constante est connue pendant le développement. Cette distinction est un peu confuse car une valeur peut être connue pendant le développement mais n'est en fait créée que lors de l'initialisation.

Mais si la valeur d'une constante est connue au moment de la compilation, le compilateur peut effectuer des calculs avec cette valeur. Par exemple, le langage Java a le concept d' expressions constantes . Une expression constante est toute expression qui se compose uniquement de littéraux de primitives ou de chaînes, d'opérations sur des expressions constantes (telles que la conversion, l'addition, la concaténation de chaînes) et de variables constantes. [ JLS §15.28 ] Une variable constante est une finalvariable qui est initialisée avec une expression constante. [JLS §4.12.4] Donc pour Java, c'est une constante de temps de compilation:

public static final int X = 7;

Cela devient intéressant lorsqu'une variable constante est utilisée dans plusieurs unités de compilation, puis la déclaration est modifiée. Considérer:

  • A.java:

    public class A { public static final int X = 7; }
  • B.java:

    public class B { public static final int Y = A.X + 2; }

Maintenant, lorsque nous compilons ces fichiers, le B.classbytecode déclarera un champ Y = 9car il B.Ys'agit d'une variable constante.

Mais lorsque nous changeons la A.Xvariable en une valeur différente (disons X = 0) et recompilons uniquement le A.javafichier, nous nous référons B.Ytoujours à l'ancienne valeur. Cet état A.X = 0, B.Y = 9est incompatible avec les déclarations du code source. Bon débogage!

Cela ne signifie pas que les constantes ne doivent jamais être modifiées. Les constantes sont définitivement meilleures que les nombres magiques qui apparaissent sans explication dans le code source. Cependant, la valeur des constantes publiques fait partie de votre API publique . Cela n'est pas spécifique à Java, mais se produit également en C ++ et dans d'autres langages qui comportent des unités de compilation distinctes. Si vous modifiez ces valeurs, vous devrez recompiler tout le code dépendant, c'est-à-dire effectuer une compilation propre.

Selon la nature des constantes, elles peuvent avoir conduit à des hypothèses incorrectes de la part des développeurs. Si ces valeurs sont modifiées, elles peuvent déclencher un bogue. Par exemple, un ensemble de constantes peut être choisi de manière à former certaines configurations binaires, par exemple public static final int R = 4, W = 2, X = 1. Si ceux-ci sont modifiés pour former une structure différente comme R = 0, W = 1, X = 2alors le code existant tel que boolean canRead = perms & Rdevient incorrect. Et pensez au plaisir qui allait en résulter Integer.MAX_VALUE! Il n'y a pas de solution ici, il est juste important de se rappeler que la valeur de certaines constantes est vraiment importante et ne peut pas être modifiée simplement.

Mais pour la majorité des constantes, les modifier va bien se passer tant que les restrictions ci-dessus sont prises en compte. Une constante est sûre de changer lorsque la signification, et non la valeur spécifique, est importante. C'est par exemple le cas pour des paramètres tels que BORDER_WIDTH = 2ou TIMEOUT = 60; // secondsou des modèles tels que API_ENDPOINT = "https://api.example.com/v2/"- bien que certains ou tous devraient être spécifiés dans les fichiers de configuration plutôt que dans le code.

amon
la source
5
J'aime cette analyse. Je le lis comme: Vous êtes libre de changer une constante tant que vous comprenez comment elle est utilisée.
candied_orange
+1 C # "souffre" également du même problème avec les constantes publiques.
Reginald Blue
6

Une constante n'est garantie que pour la durée de vie de l'application . Tant que cela est vrai, il n'y a aucune raison de ne pas profiter de la fonction de langue. Vous avez juste besoin de savoir quelles sont les conséquences de l'utilisation d'une constante par rapport aux indicateurs du compilateur dans le même but:

  • Les constantes occupent l'espace d'application
  • Les drapeaux du compilateur ne
  • Le code désactivé par les constantes peut être mis à jour et modifié avec des outils de refactorisation modernes
  • Le code désactivé par les drapeaux du compilateur ne peut pas

Après avoir maintenu une application qui activerait le dessin de boîtes englobantes pour les formes afin que nous puissions déboguer la façon dont elles ont été dessinées, nous avons rencontré un problème. Après la refactorisation, tout le code qui a été désactivé par les drapeaux du compilateur ne se compilait pas. Après cela, nous avons intentionnellement changé nos indicateurs de compilateur en constantes d'application.

Je dis cela pour démontrer qu'il y a des compromis. Le poids de quelques booléens n'allait pas faire manquer de mémoire à l'application ni prendre trop de place. Cela pourrait ne pas être vrai si votre constante est vraiment un grand objet qui a essentiellement une poignée pour tout dans votre code. S'il ne prend pas soin de supprimer toutes les références qu'il détient à un objet après qu'il ne soit plus nécessaire, votre objet peut être la source d'une fuite de mémoire.

Vous devez évaluer le cas d'utilisation et pourquoi vous souhaitez modifier les constantes.

Je ne suis pas fan des simples déclarations générales, mais en général, votre collègue senior a raison. Si quelque chose est appelé à changer souvent, il peut être nécessaire que ce soit un élément configurable. Par exemple, vous pourriez probablement convaincre votre collègue pour une constante IsInDebugMode = truelorsque vous souhaitez protéger du code contre la rupture. Cependant, certaines choses peuvent devoir changer plus souvent que vous ne publiez une application. Si tel est le cas, vous avez besoin d'un moyen de modifier cette valeur au moment approprié. Vous pouvez prendre l'exemple d'un TaxRate = .065. Cela peut être vrai au moment où vous compilez votre code, mais en raison de nouvelles lois, il peut changer avant de publier la prochaine version de votre application. C'est quelque chose qui doit être mis à jour à partir d'un mécanisme de stockage (comme un fichier ou une base de données)

Berin Loritsch
la source
Qu'entendez-vous par «drapeaux du compilateur»? Peut-être le préprocesseur C et les fonctionnalités du compilateur similaires qui prennent en charge les macros / définit et #ifdefs? Comme ceux-ci sont basés sur la substitution textuelle du code source, ils ne font pas partie de la sémantique du langage de programmation. Notez que Java n'a pas de préprocesseur.
amon
@amon, Java ne l'est peut-être pas, mais plusieurs langues le font. Je veux dire des #ifdefdrapeaux. Bien qu'ils ne fassent pas partie de la sémantique de C, ils font partie de C #. J'écrivais pour le contexte plus large de l'agnosticisme linguistique.
Berin Loritsch
Je pense que l'argument du "gaspillage de mémoire" est sans objet. La doublure et le tremblement d'arbre sont une étape à peu près universelle dans tout optimiseur en mode de libération.
Alexander - Rétablir Monica le
@Alexander, je suis d'accord. C'est quelque chose dont il faut être conscient.
Berin Loritsch
1
«Les constantes prennent de l'espace d'application» - à moins que vous ne développiez une application intégrée pour un microcontrôleur avec seulement un kilo-octet ou deux de mémoire, vous ne devriez même pas penser à de telles choses.
vsz
2

Le const, #defineou finalest un indice du compilateur (notez que le #definen'est pas réellement un indice, c'est une macro et nettement plus puissant). Il indique que la valeur ne changera pas au cours de l'exécution d'un programme et diverses optimisations peuvent être effectuées.

Cependant, en tant qu'indicateur de compilation, le compilateur fait des choses qui ne sont pas toujours attendues par le programmeur. En particulier, javac insérera un static final int FOO = 42;afin que, partout où il FOOest utilisé, le code d'octets compilé réel soit lu 42.

Ce n'est pas une grande surprise jusqu'à ce que quelqu'un modifie la valeur sans recompiler l'autre unité de compilation (fichier .java) - et le 42reste dans le code d'octet (voir est-il possible de désactiver l'inclusion de javac dans les variables finales statiques? ).

Faire quelque chose static finalsignifie que c'est cela et que ce sera toujours plus et le changer est vraiment une grosse affaire - surtout si c'est autre chose private.

Les constantes pour des choses comme ce final static int ZERO = 0n'est pas un problème. final static double TAX_RATE = 0.55(en plus d'être de l'argent et du double, c'est mauvais et devrait utiliser BigDecimal, mais ce n'est pas une primitive et donc pas en ligne) est un problème et doit être examiné avec grand soin pour savoir où il est utilisé.

user292808
la source
pour les petites valeurs de ZERO.
3
is a problem and should be examined with great care for where it is used.Pourquoi est-ce un problème?
Alexander - Rétablir Monica le
1

Comme son nom l'indique, les constantes ne doivent pas changer pendant l'exécution et à mon avis, les constantes sont définies pour ne pas changer à long terme (vous pouvez consulter cette question SO pour plus d'informations.

En ce qui concerne le besoin d'indicateurs (par exemple pour le mode de développement), vous devez plutôt utiliser un fichier de configuration ou un paramètre de démarrage (de nombreux IDE prennent en charge la configuration du paramètre de démarrage sur une base par projet; reportez-vous à la documentation appropriée) pour activer ce mode - De cette façon, vous gardez la flexibilité d'utiliser un tel mode et vous ne pouvez pas oublier de le changer chaque fois que le code devient productif.

DMuenstermann
la source
0

Pouvoir être modifié entre les exécutions est l'un des points les plus importants pour définir une constante dans votre code source!

La constante vous donne un emplacement bien défini et documenté (dans un sens) pour changer la valeur chaque fois que vous en avez besoin pendant la durée de vie de votre code source. C'est aussi une promesse que changer la constante à cet endroit changera en fait toutes les occurrences de ce qu'elle représente.

À titre d'exemple défavorable: il ne serait pas logique d'avoir une constante TRUEqui s'évalue truedans une langue qui a réellement le truemot - clé. Vous ne diriez jamais, jamais, pas même une seule fois, TRUE=falsesauf comme une plaisanterie cruelle.

Bien sûr, il existe d'autres utilisations des constantes, par exemple raccourcir le code ( CO_NAME = 'My Great World Unique ACME Company'), éviter la duplication ( PI=3.141), définir des conventions ( TRUE=1) ou autre chose, mais avoir une position définie pour modifier la constante est certainement l'une des plus importantes.

AnoE
la source