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

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

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

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


person Adam Davis    schedule 06.09.2008    source источник
comment
Вы бы избегали магических чисел, потому что другие люди, просматривающие ваш код, могут не понять, почему вы делаете то, что делаете ... например. const myNum = 22; const number = myNum / 11; прямо сейчас мои 11 могли быть людьми или бутылками пива или чем-то еще, поэтому вместо этого я бы изменил 11 на константу, такую ​​как жители.   -  person    schedule 17.09.2019
comment
Использование магических чисел в атрибутах неизбежно, поэтому я думаю, это уместно.   -  person donatasj87    schedule 23.10.2019
comment
Здесь много обсуждается использование магического числа, но почему бы не провести более широкий разговор, чтобы охватить также использование других «магических» констант, таких как строка, которая никогда не изменяется, например, для определения типа. Это хорошая практика или это ухудшает читабельность?   -  person Mg Gm    schedule 22.10.2020


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

person Marcio Aguiar    schedule 06.09.2008
comment
В мире .NET метрики кода FxCop и Visual Studio 2008 должны указать вам на это. - person paulwhit; 18.09.2008
comment
0 и 1 - исключения из этого правила. - person Jonathan Parker; 08.04.2009
comment
Следует написать: public static final HUNDRED_PERCENTS = 100;? - person Kirill V. Lyadvinsky; 22.07.2009
comment
@Kirill: Если вы ожидаете, что определение сотни процентов изменится, тогда да. Лучшим подходом было бы иметь переменную от того, что она есть, до того, что она представляет, то есть public static final MAX_DOWNLOAD_PERCENTAGE = 100. Хотя даже это не имеет смысла, потому что 100 процентов очень хорошо определены. С другой стороны, тот факт, что пароли могут состоять максимум из 7 символов, глобально не определен и фактически различается, так что это кандидат на роль переменной. - person Michael Stum; 19.09.2009
comment
@ Джонатан Паркер, кроме случаев, когда это не так (_1 _ / _ 2_) - person Brendan Long; 02.06.2010
comment
Тот факт, что магическое число никогда не изменится, не означает, что его нельзя заменять константой. Мой код полон глобальных констант, таких как HzPerMHz и msecPerSecond. Они никогда не изменятся, но они проясняют смысл и обеспечивают некоторую защиту от опечаток. - person Jeanne Pindar; 04.06.2010
comment
@paulwhit checkstyle может делать то же самое в java. - person Timo; 14.12.2012
comment
@JonathanParker Не согласен, зависит от контекста. - person b1nary.atr0phy; 30.04.2013
comment
другие хорошие естественные константы включают ONE_MINUTE = 60, ONE_HOUR = 3600, ONE_DAY = 86400 (большинство программистов будут работать с эпохами) ... или - person ; 28.08.2013
comment
Я бы сказал, что этот код МЕНЬШЕ читаем, зачем вам искать определение MAX_PASSWORD_SIZE, если оно не изменится? вы буквально просто делаете его более спагеттовым. - person MarcusJ; 18.02.2014
comment
@BrendanLong Это не всегда должно быть TRUE / FALSE как исключение. Пример: if(array.length == 0) (или < 1) - person das Keks; 08.04.2014
comment
@MarcusJ Трудно ошибиться. Это не вопрос мнения, а дело обстоит с тяжким трудом, накопленным многими программистами. Я не могу сказать вам, сколько раз за последние 40 лет программирования я проклинал предыдущего программиста, который не определял константу, поэтому я обнаружил только прямое использование числа, которое нужно было понимать во время сопровождения кода. , похороненный где-то в большом количестве кода, значение которого можно было бы прояснить, определив такую ​​константу. У любого другого старшего программиста также будет несколько ужасных историй по этому поводу. - person ToolmakerSteve; 29.09.2014
comment
... Важно выработать привычку определять константы для придания значения произвольным числам, даже в тривиальных методах, где это кажется очевидным. Почему? Потому что в будущем этот метод может быть добавлен в. То, что сначала казалось очевидным, теперь находится где-то во многих строках кода. Что касается спагетизма, AFAIK все приличные современные IDE делают тривиальным поиск значения постоянной переменной. Обычно можно просто навести указатель мыши на любое использование переменной. И даже в те времена, когда у нас не было таких тонкостей, в конечном итоге это того стоило. - person ToolmakerSteve; 29.09.2014
comment
А как насчет таких вещей, как: connection.setTimeout(50); Должен ли 50 быть здесь константой? Кажется, довольно ясно, что 50 - это тайм-аут соединения. - person Sergey Kolodyazhnyy; 12.12.2014
comment
Все хорошо, но дело в том, что при использовании hibernate критериев.list () вы часто получаете массивы Object, и тогда я не буду использовать константу для указания позиции индекса поля записи ... :-P - person Lawrence; 20.03.2015
comment
@Sergey А что насчет таких вещей, как: connection.setTimeout (50); Должно ли 50 быть здесь постоянной величиной? Кажется довольно очевидным, что 50 - это тайм-аут соединения - это магическое число независимо от одного или нескольких вызовов. Почему 50? Почему не 51? Возможно, вам придется изменить его, потому что вы развертываете среду, для которой требуются разные тайм-ауты. Я бы предпочел изменить константу, чем копаться в коде. - person MickyD; 09.04.2015
comment
Хм, просто столкнулся с этим при реализации hashCode (), где у вас часто есть магические числа, не используемые в другом месте. Теперь lint заставляет меня добавить константу, которая, IMHO, никак не улучшает читаемость. lint верен в 99% случаев и в 1% случаев действительно отстой. - person Michael; 25.04.2015
comment
Непосредственное использование магических чисел само по себе неплохо. Многие математические формулы состоят из магических чисел, на самом деле, очень много. Та же самая математика, на которой построены компьютеры и программное обеспечение. Просто более полезно сослаться на число , которое может быть переработано в будущем с использованием переменной. Поэтому, на мой взгляд, этот ответ слишком упрощен. Попробуйте реализовать таким образом простую физику, вы поймете, о чем я. - person Tim; 26.10.2015
comment
@Lawrence, вы все равно можете использовать константу, нет недостатка IMHO, потому что, если вы создаете класс определения для этого, который просто содержит константы, вам нужно будет только пойти туда и изменить его, а не выслеживать все места с той же строкой . Также вы можете связать их с String.Format() (C #) или чем-то еще, что вы могли бы сделать public const string CELLPosition= "ROW{0}Col{1}", а затем просто String.Format(CELLPosition,row,col), и вы получите то, что хотите. - person WiiMaxx; 11.02.2016
comment
А что насчет этого конкретного случая? const passwordExpiresAt = moment().add(2, 'days').toDate(); Мне действительно нужно определить PASSWORD_EXPIRE_TIME_IN_DAYS константу? Это используется только в одном месте в коде, и значение 2 явно взято из остальной части строки. Если позже я изменю его на 1 или 3, значение все равно будет очевидным. - person Merlin -they-them-; 07.12.2016
comment
1 и 0 могут быть магическими числами, это просто зависит от контекста. Проверяя if someArray.length === 0, цель очевидна, вы проверяете, пусто ли оно, не считается ли оно магическим числом. Но если вы сделаете createSomething(1), это магическое число. Невозможно получить из контекста, что это за 1, особенно когда это 1 действует как boolean. Если у вас был true или false, ясно, что этот параметр включен или выключен. - person tdtm; 22.01.2019
comment
@MickyD: Для меня число волшебно только в том случае, если его значение не является явным. Я не понимаю, почему вы думаете, что не задали бы себе тот же вопрос (почему 50, а не 51), если бы число было определено как константа. Что касается требований к будущим изменениям, я бы поспорил с YAGNI (но в тот день, когда он вам понадобится, да, во что бы то ни стало, рефакторинг его до константы). - person AgentCorleone; 21.02.2019
comment
@AgentCorleone не имеет ничего общего с значением, явным или каким-либо другим образом. Это связано с жестко запрограммированными значениями в коде. Если одно и то же значение используется в коде более одного раза, оно должно быть defined as a constant 1) чтобы быть уверенным, что ваш код синхронизирован 2) в случае, если вы хотите легко изменить его в future, о чем всегда должен думать хороший разработчик. YAGNI, будучи сторонником недисциплинированного давайте-просто-отправим-за дверь, идет вразрез с долгосрочным мышлением. Так что я не удивлен, что YAGNI это делают. - person MickyD; 21.02.2019
comment
@MickyD: Ну, согласно определению Википедии (которое мне кажется довольно приличным источником), в нем явно упоминается необъяснимое значение ... И я, конечно же, сказал, что вам следует использовать константы, когда значение используется в нескольких местах, это просто здравый смысл. Моя точка зрения заключалась в том, что что касается всего в области программирования, вы должны относиться к этому прагматично. Существует огромная разница между password.length() > 7, что вовсе не говорит само за себя, и connection.setTimeout(50), где намерение совершенно ясно. - person AgentCorleone; 22.02.2019
comment
Что касается принципа YAGNI, я поддерживаю его в случае тайм-аута просто потому, что его рефакторинг до константы, если это необходимо, не может быть более быстрым и простым. Я понимаю необходимость долгосрочного мышления, когда речь идет о бизнес-правиле (например, минимальной длине пароля) или границах архитектуры, но здесь это чисто технический параметр. - person AgentCorleone; 22.02.2019
comment
Ух ты, только что наткнулся на это, так что вспомни один из ранних комментариев: HUNDRED_PERCENT легко может быть ценной константой! для процента с точностью 1/100 (0,01% -99,99%) вы можете использовать HUNDRED_PERCENT = 10000. Если бы вы всегда использовали эту константу при преобразовании в десятичную форму и обратно, она бы отлично сработала. - person Bill K; 28.02.2019
comment
Я нашел и магическую константу в Integer.class if (radix == 10) { return toString(i); } - person Alex78191; 18.07.2019
comment
@ Alex78191 Это число определенно волшебное, оно, вероятно, даже более волшебное, чем 7 из примера Марсио. ;) - person Roland Illig; 20.01.2020
comment
Марсио, имя константы MAX_PASSWORD_SIZE не удаляет никакой магии из вашего примера кода. Это просто затрудняет чтение. Из кода было очевидно, что роль 7 - максимальная длина пароля. Это не нужно объяснять снова. Если таковые имеются, 7 должны получить комментарий, как будто это глупо иметь максимальную длину пароля, но это было решено на собрании 2011-04-01 руководством. - person Roland Illig; 20.01.2020
comment
@AgentCorleone connection.setTimeout(50) не совсем ясно на месте звонка. Связанные со временем аргументы стандартной библиотеки в таких языках, как C и PHP обычно указываются в миллисекундах, в C ++ и Assembly обычно в наносекундах или тактовых циклах, а в Python и Ruby < i> обычно указываются в секундах, не говоря уже о нестандартных библиотечных функциях, которые имеют свои собственные соглашения (или Unix, у которых есть эпохи). connection.setTimeout(MILLISECONDS_50), connection.setTimeout(time::duration::ms(50)) и connection.setTimeout(time::time_point("03:50PM")) более ясны / ясны. - person Gabriel Francischini; 15.11.2020
comment
Я не согласен, что это лучше. Я решил написать об этом статью: levelup.gitconnected.com / - person Luís Soares; 17.07.2021

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

Например, предположим, что у вас есть страница, на которой отображаются последние 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 is 50 ???"

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

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

person Michael Stum    schedule 06.09.2008
comment
Даже если это не может измениться, это все равно плохая идея, потому что непонятно, что происходит. - person Loren Pechtel; 02.06.2010
comment
Это не всегда непонятно. SmtpClient.DefaultPort = 25 возможно ясно er, чем SmtpClient.DefaultPort = DEFAULT_SMTP_PORT. - person user253751; 09.11.2014
comment
@immibis Я полагаю, это предполагает, что нет абсолютно никакого другого кода, который использует концепцию DEFAULT_SMTP_PORT. Если порт SMTP по умолчанию для этого приложения будет изменен, его необходимо будет обновить в нескольких местах, что может привести к несогласованности. - person Russ Bradberry; 01.04.2015
comment
Также труднее найти все варианты использования - вам придется искать 25 по всему приложению и следить за тем, чтобы вы изменяли только вхождения 25, которые относятся к порту SMTP, а не 25, которые, например, ширина столбца таблицы или количество записей, отображаемых на странице. - person Michael Stum; 01.04.2015
comment
В этом примере я ожидал, что код будет использовать SmtpClient.DefaultPort, а не 25. Так что вам просто нужно будет изменить его в одном месте. И номер порта, скорее всего, останется прежним, это не случайное магическое число, а номер, присвоенный IANA. - person njsg; 08.06.2015

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

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

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

person somas1    schedule 06.09.2008
comment
Хороший пример RTFW :) - person Eva; 06.03.2013
comment
Я бы сказал, что ответ далеко не полный. - person Skeeve; 12.11.2018

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

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

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

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

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

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

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

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

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

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

Основы

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

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

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

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

«Могу ли я заменить это магическое число на символическую константу?»

is:

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

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

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

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

Тем временем: Назад на ранчо

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

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

Давайте поместим нашу единственную цифру «2» в контекст: padding := 2, где контекстом является «Контейнер графического интерфейса». В этом контексте значение 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 в контексте GUI UX «моей программы», чтобы использовать ее как default_padding, быстро понимаемую в более широком контексте включающего кода. .

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

Идти дальше

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

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

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

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

Вывод

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

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

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

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

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

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

person Larry    schedule 25.10.2015
comment
Спасибо. Мои коллеги продолжают устанавливать инструменты статического анализа, продолжают жаловаться на магические числа - но как инструмент должен понимать семантику? В результате ВСЕ базовые значения заменяются символьными константами. Поскольку я согласен с вашим выводом, я считаю это далеко не идеальным. - person Chomeh; 09.10.2019

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

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

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

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

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

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

person Brian R. Bondy    schedule 06.09.2008
comment
Я не думаю, что это волшебное число, о котором он говорил - person Marcio Aguiar; 07.09.2008
comment
Возможно, вам стоит удалить добавленные вами теги формата файла и сети, потому что он явно не говорит о таких магических числах. - person Landon; 07.09.2008
comment
По-прежнему очень полезно знать, что магические числа могут относиться не только к проблеме кода. -Адам - person Adam Davis; 07.09.2008
comment
Если тема гласила: «Что такое магическое число с точки зрения исходного кода», то тегов там быть не должно. Но он этого не уточнил. Так что иметь мою дополнительную информацию - это хорошо. Я думаю, что Кайл, Лэндон и Марсио ошибаются. - person Brian R. Bondy; 14.09.2008
comment
Также не было возможности определить, кого он искал. Поскольку я был первым постом, я не мог догадаться, какой именно он ищет. - person Brian R. Bondy; 14.09.2008

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

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

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

person Nick Retallack    schedule 06.09.2008
comment
Не могли бы вы рассказать подробнее, почему удаление магических чисел НЕ всегда хорошо? - person Marcio Aguiar; 07.09.2008
comment
В математических формулах типа e ^ pi + 1 = 0 - person Jared Updike; 09.09.2008
comment
Я думаю, он имел в виду, что не всегда хорошо переносить магические числа в файлы конфигурации, с чем я согласен. - person Mike Stone; 18.09.2008
comment
Марсио: Когда вы делаете такие вещи, как const int EIGHT = 8; затем требования меняются, и вы получаете const int EIGHT = 9; - person jmucchiello; 03.03.2009
comment
Извините, но это просто пример неправильного именования или базового использования константы. - person Kzqai; 06.08.2010
comment
@MarcioAguiar: на некоторых платформах выражение типа (foo[i]+foo[i+1]+foo[i+2]+1)/3 может вычисляться намного быстрее, чем цикл. Если бы кто-то заменил 3, не переписывая код как цикл, кто-то, кто видел ITEMS_TO_AVERAGE, определенный как 3, мог бы подумать, что он может изменить его на 5 и получить в среднем больше элементов кода. Напротив, тот, кто посмотрел на выражение с буквальным знаком 3, понял бы, что 3 представляет количество суммируемых элементов. - person supercat; 31.03.2015
comment
Как насчет quadratic_root = (-b + sqrt(b*b - 4*a*c)) / (2*a)? На самом деле любая математическая формула, в которой магические числа не имеют значения, кроме их включения в формулу. - person jodag; 01.05.2018

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

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

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

person Community    schedule 13.12.2008
comment
Когда мы говорим о числах (по крайней мере, для меня), это не выглядит настолько вероятным, но я столкнулся с этим со строками, и это хит: сначала вам нужно прочитать много кода, чтобы увидеть, где он используется, чем вы должен заметить, что его используют для разных целей ... не мое любимое времяпрепровождение. - person Tomislav Nakic-Alfirevic; 24.07.2014

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

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

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

person Sören Kuklau    schedule 07.09.2008

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

person Kyle Cronin    schedule 06.09.2008

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

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

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

person DGentry    schedule 07.09.2008

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

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

person Rob Rolnick    schedule 06.09.2008
comment
На мой взгляд, запах кода магического числа относится именно к необъяснимым константам. Пока вы помещаете их в именованную константу, это не должно быть проблемой. - person Don Kirkby; 17.09.2008

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

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.

person Ascalonian    schedule 12.10.2009
comment
Думаю, ничего страшного, если конструктор инициализирует значение. В противном случае, если значение инициализируется вне конструктора, я просто рассматриваю это как хлопот и как нечто сложное для чтения. - person Thomas Eding; 02.06.2010
comment
Я думаю, что константы static final - это излишество, когда вы используете их в одном методе. Переменная final, объявленная вверху метода, ИМХО более читабельна. - person Eva; 06.03.2013

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

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

person Andrew    schedule 06.09.2008
comment
Код IOW должен быть записан так: factorial n = if n == BASE_CASE then BASE_VALUE else n * factorial (n - RECURSION_INPUT_CHANGE); RECURSION_INPUT_CHANGE = 1; BASE_CASE = 0; BASE_VALUE = 1 - person Thomas Eding; 02.06.2010

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

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

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

int procGetIdCompanyByName(string companyName);

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

int procGetIdCompanyByName(string companyName, bool existsCompany);

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

Другой вариант - сделать две отдельные функции:

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

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

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

Между прочим, суть в следующем: что вы думаете об использовании такого рода «магических чисел», которые относительно известны и безопасны, чтобы сказать, что что-то не удалось или что чего-то не существует?

person Oskytar    schedule 12.08.2013
comment
В этом конкретном случае, если в документации функции указано, что отрицательное возвращаемое значение означает, что компания не найдена, тогда нет причин для использования константы. - person Vincent Fourmond; 18.07.2015

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

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();
         }
    }
}
person jguiraud    schedule 09.06.2015