Подтвердить что ты не робот

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

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

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

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

4b9b3361

Ответ 1

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

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

Ответ 2

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

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

Теперь, что вы делаете, у вас есть 50 в разных местах - ваш SQL script (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??"

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

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

Ответ 3

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

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

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

Ответ 4

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

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

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

Семантика: смысл или цель вещи.

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

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

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

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

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

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

Основы

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

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

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

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

"Я заменяю это магическое число символической константой?"

:

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

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

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

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

Между тем: Назад на ранчо

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

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

Положим наш одиночный "2" в контексте: padding := 2, где контекст является "GUI Container". В этом контексте значение 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 в контексте UI GUI "моей программы", чтобы использовать его как default_padding, которое быстро понимается в большем контексте прилагаемого кода.

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

Далее >

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

  • full_life_force: INTEGER = 10 - Очень живой (и невредимый)
  • minimum_life_force: INTEGER = 1 - Довольно живой (очень больно)
  • dead: INTEGER = 0 - Dead
  • undead: INTEGER = -1 - Min undead (почти мертв)
  • зомби: INTEGER = -10 - максимальная нежить (очень нежити)

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

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

Заключение

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

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

  • Может ли базовое значение иметь особое значение или цель в вашем программном мире?
  • Может ли особый смысл или цель быть неизвестным, непознаваемым, непонятным или запутанным даже в его надлежащем контексте?
  • Может ли неправильное базовое значение быть неправильно использовано с плохими последствиями в неправильном контексте?
  • Может ли неправильное базовое значение правильно использоваться с плохими последствиями в правильном контексте?
  • Имеет ли базовое значение семантические или целевые отношения с другими базовыми значениями в конкретных контекстах?
  • Может ли базовое значение присутствовать в более чем одном месте в нашем коде с различной семантикой в ​​каждом, что вызывает у нашего читателя путаницу?

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

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

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

Ответ 5

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

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

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

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

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

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

Ответ 6

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

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

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

Ответ 7

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

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

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

Ответ 8

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

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

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

Ответ 9

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

Ответ 10

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

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

Ответ 11

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

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

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

Ответ 12

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

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.

Ответ 13

@eed3si9n: Я даже предположил, что "1" - магическое число.: -)

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

Ответ 14

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

Мне особенно сложно выполнять хранимые процедуры.

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

int procGetIdCompanyByName(string companyName);

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

int procGetIdCompanyByName(string companyName, bool existsCompany);

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

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

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

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

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

Суть в следующем: что вы думаете об использовании таких "волшебных чисел", которые относительно известны и безопасны, чтобы сказать, что что-то не увенчалось успехом или что-то не существует?

Ответ 15

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

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();
         }
    }
}