Что такое магическое число и почему оно плохо? - PullRequest
478 голосов
/ 07 сентября 2008

Что такое магическое число?

Почему этого следует избегать?

Есть ли случаи, когда это уместно?

Ответы [ 15 ]

534 голосов
/ 07 сентября 2008

Магическое число - это прямое использование числа в коде.

Например, если у вас есть (на Java):

public class Foo {
    public void setPassword(String password) {
         // don't do this
         if (password.length() > 7) {
              throw new InvalidArgumentException("password");
         }
    }
}

Это должно быть изменено на:

public class Foo {
    public static final int MAX_PASSWORD_SIZE = 7;

    public void setPassword(String password) {
         if (password.length() > MAX_PASSWORD_SIZE) {
              throw new InvalidArgumentException("password");
         }
    }
}

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

JDK полон примеров, подобных классам Integer, Character и Math.

PS: инструменты статического анализа, такие как FindBugs и PMD, обнаруживают использование магических чисел в вашем коде и предлагают рефакторинг.

142 голосов
/ 07 сентября 2008

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

Например, допустим, у вас есть страница, на которой отображаются последние 50 заказов на обзорной странице "Ваши заказы". Здесь 50 - это магическое число, потому что оно не установлено стандартом или соглашением, это число, которое вы создали по причинам, указанным в спецификации.

Теперь у вас есть 50 в разных местах - ваш SQL-скрипт (SELECT TOP 50 * FROM orders), ваш веб-сайт (ваши последние 50 заказов), логин вашего заказа (for (i = 0; i < 50; i++)) и, возможно, многие другие места.

Теперь, что происходит, когда кто-то решает изменить 50 на 25? или 75? или 153? Теперь вам нужно заменить 50 во всех местах, и вы, скорее всего, пропустите это. Поиск / замена может не работать, потому что 50 может использоваться для других целей, а слепая замена 50 на 25 может иметь некоторые другие плохие побочные эффекты (например, ваш Session.Timeout = 50 вызов, который также установлен на 25, и пользователи начинают сообщать о слишком частых таймаутах ).

Кроме того, код может быть трудным для понимания, то есть «if a < 50 then bla» - если вы столкнетесь с этим в середине сложной функции, другие разработчики, которые не знакомы с кодом, могут спросить себя «WTF равен 50 ?? ? "

Вот почему лучше иметь такие неоднозначные и произвольные числа ровно в 1 месте - "const int NumOrdersToDisplay = 50", потому что это делает код более читабельным ("if a < NumOrdersToDisplay", это также означает, что вам нужно только изменить его в 1 четко определенное место.

Места, где подходят магические числа, - это все, что определено стандартом, т. Е. SmtpClient.DefaultPort = 25 или TCPPacketSize = whatever (не уверен, стандартизирован ли он). Кроме того, все, что определено только в пределах 1 функции, может быть приемлемым, но это зависит от контекста.

32 голосов
/ 07 сентября 2008

Вы просматривали запись в Википедии для магического числа?

В нем подробно рассказывается обо всех способах ссылки на магические числа. Вот цитата о магическом числе как о плохой практике программирования

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

23 голосов
/ 25 октября 2015

магическое число против Символическая константа: когда заменить?

Магия: Неизвестная семантика

Символическая константа -> Предоставляет как правильный семантический, так и правильный контекст для использования

Семантика: значение или назначение вещи.

«Создайте константу, назовите ее после значения и замените ее числом». - Мартин Фаулер

Во-первых, магические числа - это не просто числа. Любое базовое значение может быть «магическим». Базовые значения - это объекты манифеста, такие как целые числа, вещественные числа, числа с плавающей запятой, числа с плавающей запятой, даты, строки, логические значения, символы и т. Д. Проблема заключается не в типе данных, а в «магическом» аспекте значения, как это показано в нашем коде.

Что мы подразумеваем под "магией"? Чтобы быть точным: «магией» мы намереваемся указать на семантику (значение или цель) значения в контексте нашего кода; что это неизвестно, непостижимо, неясно или сбивает с толку. Это понятие "магия". Базовое значение не является магическим, когда его семантическое значение или цель существования быстро и легко узнаются, ясны и понятны (не путаются) из окружающего контекста без специальных вспомогательных слов (например, символической константы).

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

Полезные определения

  • сбить с толку: заставить (кого-то) сбиться с толку или растерян.
  • сбит с толку: привести кого-то в замешательство и замешательство.
  • озадачен: полностью сбит с толку; очень озадачен.
  • сбит с толку: совершенно сбит с толку или озадачен.
  • озадачен: не в состоянии понять; озадачен.
  • понимать: воспринимать предполагаемое значение (слова, язык или говорящий).
  • значение: что означает слово, текст, понятие или действие.
  • означает: намереваться передать, указать или сослаться (на конкретную вещь или понятие); означают.
  • означать: указание на.
  • указание: знак или часть информации, которая указывает что-либо.
  • указать: указать; показать.
  • знак: объект, качество или событие, наличие или происхождение которого указывает на вероятное присутствие или возникновение чего-либо еще.

Основы

У нас есть два сценария для наших основных магических ценностей. Только второе имеет первостепенное значение для программистов и кода:

  1. Одиночное базовое значение (например, число), значение которого неизвестно, неизвестно, неясно или сбивает с толку.
  2. Базовое значение (например, число) в контексте, но его значение остается неизвестным, непознаваемым, неясным или сбивающим с толку.

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

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

«Заменить ли это магическое число символической константой?»

это:

"Как быстро вы можете оценить и понять семантическое значение номер (его цель для того, чтобы быть там) в его контексте? "

Вид магии, но не совсем

Имея в виду эту мысль, мы можем быстро увидеть, что число, такое как Pi (3.14159), не является "магическим числом", если помещено в надлежащий контекст (например, 2 x 3.14159 x радиус или 2 * Pi * r). Здесь число 3.14159 мысленно распознается как Пи без символического идентификатора константы.

Тем не менее, мы обычно заменяем 3.14159 символьным постоянным идентификатором, таким как Pi, из-за длины и сложности числа. Аспекты длины и сложности числа Pi (в сочетании с необходимостью в точности) обычно означают, что символический идентификатор или константа менее подвержены ошибкам. Признание «Пи» в качестве имени - это просто удобный бонус, но не основная причина наличия постоянной.

Тем временем: обратно на ранчо

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

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

Давайте поместим нашу одиночную «2» в контекст: padding := 2, где контекст - это «Контейнер GUI». В этом контексте значение 2 (в виде пикселей или другой графической единицы) предлагает нам быстрое предположение о его семантике (значение и цель). Мы могли бы остановиться здесь и сказать, что 2 в этом контексте хорошо, и нам больше ничего не нужно знать. Однако, возможно, в нашей программной вселенной это еще не все. Это еще не все, но "padding = 2" как контекст не может раскрыть его.

Давайте далее притворимся, что 2 в качестве отступа пикселей в нашей программе имеет разновидность «default_padding» во всей нашей системе. Поэтому написание инструкции padding = 2 недостаточно. Понятие «дефолт» не раскрывается. Только когда я пишу: padding = default_padding в качестве контекста, а затем в другом месте: default_padding = 2, я полностью осознаю лучшее и более полное значение (семантическое и целевое) значения 2 в нашей системе.

Пример выше довольно хорош, потому что «2» может быть чем угодно. Только когда мы ограничиваем диапазон и область понимания «моей программой», где 2 - это default_padding в UX-части GUI «моей программы», мы наконец понимаем «2» в соответствующем контексте. Здесь «2» - это «магическое» число, которое переводится в символическую константу default_padding в контексте пользовательского интерфейса GUI «моей программы», чтобы его можно было использовать как default_padding, быстро понимаемое в более широком контексте кода вложения.

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

Идем дальше

Числа на шкале также могут иметь семантику. Например, представьте, что мы делаем игру D & D, где у нас есть понятие монстра. Наш монстр объект имеет функцию под названием life_force, которая является целым числом. Числа имеют значения, которые невозможно понять или понять без слов, чтобы придать смысл. Таким образом, мы начинаем с произвольной поговорки:

  • full_life_force: INTEGER = 10 - очень живой (и невредимый)
  • imum_life_force: INTEGER = 1 - едва жив (очень больно)
  • мертв: INTEGER = 0 - мертв
  • нежить: INTEGER = -1 - Мин нежити (почти мертвый)
  • зомби: INTEGER = -10 - Макс нежити (очень нежить)

Исходя из символических констант, приведенных выше, мы начинаем получать мысленную картину живости, мертвости и «нежити» (и возможных последствий или последствий) для наших монстров в нашей игре D & D. Без этих слов (символических констант) у нас останутся только цифры в диапазоне от -10 .. 10. Просто диапазон без слов оставляет нас в возможной путанице и, возможно, с ошибками в нашей игре, если разные части игры зависят от значения этого диапазона чисел для различных операций, таких как attack_elves или seek_magic_healing_potion.

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

Заключение

Давайте рассмотрим, какие вопросы мы должны задать:

У вас может быть магическое число, если ...

  1. Может ли базовое значение иметь особое значение или цель в вашей вселенной программного обеспечения?
  2. Может ли особое значение или цель быть неизвестными, непознаваемыми, неясными или сбивающими с толку даже в собственном контексте?
  3. Может ли правильное базовое значение быть неправильно использовано с плохими последствиями в неправильном контексте?
  4. Может ли неправильное базовое значение правильно использоваться с плохими последствиями в правильном контексте?
  5. Имеет ли базовое значение семантические или целевые отношения с другими базовыми значениями в определенных контекстах?
  6. Может ли базовое значение существовать в более чем одном месте в нашем коде с различной семантикой в ​​каждом, что вызывает у читателя путаницу?

Изучите автономные базовые значения манифеста в тексте кода. Медленно и вдумчиво задавайте каждый вопрос о каждом случае такой ценности. Подумайте о силе вашего ответа. Часто ответ не черно-белый, но имеет оттенки неправильно понятого значения и цели, скорости обучения и скорости понимания. Также необходимо посмотреть, как он подключается к программному компьютеру вокруг него.

В конце ответом на замену является ответ на меру (по вашему мнению) силы или слабости читателя, чтобы установить связь (например, «получить его»). Чем быстрее они понимают смысл и цель, тем меньше у вас «волшебства».

ЗАКЛЮЧЕНИЕ: Заменяйте базовые значения символическими константами только тогда, когда магия достаточно велика, чтобы затруднять обнаружение ошибок, возникающих в результате путаницы.

17 голосов
/ 07 сентября 2008

Магическое число - это последовательность символов в начале формата файла или протокола обмена. Этот номер служит проверкой работоспособности.

Пример: Откройте любой файл GIF, вы увидите в самом начале: GIF89. «GIF89» - магическое число.

Другие программы могут читать первые несколько символов файла и правильно идентифицировать GIF-файлы.

Опасность заключается в том, что случайные двоичные данные могут содержать эти же символы. Но это очень маловероятно.

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

Магические числа все еще полезны.

11 голосов
/ 07 сентября 2008

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

Это плохо по той же причине, что SPOT (Single Point of Truth) хорош: если вы захотите изменить эту константу позже, вам придется искать код, чтобы найти каждый экземпляр. Это также плохо, потому что другим программистам может быть непонятно, что представляет это число, отсюда и «магия».

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

10 голосов
/ 13 декабря 2008

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

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

И затем, разумеется, вам нужно изменить значение ... только для одной цели.

9 голосов
/ 07 сентября 2008

Магическое число также может быть числом со специальной жестко закодированной семантикой. Например, однажды я видел систему, в которой идентификаторы записей> 0 обрабатывались нормально, сам 0 был «новой записью», -1 был «это корень», а -99 был «это было создано в корне». 0 и -99 приведут к тому, что WebService предоставит новый идентификатор.

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

Возможно, 22, 7, -12 и 620 тоже считаются магическими числами. ; -)

4 голосов
/ 07 сентября 2008

Я предполагаю, что это ответ на мой ответ на ваш предыдущий вопрос. В программировании магическое число - это встроенная числовая константа, которая появляется без объяснения причин. Если он появляется в двух разных местах, это может привести к обстоятельствам, когда один экземпляр изменяется, а не другой. По обеим этим причинам важно изолировать и определить числовые константы вне мест их использования.

3 голосов
/ 12 октября 2009

Как насчет инициализации переменной в верхней части класса значением по умолчанию? Например:

public class SomeClass {
    private int maxRows = 15000;
    ...
    // Inside another method
    for (int i = 0; i < maxRows; i++) {
        // Do something
    }

    public void setMaxRows(int maxRows) {
        this.maxRows = maxRows;
    }

    public int getMaxRows() {
        return this.maxRows;
    }

В этом случае 15000 - это магическое число (согласно CheckStyles). Для меня установка значения по умолчанию в порядке. Я не хочу делать:

private static final int DEFAULT_MAX_ROWS = 15000;
private int maxRows = DEFAULT_MAX_ROWS;

Это затрудняет чтение? Я никогда не думал об этом, пока не установил CheckStyles.

...