web-dev-qa-db-fra.com

Quel est le problème avec les cordes magiques?

En tant que développeur de logiciels expérimenté, j'ai appris à éviter les chaînes magiques.

Mon problème est que cela fait si longtemps que je ne les ai pas utilisés, j'ai oublié la plupart des raisons. Par conséquent, j'ai du mal à expliquer pourquoi ils posent problème à mes collègues moins expérimentés.

Quelles sont les raisons objectives pour les éviter? Quels problèmes causent-ils?

176
Kramii
  1. Dans un langage qui compile, la valeur d'une chaîne magique est non vérifiée au moment de la compilation. Si la chaîne doit correspondre à un modèle particulier, vous devez exécuter le programme pour garantir qu'il correspond à ce modèle. Si vous avez utilisé quelque chose comme une énumération, la valeur est au moins valide au moment de la compilation, même si elle peut être incorrecte.

  2. Si une chaîne magique est écrite à plusieurs endroits vous devez tous les changer sans aucune sécurité (comme une erreur de compilation). Cela peut être contré en le déclarant seulement en un seul endroit et en réutilisant la variable, cependant.

  3. Les fautes de frappe peuvent devenir de graves bogues. Si vous avez une fonction:

    func(string foo) {
        if (foo == "bar") {
            // do something
        }
    }
    

    et quelqu'un tape accidentellement:

    func("barr");
    

    C'est pire, plus la chaîne est rare ou complexe, surtout si vous avez des programmeurs qui ne connaissent pas la langue maternelle du projet.

  4. Les cordes magiques sont rarement auto-documentées. Si vous voyez une chaîne, cela ne vous dit rien de ce que la chaîne pourrait/devrait être d'autre. Vous devrez probablement examiner l'implémentation pour vous assurer d'avoir choisi la bonne chaîne.

    Ce type d'implémentation est fuite, nécessitant une documentation externe ou un accès au code pour comprendre ce qui doit être écrit, d'autant plus qu'il doit être parfait (comme au point 3).

  5. À court de fonctions de "recherche de chaîne" dans les IDE, il existe un petit nombre d'outils qui prennent en charge le modèle.

  6. Vous pouvez utiliser par hasard la même chaîne magique à deux endroits, alors qu'en réalité ce sont des choses différentes, donc si vous avez fait un Find & Replace, et changé les deux, l'un d'eux pourrait se casser pendant que l'autre fonctionnerait.

215
Erdrik Ironrose

Le sommet de ce que les autres réponses ont saisi n'est pas que les "valeurs magiques" sont mauvaises, mais qu'elles devraient être:

  1. définis de façon reconnaissable comme constantes;
  2. définis une seule fois dans l'ensemble de leur domaine d'utilisation (si cela est possible sur le plan architectural);
  3. définis ensemble s'ils forment un ensemble de constantes qui sont en quelque sorte liées;
  4. définis à un niveau de généralité approprié dans l'application dans laquelle ils sont utilisés; et
  5. définis de manière à limiter leur utilisation dans des contextes inappropriés (par exemple, susceptibles de vérification de type).

Ce qui distingue généralement les "constantes" acceptables des "valeurs magiques" est une violation d'une ou plusieurs de ces règles.

Bien utilisées, les constantes nous permettent simplement d'exprimer certains axiomes de notre code.

Ce qui m'amène à un dernier point, à savoir un usage excessif des constantes (et donc un nombre excessif d'hypothèses ou de contraintes exprimées en termes de valeurs), même s'il satisfait par ailleurs aux critères ci-dessus (mais surtout s'il s'en écarte), peut impliquer que la solution en cours de conception n'est pas suffisamment générale ou bien structurée (et donc nous ne parlons plus vraiment des avantages et des inconvénients des constantes, mais des avantages et inconvénients d'un code bien structuré).

Les langages de haut niveau ont des constructions de modèles dans les langages de bas niveau qui devraient utiliser des constantes. Les mêmes modèles peuvent également être utilisés dans le langage de niveau supérieur, mais ne devraient pas l'être.

Mais il peut s'agir d'un jugement d'expert basé sur une impression de toutes les circonstances et de ce à quoi devrait ressembler une solution, et la manière exacte dont ce jugement sera justifié dépendra fortement du contexte. En effet, cela peut ne pas être justifiable en termes de principe général, sauf pour affirmer "Je suis assez vieux pour avoir déjà vu ce genre de travail, que je connais, fait mieux"!

EDIT: ayant accepté une modification, rejeté une autre, et ayant maintenant effectué ma propre modification, puis-je maintenant considérer que le style de formatage et de ponctuation de ma liste de règles est réglé une fois pour toutes haha!

89
Steve
  • Ils sont difficiles à suivre.
  • Tout changer peut nécessiter de modifier plusieurs fichiers dans plusieurs projets (difficile à maintenir).
  • Parfois, il est difficile de dire quel est leur objectif simplement en examinant leur valeur.
  • Pas de réutilisation.
34
jason

Exemple concret: je travaille avec un système tiers dans lequel des "entités" sont stockées avec des "champs". Fondamentalement, un système EAV . Comme il est assez facile d'ajouter un autre champ, vous pouvez y accéder en utilisant le nom du champ comme chaîne:

Field nameField = myEntity.GetField("ProductName");

(notez la chaîne magique "ProductName")

Cela peut entraîner plusieurs problèmes:

  • Je dois me référer à la documentation externe pour savoir que "ProductName" existe même et son orthographe exacte
  • De plus, je dois me référer à ce document pour voir quel est le type de données de ce champ.
  • Les fautes de frappe dans cette chaîne magique ne seront pas capturées tant que cette ligne de code n'aura pas été exécutée.
  • Lorsque quelqu'un décide de renommer ce champ sur le serveur (difficile tout en empêchant la perte de données, mais pas impossible), je ne peux pas facilement rechercher dans mon code pour voir où je dois ajuster ce nom.

Ma solution a donc été de générer des constantes pour ces noms, organisées par type d'entité. Alors maintenant, je peux utiliser:

Field nameField = myEntity.GetField(Model.Product.ProductName);

Il s'agit toujours d'une constante de chaîne et se compile exactement au même binaire, mais présente plusieurs avantages:

  • Après avoir tapé "Modèle", mon IDE montre juste les types d'entités disponibles, donc je peux sélectionner "Produit" facilement.
  • Ensuite, mon IDE fournit uniquement les noms de champs disponibles pour ce type d'entité, également sélectionnables.
  • La documentation générée automatiquement montre la signification de ce champ ainsi que le type de données utilisé pour stocker ses valeurs.
  • À partir de la constante, mon IDE peut trouver tous les endroits où cette constante exacte est utilisée (par opposition à sa valeur)
  • Les fautes de frappe seront détectées par le compilateur. Cela s'applique également lorsqu'un nouveau modèle (éventuellement après avoir renommé ou supprimé un champ) est utilisé pour régénérer les constantes.

Suivant sur ma liste: cacher ces constantes derrière les classes générées fortement typées - puis le type de données est également sécurisé.

26
Hans Kesting

Cordes magiques pas toujours mauvaises, donc cela pourrait être la raison pour laquelle vous ne pouvez pas trouver une raison générale de les éviter. (Par "chaîne magique", je suppose que vous entendez littéral chaîne dans le cadre d'une expression, et non défini comme une constante.)

Dans certains cas particuliers, les cordes magiques doivent être évitées:

  • La même chaîne apparaît plusieurs fois dans le code. Cela signifie que vous pourriez avoir une faute d'orthographe à l'un des endroits. Et ce sera une corvée des changements de chaîne. Transformez la chaîne en constante et vous éviterez ce problème.
  • La chaîne peut changer indépendamment du code où elle apparaît. Par exemple. si la chaîne est du texte affiché pour l'utilisateur final, elle changera probablement indépendamment de tout changement logique. La séparation de cette chaîne dans un module séparé (ou une configuration externe ou une base de données) facilitera la modification indépendamment
  • La signification de la chaîne n'est pas évidente dans le contexte. Dans ce cas, l'introduction d'une constante rendra le code plus facile à comprendre.

Mais dans certains cas, les "cordes magiques" vont bien. Supposons que vous ayez un analyseur simple:

switch (token.Text) {
  case "+":
    return a + b;
  case "-":
    return a - b;
  //etc.
}

Il n'y a vraiment pas de magie ici, et aucun des problèmes décrits ci-dessus ne s'applique. Il n'y aurait aucun avantage à mon humble avis à définir string Plus="+" etc. Restez simple.

11
JacquesB

Pour ajouter aux réponses existantes:

Internationalisation (i18n)

Si le texte à afficher à l'écran est codé en dur et enfoui dans des couches de fonctions, vous aurez du mal à fournir des traductions de ce texte dans d'autres langues.

Certains environnements de développement (par exemple Qt) gèrent les traductions par recherche d'une chaîne de texte de la langue de base vers la langue traduite. Les cordes magiques peuvent généralement survivre à cela - jusqu'à ce que vous décidiez d'utiliser le même texte ailleurs et d'obtenir une faute de frappe. Même dans ce cas, il est très difficile de trouver les chaînes magiques à traduire lorsque vous souhaitez ajouter la prise en charge d'une autre langue.

Certains environnements de développement (par exemple MS Visual Studio) adoptent une autre approche et nécessitent que toutes les chaînes traduites soient conservées dans une base de données de ressources et relues pour les paramètres régionaux actuels par l'ID unique de cette chaîne. Dans ce cas, votre application avec des chaînes magiques ne peut tout simplement pas être traduite dans une autre langue sans retouche majeure. Un développement efficace nécessite que toutes les chaînes de texte soient entrées dans la base de données des ressources et reçoivent un ID unique lorsque le code est écrit pour la première fois, et i18n par la suite est relativement facile. Essayer de remblayer cela après coup nécessitera généralement un effort très important (et oui, j'y suis allé!) Il est donc beaucoup mieux de bien faire les choses en premier lieu.

6
Graham

Ce n'est pas une priorité pour tout le monde, mais si jamais vous voulez pouvoir calculer les mesures de couplage/cohésion sur votre code de manière automatisée, les chaînes magiques rendent cela presque impossible. Une chaîne à un endroit fera référence à une classe, une méthode ou une fonction à un autre endroit, et il n'existe aucun moyen simple et automatique de déterminer que la chaîne est couplée à la classe/méthode/fonction simplement en analysant le code. Seul le cadre sous-jacent (angulaire, par exemple) peut déterminer qu'il existe un lien - et il ne peut le faire qu'au moment de l'exécution. Pour obtenir vous-même les informations de couplage, votre analyseur doit tout savoir sur le framework que vous utilisez, au-delà du langage de base dans lequel vous codez.

Mais encore une fois, ce n'est pas quelque chose dont beaucoup de développeurs se soucient.

3
user3511585