Что такое магическое число, и почему оно плохое?


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

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

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

15 441

15 ответов:

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

например, если у вас есть (в 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, обнаруживают использование магических чисел в вашем коде и предлагают рефакторинг.

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

например, предположим, что у вас есть страница, которая отображает последние 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 может быть приемлемым, но это зависит от контекста.

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

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

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

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

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

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

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

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

магические числа по-прежнему полезны.

Магическое Число Против. Символьная Константа: когда на замену?

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

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

семантика: значение или цель вещи.

"создайте константу, назовите ее по значению и замените число на нее."Мартин Фаулер

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

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

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

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

основы

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

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

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

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

заменить ли это магическое число на символическую константу?"

- это:

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

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

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

тем не менее, мы обычно заменяем 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 в частях GUI UX "моя программа", мы, наконец, понимаем" 2 " в его правильном контексте. Здесь "2" - это "магическое" число, которое учитывается как символическое константа default_padding в контексте графического интерфейса UX "моя программа", чтобы использовать его как default_padding быстро понял в более широком контексте прилагаемого кода.

Дальше

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

  • full_life_force: INTEGER = 10 -- очень живой (и невредимый)
  • minimum_life_force: INTEGER = 1 -- еле живой (очень больно)
  • dead: INTEGER = 0 -- Мертв
  • нежить: целое число = -1 -- Min нежить (почти мертвая)
  • zombie: INTEGER = -10 -- Max undead (очень нежить)
-10 .. 10. Просто диапазон без слов уходит мы находимся в месте, возможно, большой путаницы и потенциально с ошибками в нашей игре, если разные части игры имеют зависимости от того, что этот диапазон чисел означает для различных операций, таких как attack_elves или seek_magic_healing_potion.

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

вывод

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

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

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

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

в конце концов, ответ на замену-это ответ на меру (в вашем уме) силы или слабости читателя, чтобы сделать соединение (например, "get it"). Чем быстрее они понимают смысл и цель, тем меньше у вас "магии".

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

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

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

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

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

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

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

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

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

возможно, 22, 7, -12 и 620 граф, как магические числа, тоже. ; -)

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

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

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

Я всегда использовал термин "магическое число" по-разному, как неясное значение, хранящееся в структуре данных, которое может быть проверено как быстрая проверка действительности. Например, файлы gzip содержат 0x1f8b08 в качестве первых трех байтов, файлы классов Java начинаются с 0xcafebabe и т. д.

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

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

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

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.

@eed3si9n: я бы даже предположил, что '1' - это магическое число. : -)

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

как насчет возвращаемых переменных?

Я специально считаю это сложным при реализации хранимых процедур.

представьте себе следующую хранимую процедуру (неверный синтаксис, я знаю, просто чтобы показать пример):

int procGetIdCompanyByName(string companyName);

он возвращает идентификатор компании, если он существует в определенной таблице. В противном случае он возвращает -1. Каким-то образом это магическое число. Некоторые из рекомендаций, которые я читал до сих пор, говорят, что мне действительно нужно будет сделать что-то вроде дизайна что:

int procGetIdCompanyByName(string companyName, bool existsCompany);

кстати, что он должен вернуться, если компания не существует? Ок: он будет установлен existesCompany как ложные, но также вернет -1.

опция Antoher состоит в том, чтобы сделать две отдельные функции:

bool procCompanyExists(string companyName);
int procGetIdCompanyByName(string companyName);

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

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

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

public class Foo {
    /** 
     * Max age in year to get child rate for airline tickets
     * 
     * The value of the constant is {@value}
     */
    public static final int MAX_AGE_FOR_CHILD_RATE = 2;

    public void computeRate() {
         if (person.getAge() < MAX_AGE_FOR_CHILD_RATE) {
               applyChildRate();
         }
    }
}