Что не так с магическими струнами?

Как опытный разработчик программного обеспечения, я научился избегать магических строк.

Моя проблема в том, что я так давно использую их, я забыл большинство причин. В результате у меня возникли проблемы с объяснением, почему они являются проблемой для моих менее опытных коллег.

Какие объективные причины для их устранения? Какие проблемы они вызывают?

152 голоса | спросил Kramii 5 FebruaryEurope/MoscowbMon, 05 Feb 2018 12:29:00 +0300000000pmMon, 05 Feb 2018 12:29:00 +030018 2018, 12:29:00

7 ответов


202
  1. В языке, который компилирует, значение волшебной строки не проверяется во время компиляции . Если строка должна соответствовать определенному шаблону, вы должны запустить программу, чтобы гарантировать ее соответствие этому шаблону. Если вы использовали что-то вроде перечисления, значение, по крайней мере, является допустимым во время компиляции, даже если это может быть неправильное значение.

  2. Если волшебная строка записывается в нескольких местах , вы должны изменить все из них без какой-либо безопасности (например, ошибки времени компиляции). Это может быть противопоставлено только объявлением его в одном месте и повторным использованием переменной.

  3. Опечатки могут стать серьезными ошибками. Если у вас есть функция:

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

    и кто-то случайно набирает:

    func("barr");
    

    Это хуже, чем реже или сложнее строка, особенно если у вас есть программисты, незнакомые с родным языком проекта.

  4. Магические строки редко самодокументируются. Если вы видите одну строку, это ничего не говорит о том, что еще может /должно быть в строке. Вам, вероятно, придется изучить реализацию, чтобы убедиться, что вы выбрали правильную строку.

    Такая реализация - leaky , требующая либо внешней документации, либо доступа к коду, чтобы понять, что должно быть написано, тем более, что она должна быть идеальной (как в пункте 3).

  5. За исключением функций «найти строку» в IDE, существует небольшое количество инструментов, поддерживающих шаблон.

ответил Erdrik Ironrose 5 FebruaryEurope/MoscowbMon, 05 Feb 2018 13:10:33 +0300000000pmMon, 05 Feb 2018 13:10:33 +030018 2018, 13:10:33
87

На вершине того, что понимали другие ответы, заключается не в том, что «магические ценности» плохие, а в том, что они должны быть:

  1. определяется как константы;
  2. определяется только один раз во всей своей области использования (если это возможно в архитектуре);
  3. определены вместе, если они образуют набор констант, которые так или иначе связаны;
  4. , определенные на соответствующем уровне общности в приложении, в котором они используются; и
  5. , определяемые таким образом, чтобы ограничить их использование в ненадлежащих контекстах (например, поддаются проверке типов).

То, что обычно отличает приемлемые «константы» от «магических значений», является некоторым нарушением одного или нескольких из этих правил.

Хорошо, константы просто позволяют нам выразить некоторые аксиомы нашего кода.

Что доводит меня до последнего момента, что чрезмерное использование констант (и, следовательно, чрезмерное количество допущений или ограничений, выраженных в терминах значений), даже если оно в противном случае соответствует вышеприведенным критериям (но особенно если оно отклоняется от их), может означать, что разрабатываемое решение не является достаточно общим или хорошо структурированным (и поэтому мы больше не говорим о плюсах и минусах констант, а о плюсах и минусах хорошо структурированного кода).

Языки высокого уровня имеют конструкции для шаблонов на языках более низкого уровня, которые должны были бы использовать константы. Те же шаблоны также могут использоваться на языке более высокого уровня, но не должны быть.

Но это может быть экспертное суждение, основанное на впечатлении от всех обстоятельств и на то, как должно выглядеть решение, и точно, как это решение будет оправдано, будет в значительной степени зависеть от контекста. Действительно, это не может быть оправдано с точки зрения какого-либо общего принципа, кроме как утверждать: «Я достаточно взрослый, чтобы уже видеть такую ​​работу, с которой я знаком, сделан лучше»!

РЕДАКТИРОВАТЬ: , приняв одно редактирование, отклонил другое, и теперь выполнил мое собственное редактирование, могу ли я теперь рассмотреть стиль форматирования и пунктуации моего списка правил, который будет разрешен раз и навсегда, хаха

ответил Steve 5 FebruaryEurope/MoscowbMon, 05 Feb 2018 16:40:13 +0300000000pmMon, 05 Feb 2018 16:40:13 +030018 2018, 16:40:13
33
  • Их трудно отслеживать.
  • Для изменения всех может потребоваться изменение нескольких файлов в возможно нескольких проектах (трудно поддерживать).
  • Иногда трудно сказать, какова их цель, просто глядя на их ценность.
  • Нет повторного использования.
ответил jason dinAlt 5 FebruaryEurope/MoscowbMon, 05 Feb 2018 12:41:52 +0300000000pmMon, 05 Feb 2018 12:41:52 +030018 2018, 12:41:52
23

Пример реальной жизни: я работаю с третьей стороной, в которой «сущности» хранятся с «полями». В основном система EAV . Поскольку довольно просто добавить другое поле, вы получаете доступ к одному, используя имя поля в виде строки:

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

(обратите внимание на магическую строку «ProductName»)

Это может привести к нескольким проблемам:

  • Мне нужно ссылаться на внешнюю документацию, чтобы знать, что «ProductName» даже существует и его точное написание
  • Плюс мне нужно обратиться к этому документу, чтобы узнать, что такое тип данных этого поля.
  • Описания в этой магической строке не будут попадать, пока эта строка кода не будет выполнена.
  • Когда кто-то решает переименовать это поле на сервере (трудно предотвратить dataloss, но не невозможно), то я не могу легко выполнить поиск по своему коду, чтобы увидеть, где я должен настроить это имя.

Итак, мое решение для этого заключалось в создании констант для этих имен, организованных по типу сущности. Теперь я могу использовать:

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

Он по-прежнему является строковой константой и компилируется в один и тот же двоичный файл, но имеет несколько преимуществ:

  • После того, как я набрал «Модель», моя IDE показывает только доступные типы сущностей, поэтому я могу легко выбрать «Продукт».
  • Затем моя IDE поставляет только имена полей, которые доступны для этого типа сущности, также можно выбрать.
  • Автогенерированная документация показывает, что значение этого поля плюс тип данных, который используется для хранения его значений.
  • Начиная с константы, моя IDE может найти все места, где используется эта точная константа (в отличие от ее значения)
  • Опечатки будут пойманы компилятором. Это также применяется, когда для восстановления констант используется новая модель (возможно, после переименования или удаления поля).

Далее в моем списке: скрыть эти константы за сгенерированными строго типизированными классами - тогда также защищен тип данных.

ответил Hans Kesting 5 FebruaryEurope/MoscowbMon, 05 Feb 2018 19:21:24 +0300000000pmMon, 05 Feb 2018 19:21:24 +030018 2018, 19:21:24
9

Магические строки не всегда плохие , поэтому это может привести к тому, что вы не можете найти общую причину для их устранения. (Под «магической строкой» я предполагаю, что вы подразумеваете строковый литерал как часть выражения, а не как константу.)

В некоторых частных случаях следует избегать магических строк:

  • Такая же строка появляется несколько раз в коде. Это означает, что у вас может быть орфографическая ошибка в одном из мест. И это будет хлопот струнных изменений. Поверните строку в константу, и вы избежите этой проблемы.
  • Строка может меняться независимо от кода, где она появляется. Например. если строка представляет собой текст, отображаемый конечному пользователю, он, скорее всего, изменится независимо от любого изменения логики. Разделение такой строки на отдельный модуль (или внешнюю конфигурацию или базу данных) упростит изменение независимо.
  • Значение строки не является очевидным из контекста. В этом случае введение константы сделает код более понятным.

Но в некоторых случаях «магические строки» прекрасны. Скажем, у вас простой парсер:

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

Здесь нет никакой магии, и ни одна из вышеперечисленных проблем не применяется. Не было никакой пользы от ИМХО, чтобы определить string Plus="+" и т. Д. Держите это просто.

ответил JacquesB 5 FebruaryEurope/MoscowbMon, 05 Feb 2018 20:15:27 +0300000000pmMon, 05 Feb 2018 20:15:27 +030018 2018, 20:15:27
5

Чтобы добавить к существующим ответам:

Интернационализация (i18n)

Если текст, отображаемый на экране, жестко закорочен и утоплен внутри слоев функций, у вас будет очень трудное время, обеспечивающее перевод этого текста на другие языки.

Некоторые среды разработки (например, Qt) обрабатывают переводы путем поиска из текстовой строки базового языка на переведенный язык. Волшебные струны вообще могут выжить - пока вы не решите, что хотите использовать один и тот же текст в другом месте и получить опечатку. Даже тогда очень сложно найти, какие магические строки нужно перевести, если вы хотите добавить поддержку другого языка.

Некоторые среды разработки (например, MS Visual Studio) используют другой подход и требуют, чтобы все переведенные строки содержались в базе данных ресурсов и считывались для текущей локали по уникальному идентификатору этой строки. В этом случае ваше приложение с магическими строками просто не может быть переведено на другой язык без серьезной переделки. Для эффективного развития требуется, чтобы все текстовые строки были введены в базу данных ресурсов и получили уникальный идентификатор, когда код был впервые написан, и после этого будет довольно легко. Пытаясь засыпать это после того, как этот факт, как правило, потребует очень больших усилий (и да, я был там!), Поэтому гораздо лучше сделать все правильно.

ответил Graham 9 FebruaryEurope/MoscowbFri, 09 Feb 2018 17:58:38 +0300000000pmFri, 09 Feb 2018 17:58:38 +030018 2018, 17:58:38
3

Это не приоритет для всех, но если вы когда-либо захотите рассчитать метрики сцепления /сцепления на вашем коде в автоматическом режиме, магические строки делают это практически невозможным. Строка в одном месте будет относиться к классу, методу или функции в другом месте, и нет простого, автоматического способа определить, что строка связана с классом /методом /функцией, просто анализируя код. Только базовая структура (Угловая, например) может определить, что существует связь, и она может выполнять ее только во время выполнения. Чтобы получить информацию о связях самостоятельно, вашему парсеру нужно было бы знать все о структуре, которую вы использовали, выше и за пределами базового языка, в котором вы кодируете.

Но опять же, это не то, о чем заботятся многие разработчики.

ответил user3511585 6 FebruaryEurope/MoscowbTue, 06 Feb 2018 18:05:53 +0300000000pmTue, 06 Feb 2018 18:05:53 +030018 2018, 18:05:53

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132