Что такое магическое число и почему оно плохое?
Что такое магическое число?
Почему этого следует избегать?
есть случаи, когда это уместно?
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");
}
}
}
Это улучшает читаемость кода и его легче поддерживать. Представьте себе случай, когда я устанавливаю размер поля пароля в GUI. Если я использую магическое число, всякий раз, когда максимальный размер изменяется, я должен изменить в двух местах кода. Если я забуду одну, это приведет к неточности.
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 может быть приемлемым, но это зависит от контекста.
вы взглянули на запись Википедии для магическое число?
он вдается в детали обо всех способах ссылки на магическое число. Вот цитата о magic number как плохой практике программирования
термин магическое число также относится к плохой практике программирования использования чисел непосредственно в исходном коде без объяснения причин. В большинстве случаев это затрудняет чтение, понимание и обслуживание программ. Хотя большинство руководств делают исключение для чисел ноль и один, неплохо определить все остальные числа в коде как именованные константы.
магическое число-это последовательность символов в начале файла формат или протокол обмена. Эта цифра является проверочной.
пример: Откройте любой GIF-файл, который вы увидите в самом начале: GIF89. "GIF89" - магическое число.
другие программы могут читать первые несколько символов файла и правильно идентифицировать GIFs.
опасность заключается в том, что случайные двоичные данные могут содержать эти самые символы. Но это маловероятно.
Что касается обмена протоколами, вы можете использовать его, чтобы быстро определить, что текущее "сообщение", которое передается вам, повреждено или недействительно.
магические числа по-прежнему полезны.
Магическое Число Vs. Символьная Константа: когда заменить?
магия: неизвестная семантика
символьная Константа - > обеспечивает как правильный семантический и правильный контекст для использования
семантический: смысл или цель вещи.
"создайте константу, назовите ее после значения и замените число на нее.- ...Мартин Фаулер!--14-->
во-первых, магические числа-это не просто числа. Любое базовое значение может быть "волшебным". Основные ценности манифестные сущности, такие как целые числа, реалы, двойники, поплавки, даты, строки, логические значения, символы и так далее. Проблема не в типе данных, а в" волшебном " аспекте значения, как оно появляется в нашем тексте кода.
что мы подразумеваем под "магией"? Если быть точным: "магией" мы намерены указать на семантику (смысл или цель) значения в контексте нашего кода; что оно неизвестно, непознаваемо, неясно или запутанно. Это и есть понятие "магия". Базовое значение-это не магия, когда его семантический смысл или цель бытия-быстро и легко узнаваем, понятен и понятен (не путает) из окружающего контекста без специальных вспомогательных слов (например, символическая константа).
таким образом, мы идентифицируем магические числа, измеряя способность читателя кода знать, быть ясным и понимать смысл и цель базового значения из окружающего контекста. Чем менее известен, менее понятен и более запутан читатель, тем более "волшебной" является основная ценность есть.
Полезные Определения
- путать: причина (кто-то), чтобы стать в недоумении или недоумении.
- недоумение: причина (кто-то) стали в недоумении и замешательстве.
- озадачен: совершенно сбит с толку; очень озадачен.
- озадачен: полностью озадачен или озадачен.
- озадачен: не может понять; озадачен.
- понять: воспринимайте Предполагаемое значение (слова, язык или говорящий).
- значение: что подразумевается под словом, текстом, концепцией или действием.
- означало: намереваться передать, указать или сослаться на (определенную вещь или понятие); обозначить.
- signify: быть указанием.
- индикация: знак или часть информации, которая указывает на что-то.
- указать: указать; показать.
- знак: объект, качество или событие, присутствие или возникновение которого указывает на вероятное присутствие или возникновение что-то еще.
основы
у нас есть два сценария для наших основных магических ценностей. Только второе имеет первостепенное значение для программистов и кода:
- единственное базовое значение (например, число), из которого его значение неизвестно, непознаваемо, неясно или запутанно.
- базовое значение (например, число) в контексте, но его значение остается неизвестным, непознаваемым, неясным или запутанным.
общих зависимостей "магия" - это то, как единственное базовое значение (например, число) не имеет общеизвестной семантики (например, Pi), но имеет локально известную семантику (например, ваша программа), которая не совсем понятна из контекста или может быть использована в хорошем или плохом контексте(контекстах).
семантика большинства языков программирования не позволит нам использовать одиночные базовые значения, кроме (возможно) в качестве данных (т. е. таблиц данных). Когда мы сталкиваемся с" магическими числами", мы обычно делаем это в контексте. Поэтому ответ на
"заменить ли это магическое число на символическую константу?"
- это:
" как быстро вы можете оценить и понять смысловое значение число (его цель быть там) в его контексте?"
вид магии, но не совсем
имея в виду эту мысль, мы можем быстро увидеть, как такое число, как Pi (3.14159), не является "магическим числом", если поместить его в надлежащий контекст (например, 2 X 3.14159 X радиус или 2*Pi*r). Здесь число 3.14159 мысленно распознается Pi без идентификатора символьной константы.
тем не менее, мы обычно заменяем 3.14159 символьным постоянным идентификатором, таким как Pi, из-за длины и сложности числа. Аспекты длины и сложности Pi (в сочетании с необходимостью точности) обычно означают, что символьный идентификатор или константа менее подвержены ошибкам. Признание "Пи" в качестве имени-это просто удобный бонус, но это не главная причина наличия константы.
тем временем: назад на ранчо
если я использую число 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 UX "моей программы", чтобы использовать его как default_padding
быстро понял в более широком контексте прилагаемого кода.
таким образом, любое базовое значение, смысл (смысл и цель) которого не может быть достаточно и быстро понят, является хорошим кандидатом на символическую константу вместо базового значения (например, магическое число).
Дальше
числа в масштабе также могут иметь семантику. Для например, представьте, что мы делаем игру D&D, где у нас есть понятие монстра. Наш объект monster имеет функцию под названием life_force
, который является целым числом. Числа имеют значения, которые не могут быть понятны или понятны без слов, чтобы придать смысл. Итак, мы начинаем с произвольного высказывания:--14-->
- full_life_force: INTEGER = 10 -- очень живой (и невредимый)
- minimum_life_force: INTEGER = 1 -- еле живой (очень больно)
- dead: целое число = 0 -- Мертвый
- нежить: целое число = -1 -- мин нежить (почти мертвая)
- зомби: целое число = -10 -- Макс нежить (очень нежить)
из символических констант выше мы начинаем получать ментальную картину живости, мертвенности и "нежити" (и возможных последствий) для наших монстров в нашей игре D&D. Без этих слов (символических констант) мы остаемся только с числами в диапазоне от -10 .. 10
. Просто диапазон без слов уходит. мы в месте, возможно, большой путаницы и потенциально с ошибками в нашей игре, если разные части игры имеют зависимости от того, что этот диапазон чисел означает для различных операций, таких как attack_elves
или seek_magic_healing_potion
.
поэтому при поиске и рассмотрении замены "магических чисел" мы хотим задать очень целенаправленные вопросы о числах в контексте нашего программного обеспечения и даже о том, как числа семантически взаимодействуют друг с другом другой.
вывод
давайте рассмотрим, какие вопросы мы должны задать:
у вас может быть магическое число, если ...
- может ли базовое значение иметь особый смысл или цель в вашей вселенной программного обеспечения?
- может ли правильное базовое значение неправильно использоваться с плохими последствиями в неправильный контекст?
- можно ли правильно использовать неправильное базовое значение с плохими последствиями в правильном контексте?
- имеет ли базовое значение семантические или целевые отношения с другими базовыми значениями в определенных контекстах?
- может ли базовое значение существовать более чем в одном месте в нашем коде с различной семантикой в каждом, тем самым вызывая у нашего читателя путаницу?
Проверьте автономные манифестные постоянные базовые значения в тексте кода. Задавайте каждый вопрос медленно и вдумчиво о каждом экземпляре такого значения. Подумайте о силе вашего ответа. Во многих случаях ответ не черно-белый, но имеет оттенки неправильно понятого смысла и цели, скорости обучения и скорости понимания. Существует также необходимость увидеть, как он подключается к программной машине вокруг него.
в конце концов, ответ на замену-это ответ на меру (в вашем уме) силы или слабости читателя, чтобы сделать подключение (например, "get it"). Чем быстрее они понимают смысл и цель, тем меньше у вас "магии".
заключение: замените базовые значения символьными константами только тогда, когда магия достаточно велика, чтобы вызвать трудности с обнаружением ошибок, возникающих из-за путаницы.
в программировании "магическое число" - это значение, которому должно быть дано символическое имя, но вместо этого оно было вставлено в код как литерал, обычно в нескольких местах.
Это плохо по той же причине, что и SPOT (Single Point of Truth): если вы хотите изменить эту константу позже, вам придется искать свой код, чтобы найти каждый экземпляр. Это также плохо, потому что другим программистам может быть непонятно, что представляет собой это число, поэтому "магия."
люди иногда принимают устранение магического числа дальше, перемещая эти константы в отдельные файлы, чтобы действовать как конфигурация. Это иногда полезно, но также может создать больше сложности, чем стоит.
проблема, которая не упоминалась при использовании магических чисел...
Если у вас их очень много, шансы на то, что у вас есть два разных цели что вы используете магические числа Для, где значения случилось то же самое.
и затем, конечно же, вам нужно изменить значение... только с одной целью.
магическое число также может быть числом со специальным, жестко семантики. Например, я однажды видел систему, где идентификаторы записей > 0 обрабатывались нормально, 0 сам был "новой записью", -1 был" это корень "и -99 был"это было создано в корне". 0 и -99 заставят веб-сервис предоставить новый идентификатор.
плохо то, что вы повторно используете пробел (что из целых чисел со знаком для идентификаторов записей) для специальных способностей. Возможно, вы никогда не захотите создать запись с ID 0 или с отрицательным ID, но даже если нет, каждый человек, который смотрит на код или на базу данных может наткнуться на это и смутило поначалу. Само собой разумеется, что эти особые ценности не были хорошо документированы.
возможно, 22, 7, -12 и 620 граф, как магические числа, тоже. ;-)
Я предполагаю, что это ответ на мое ответ к вашему предыдущему вопросу. В программировании магическое число является встроенной числовой константой, которая появляется без объяснения причин. Если он появляется в двух разных местах, это может привести к обстоятельствам, когда один экземпляр изменяется, а не другой. По обеим этим причинам, важно, чтобы изолировать и определить числовые константы вне мест, где они используются.
стоит отметить, что иногда вам нужны не настраиваемые "жестко закодированные" номера в вашем коде. Существует ряд известные из них включая 0x5F3759DF, который используется в оптимизированном алгоритме обратного квадратного корня.
в редких случаях, когда я нахожу необходимость использовать такие магические числа, я устанавливаю их как const в своем коде и документирую, почему они используются, как они работают и откуда они пришли.
Я всегда использовал термин "магическое число" по-разному, как неясное значение, хранящееся в структуре данных, которая может быть проверена как быстрая проверка правильности. Например, файлы gzip содержат 0x1f8b08 в качестве первых трех байтов, файлы классов Java начинаются с 0xcafebabe и т. д.
вы часто видите магические числа, встроенные в форматы файлов, потому что файлы могут быть отправлены довольно беспорядочно и потерять любые метаданные о том, как они были созданы. Однако магические числа также иногда используются для структур данных в памяти, таких как вызовы ioctl ().
быстрая проверка магического числа перед обработкой файла или структуры данных позволяет сигнализировать об ошибках раньше, а не schlep на всем пути потенциально длительной обработки, чтобы объявить, что вход был полным вздором.
насчет инициализации переменной в верхней части класса со значением по умолчанию? Например:
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);
кстати, что он должен вернуться, если компания не существует? Ok: он будет установлен existesCompany as false, но также вернет -1.
другой вариант-сделать две отдельные функции:
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();
}
}
}