Où mettre des constantes et pourquoi?

34

Dans nos applications pour la plupart volumineuses, nous n'avons généralement que quelques emplacements pour les "constantes":

  • Une classe pour l'interface graphique et les constantes internes (titres de page d'onglet, titres de zone de groupe, facteurs de calcul, énumérations)
  • Une classe pour les tables et colonnes de base de données (cette partie est du code généré) plus des noms lisibles pour eux (attribués manuellement)
  • Une classe pour les messages d'application (journalisation, boîtes de message, etc.)

Les constantes sont généralement séparées en différentes structures dans ces classes. Dans nos applications C ++, les constantes ne sont définies que dans le fichier .h et les valeurs sont affectées dans le fichier .cpp.

L'un des avantages est que toutes les chaînes, etc. sont au même endroit et que tout le monde sait où les trouver quand quelque chose doit être changé.

C'est particulièrement quelque chose que les chefs de projet semblent aimer quand les gens vont et viennent et de cette façon, tout le monde peut changer des choses aussi triviales sans avoir à fouiller dans la structure de l'application.

En outre, vous pouvez facilement changer le titre de zones de groupe / onglets similaires, etc. à la fois. Un autre aspect est que vous pouvez simplement imprimer cette classe et la donner à un non-programmeur qui peut vérifier si les légendes sont intuitives et si les messages à l'utilisateur sont trop détaillés ou trop confus, etc.

Cependant, je vois certains inconvénients:

  • Chaque classe est étroitement couplée aux classes de constantes
  • Ajouter / supprimer / renommer / déplacer une constante nécessite une recompilation d'au moins 90% de l'application (Remarque: la modification de la valeur ne le fait pas, au moins pour C ++). Dans l'un de nos projets C ++ avec 1500 classes, cela signifie environ 7 minutes de temps de compilation (en utilisant des en-têtes précompilés; sans eux, c'est environ 50 minutes) plus environ 10 minutes de liaison avec certaines bibliothèques statiques.
  • La création d'une version à vitesse optimisée via le compilateur Visual Studio prend jusqu'à 3 heures. Je ne sais pas si l'énorme quantité de relations de classe est la source, mais cela pourrait tout aussi bien l'être.
  • Vous vous retrouvez dans des chaînes temporairement codées en dur directement dans du code parce que vous voulez tester quelque chose très rapidement et ne voulez pas attendre 15 minutes juste pour ce test (et probablement tous les suivants). Tout le monde sait ce qu'il advient des pensées «je réglerai ça plus tard».
  • La réutilisation d'une classe dans un autre projet n'est pas toujours aussi simple (principalement en raison d'autres couplages étroits, mais la gestion des constantes ne facilite pas la tâche.)

Où stockeriez-vous des constantes comme ça? Quels arguments apporteriez-vous également pour convaincre votre chef de projet qu'il existe de meilleurs concepts qui répondent également aux avantages énumérés ci-dessus?

N'hésitez pas à donner une réponse spécifique ou indépendante en C ++.

PS: Je sais que cette question est un peu subjective mais honnêtement, je ne connais pas de meilleur endroit que ce site pour ce genre de question.

Mise à jour sur ce projet

J'ai des nouvelles sur le temps de compilation:
après les messages de Caleb et de gbjbaanb, j'ai divisé mon fichier de constantes en plusieurs autres fichiers quand j'ai eu le temps. J'ai également finalement divisé mon projet en plusieurs bibliothèques, ce qui était désormais beaucoup plus facile. La compilation en mode de publication a montré que le fichier généré automatiquement qui contient les définitions de la base de données (table, noms de colonne et plus - plus de 8000 symboles) et accumule certains hachages a causé les énormes temps de compilation en mode de publication.

Désactiver l'optimiseur de MSVC pour la bibliothèque qui contient les constantes DB nous a maintenant permis de réduire le temps total de compilation de votre projet (plusieurs applications) en mode release de jusqu'à 8 heures à moins d'une heure!

Nous n'avons pas encore découvert pourquoi MSVC a tant de mal à optimiser ces fichiers, mais pour l'instant, ce changement soulage beaucoup de pression car nous n'avons plus à nous fier uniquement aux versions nocturnes.

Ce fait - et d'autres avantages, tels qu'un couplage moins serré, une meilleure réutilisation, etc. - ont également montré que passer du temps à diviser les "constantes" n'était pas une si mauvaise idée après tout ;-)

Mise à jour2

Étant donné que cette question reçoit toujours une certaine attention:
voici ce que je fais depuis quelques années:

Mettez chaque constante, variable, etc. exactement dans la portée qui lui convient: Si vous utilisez une constante uniquement dans une seule méthode, il est OK de la définir dans cette méthode. Si une seule classe est intéressée, laissez-la comme détail d'implémentation privée de cette classe. Il en va de même pour l'espace de noms, le module, le projet, la portée de l'entreprise. J'utilise également le même modèle pour les fonctions d'assistance et similaires. (Cela peut ne pas s'appliquer à 100% si vous développez un cadre public.)

Cela augmente la réutilisabilité, la testabilité et la maintenabilité à un degré où vous passez non seulement moins de temps à compiler (au moins en C ++), mais aussi moins de temps sur la correction de bogues, ce qui vous laisse plus de temps pour réellement développer de nouvelles fonctionnalités. Dans le même temps, le développement de ces fonctionnalités ira plus vite car vous pouvez réutiliser plus de code plus facilement. Cela l'emporte sur tout avantage que le fichier de constantes centrales pourrait avoir d'une ampleur.

Jetez un œil en particulier au principe de ségrégation d'interface et au principe de responsabilité unique si vous voulez en savoir plus.

Si vous êtes d'accord, votez pour Caleb, car cette mise à jour est essentiellement une interprétation plus générale de ce qu'il a dit.

Tim Meyer
la source
2
personnellement, je n'aurais pas du tout le titre de l'interface utilisateur ou les chaînes de message dans les constantes. Je les aurais dans app.config
jk.
1
J'aime un peu la façon dont vous le faites maintenant - je comprends vos inconvénients, mais nous pourrions avoir à y faire face.
bigtang
1
J'ai vu exactement le même problème dans un grand projet Java ... Une énorme interface "constantes", changez quoi que ce soit et attendez 15 minutes pour qu'eclipse se recompile. Je suis avec Caleb: regroupez les constantes où elles appartiennent naturellement, à proximité du code qui les utilise. Les garder séparés car ils sont constants est un exercice de TOC inutile.
Michael Borgwardt
Le couplage serré n'est pas un problème, car c'est ce que vous voulez réellement. Vous voulez qu'une modification dans votre fichier de constantes affecte éventuellement de nombreux fichiers source. (Bien sûr, vous avez également d'autres problèmes).
gnasher729
@ gnasher729 qui n'est vrai que si beaucoup de classes utilisent la même constante. Vous ne voudriez jamais que les classes soient étroitement couplées à des constantes avec lesquelles elles ne sont pas liées. Cela peut ne pas sembler un problème au premier abord jusqu'à ce que vous essayez de le réutiliser dans un autre projet sans le copier ou exécuter des tests isolés
Tim Meyer

Réponses:

29

Les constantes spécifiques à une classe doivent aller dans l'interface de cette classe.

Les constantes qui sont vraiment des options de configuration doivent faire partie d'une classe de configuration. Si vous fournissez des accesseurs pour les options de configuration de cette classe (et les utilisez à la place des constantes ailleurs), vous n'aurez pas à recompiler le monde entier lorsque vous modifiez quelques options.

Les constantes qui sont partagées entre les classes mais qui ne sont pas censées être configurables doivent avoir une portée raisonnable - essayez de les décomposer en fichiers qui ont des utilisations particulières afin que les classes individuelles incluent uniquement ce dont elles ont réellement besoin. Cela permettra à nouveau de réduire les temps de compilation lorsque vous modifiez certaines de ces constantes.

Caleb
la source
1
Au cas où vous seriez intéressé: j'ai mis à jour ma question avec ce que j'ai accompli, suite à votre réponse et à d'autres
Tim Meyer
6

Je dirais simplement que vous voulez diviser votre énorme classe de constantes en plusieurs fichiers plus petits, un par formulaire par exemple. Cela garantit que vous n'avez pas une telle dépendance énorme sur le fichier de constantes, donc l'ajout ou la mise à jour d'une chaîne ne nécessiterait pas alors une recompilation totale. Vous pouvez toujours stocker ces fichiers dans un emplacement central, mais (par exemple) avoir 1 fichier avec des constantes pour chaque boîte de dialogue, convenablement nommé. Ensuite, vous ne pouvez inclure ces fichiers que dans les fichiers de dialogue appropriés, ce qui réduit considérablement la recompilation.

Je vous suggère également d'utiliser quelque chose comme les utilitaires GNU GetText pour gérer les chaînes, il est conçu pour la traduction, mais fonctionne tout aussi bien pour simplement changer le texte en quelque chose d'autre. Vous pouvez les mettre dans une ressource de chaînes, mais je trouve qu'elles sont plus difficiles à travailler car elles sont saisies par ID, les utilitaires GetText sont saisis par une chaîne d'origine - rend les choses très faciles à développer.

gbjbaanb
la source
Je pourrais essayer. Comme la classe de constantes avec des titres, etc. est divisée en plusieurs structures, je pourrais peut-être faire une classe par structure comme début. Pas sûr de la chose GNU, nous ne voulons généralement pas changer nos chaînes au moment de l'exécution, juste pendant le temps de développement. Nous utilisons cependant le mécanisme de traduction de Qt, au cas où nous devions traduire dans une autre langue à l'avenir.
Tim Meyer
Dans le cas où vous êtes intéressé: j'ai mis à jour ma question avec ce que j'ai accompli, suite à votre réponse et à d'autres
Tim Meyer
2

Remarque: je ne suis pas un développeur C ++ ... mais voici ma pensée: vous devez envisager de suivre le commentaire de @ jk sur la différence entre l'utilisation des fichiers de configuration. Dans DotNet, il existe un fichier de ressources utilisé pour stocker ces informations. Dans Windows Forms, un fichier de ressources est conservé à partir de VS pour chaque formulaire.

Je ne vois pas de valeur pour une constante à placer en dehors de son champ d'utilisation, sauf s'il s'agit d'une constante globale qui doit être partagée. Comme vous l'avez mentionné, cela sera au moins difficile à maintenir pendant le développement. En outre, vous pouvez rencontrer des conflits de noms. Une autre chose est qu'il peut être difficile de savoir qui utilise une constante donnée.

Maintenant, si vous souhaitez que les non-programmeurs examinent les informations, pour l'interface graphique, vous capturez l'écran pour eux. Si vous souhaitez qu'ils examinent les entrées du tableau de données, vous pouvez exporter les données vers Excel ou quelque chose de similaire.

Si vous souhaitez toujours utiliser une approche de localisation centralisée et que vous souhaitez placer toutes vos constantes dans un seul gros fichier, chaque développeur peut utiliser un fichier partagé qui est mis à jour à la fin de chaque intervalle dans un fichier central. Les données proviendront de fichiers individuels utilisés dans le développement. Cela peut être facilement automatisé ou effectué manuellement. Cependant, comme je l'ai dit, c'est probablement un risque que vous n'avez pas à prendre.

Aucune chance
la source
2

Il n'y a pas de solution générale. Renseignez-vous sur les performances, la convivialité, la sécurité et le cycle de vie d'une constante.

Plus ils sont proches de leur portée, plus les performances sont élevées.

Plus ils sont regroupés logiquement et hors de leur portée, plus la réutilisation est élevée.

Moins les costants sont accessibles, plus la sécurité est élevée.

Plus la durée de vie d'une constante est élevée, moins elle se soucie de l'endroit où vous la mettez pour la facilité d'utilisation.

Une constante comme un numéro de version sera définie dans une sorte de manifeste. Un code d'erreur d'une fonction d'erreur sera défini à l'intérieur de la classe. Le code d'erreur est probablement quelque chose avec une durée de vie élevée (= ne change presque jamais). Le mettre dans un fichier constant ne fait que spammer le fichier avec des trucs inutiles.

Moins la constante a le caractère d'une constante mais d'une variable (comme le numéro de version), plus vous pouvez la mettre à l'extérieur. Moins la constante est variable, plus elle est constante, plus elle doit être placée à l'intérieur de sa portée. Pendant le débogage, il est judicieux de le placer à l'extérieur pour réduire le temps de compilation.

Cependant, votre problème initial est le temps de compilation. La question est donc de savoir si vous posez la bonne question. Si le temps de compilation de votre application est trop élevé, vous devriez mieux penser à un moyen de le rendre plus modulaire afin que les pièces fonctionnent indépendamment les unes des autres. Compilez-le partiellement et testez vos trucs indépendamment. Si vos tests unitaires sont correctement effectués et à fond (ce qui représente en fait beaucoup de travail), vous pouvez très facilement déplacer des éléments sans avoir à vous en soucier. Et puis la question devient totalement différente.

Christian
la source
1

Je suggérerais de mettre toutes ces constantes dans une sorte de fichier de configuration. Pour les applications Java, nous utilisons généralement des fichiers .properties, un simple texte avec chaque ligne formatée comme "(clé) = (valeur)". Exemple

MainPanel.Title = Bienvenue dans notre application
DB.table.users = TBL_USERS
logging.filename = application.log

Vous chargez ensuite ce fichier au moment de l'exécution, remplissez un cache qui vous permet de rechercher une clé et de récupérer une valeur. Lorsque vous avez besoin d'une constante, vous interrogez le cache. Vous devrez toujours avoir vos clés quelque part et le cache devra être accessible globalement, mais lorsque vous modifiez les valeurs réelles des constantes, aucune recompilation n'est nécessaire, il devrait être possible de simplement redémarrer l'application (ou si vous voulez vraiment obtenir de la fantaisie, avoir plusieurs fichiers et caches .properties et donner à l'application la possibilité de recharger un cache au moment de l'exécution).

Pour une implémentation, j'ai trouvé cette question SO: /programming/874052/properties-file-library-for-c-or-c (c'était le premier hit sur une recherche Google - je n'ai pas effectivement utilisé ce logiciel moi-même).

FrustratedWithFormsDesigner
la source
Pour les projets C ++, nous n'avons besoin de recompiler le fichier de constantes que si nous modifions une valeur. En effet, les valeurs sont affectées dans un fichier .cpp. Cependant, ajouter / supprimer / renommer / déplacer une constante nécessite toujours une reconstruction complète
Tim Meyer
@TimMeyer: Si vous divisez les constantes en plusieurs fichiers, l'ajout / la suppression d'une constante n'affecterait que les fichiers qui dépendent de ce fichier particulier, n'est-ce pas?
FrustratedWithFormsDesigner
Correct. Le problème principal est que si je suggère cela, les gens ont tendance à mentionner l'une des choses que j'ai répertoriées comme "avantages" se perdent
Tim Meyer
Cependant, je n'ai pas vraiment pensé à mettre plusieurs fichiers de constantes au même endroit
Tim Meyer
+1. @Tim Meyer: À cette fin, la vérification au moment de la compilation coûte beaucoup plus qu'elle n'économise. D'ailleurs, qu'allez-vous faire si vous avez besoin de vous internationaliser?
kevin cline