Хорошая ли идея жестко закодировать значения в наших приложениях?

45

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

Эдвард
источник
2
параметр конфигурации поможет вам
Gopi
54
Вы никогда не знаете, когда значение piможет измениться ...
Гейб
12
Чувак, я думаю, что такие люди, как @gabe, являются причиной, по которой это "правило". Если вы повторите код 3.14 в 20 местах своего кода, а затем обнаружите, что вам действительно нужно больше точности, вы облажались. Я не понимал, что это не было очевидно.
Билл К
17
Это было немного грубо, @Bill. @Gabe явно шутил, но, кроме этого, вопрос был о жестком кодировании и параметрах конфигурации, а не об использовании магических чисел с константой и повторением в нескольких местах.
Дэвид Конрад
1
Да, жесткое кодирование иногда может быть хорошей идеей. См. Статью в Википедии об анти-шаблоне "Softcoding".
user16764

Ответы:

64

Да, но сделай это очевидным .

Делать:

  • использовать константы
  • используйте описательное имя переменной

Не рекомендуется:

Джонатан Ху
источник
44
Что чище diameter = 2 * radiusили diameter = RADIUS_TO_DIAMETER_FACTOR * radius? В действительности, существуют угловые случаи, когда магическое число может быть лучшим решением.
Joonas Pulakka
5
Я не могу согласиться с этим ответом достаточно. Я склонен думать о программировании как о писателе. Вы рассказываете свою историю через код, и если люди не могут понять логику, это делает ваш код бесполезным, на мой взгляд. Вот почему хорошо продуманные соглашения об именах по существу предназначены для удобства чтения. Также нет веских причин использовать магические числа. Используя магические числа, вы удаляете «почему» из уравнения и усложняете его понимание. Например: "диаметр = 2 * радиус" Для чего эти два? Этот "диаметр = RADIUS_TO_DIAMETER_FACTOR * радиус" имеет гораздо больше смысла.
chrisw
9
диаметр = 2 * радиус прямо из математики средней школы. Причина, по которой нельзя назвать «2», состоит в том, что для того, чтобы он имел значение чего-либо еще, потребовалось бы изменить законы физики или математики, или и то, и другое. (С другой стороны, присвоение имени Pi или постоянной Планка является хорошим шагом для простой читабельности).
quick_now
8
@Joonas: Пффф. Конечно, вы имеете в виду diameter = radius << 1? Я полагаю, что это также может быть diameter = radius << RADIUS_TO_DIAMETER_BITS_TO_SHIFT.
Муравей
4
как насчет какойdiameter = radius.toDiameter()
Carson Myers
27

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

Т.Л., д - р: Да, это иногда хорошая идея значений трудно кода, но нет простого правила, чтобы , когда ; это полностью зависит от контекста.

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

Несколько примеров «жестко закодированных» значений:

  • Значения конфигурации

    Я съеживаюсь всякий раз, когда вижу подобные заявления command.Timeout = 600. Почему 600? Кто это решил? Было ли это раньше, и кто-то поднял тайм-аут как хак, вместо того, чтобы исправить основную проблему производительности? Или это действительно известное и документированное ожидание времени обработки?

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

  • Математические формулы

    Формулы обычно имеют тенденцию быть довольно статичными, так что природа постоянных значений внутри не особенно важна. Объем пирамиды составляет (1/3) б * ч. Нас волнует, откуда пришли 1 или 3? На самом деле, нет. Предыдущий комментатор справедливо указал, что diameter = radius * 2, вероятно, лучше, чем ... diameter = radius * RADIUS_TO_DIAMETER_CONVERSION_FACTORно это ложная дихотомия.

    Что вы должны делать для этого типа сценария - это создание функции . Мне не нужно знать, как вы пришли к формуле, но мне все еще нужно знать, для чего она нужна . Если вместо какой-либо ерунды, написанной выше, я пишу, volume = GetVolumeOfPyramid(base, height)то вдруг все становится намного яснее, и вполне нормально иметь магические числа внутри функции ( return base * height / 3), потому что очевидно, что они являются лишь частью формулы.

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

  • Домен / бизнес-правила

    Это всегда серая область, потому что это зависит от того, какое именно значение. В большинстве случаев именно эти магические числа являются кандидатами на превращение в константы, потому что это облегчает понимание программы, не усложняя логику программы. Рассмотрим тест if Age < 19против if Age < LegalDrinkingAge; Вы, вероятно, можете понять, что происходит без константы, но это проще с описательным названием.

    Они также могут стать кандидатами на абстракцию функции, например function isLegalDrinkingAge(age) { return age >= 19 }. Единственное, что ваша бизнес-логика зачастую гораздо более запутанная, и может не иметь смысла начинать писать десятки функций с 20-30 параметрами в каждой. Если нет четкой абстракции, основанной на объектах и ​​/ или функциях, тогда прибегать к константам можно.

    Предостережение в том, что если вы работаете в налоговом департаменте, это становится действительно, очень обременительным и честно писать AttachForm(FORM_CODE_FOR_SINGLE_TAXPAYER_FILING_JOINTLY_FOR_DEPRECIATION_ON_ARMPIT_HAIR). Вы не будете этого делать, вы будете делать это, AttachForm("B-46")потому что каждый разработчик, который когда-либо работал или будет работать там, узнает, что «B-46» - это код формы для одного налогоплательщика, подающего бла-бла-бла - коды форм являются частью самого домена, они никогда не меняются, поэтому на самом деле они не являются магическими числами.

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

  • Коды ошибок и флаги состояния

    Это никогда не подходит для жесткого кода, как Previous action failed due to error code 46может сказать любой бедный ублюдок, который когда-либо был поражен этим . Если ваш язык поддерживает это, вы должны использовать тип перечисления. В противном случае у вас обычно будет целый файл / модуль, полный констант, указывающих допустимые значения для определенного типа ошибки.

    Никогда не позволяй мне увидеть return 42в обработчике ошибок, capiche? Никаких оправданий.

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

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

Aaronaught
источник
Спасибо за хорошую разбивку! - большинство людей не продумывают все опции, которые я бы добавил «Конфигурация среды» - я думаю, что их следует избегать (не жестко запрограммировав), поскольку большинство данных должно быть помещено в файл конфигурации или базу данных. Это следует принципу «держать данные и логику отдельно», которая является основой MVC или MVVM. string TestServerVar = "foo"; string ProdServerVal = "bar";
m1m1k
7

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

  • Если номер может измениться, он должен иметь идентификатор. Гораздо проще найти NUMBER_OF_PLANETS, чем искать каждый экземпляр 9 и подумать, следует ли его изменить на 8. (Обратите внимание, что видимые пользователю строки могут измениться, если когда-либо придется использовать программное обеспечение на другом языке, и это сложно предсказать заранее.)
  • Если номер трудно ввести каким-либо образом. Для констант типа pi лучше дать одно определение с максимальной точностью, чем перепечатывать его в нескольких местах, возможно, неточно.
  • Если номер встречается в разных местах. Вам не нужно смотреть на два использования 45 в смежных функциях и задаться вопросом, означают ли они одно и то же.
  • Если смысл не сразу узнаваем. Можно с уверенностью предположить, что все знают, что такое 3.14159265 ... Не безопасно предполагать, что каждый узнает гравитационную постоянную или даже пи / 2. (Здесь «каждый» зависит от природы программного обеспечения. Можно ожидать, что системные программисты будут знать восьмеричное представление битов разрешений Unix и т. П. В программном обеспечении для морской / морской архитектуры проверка числа Фруда предлагаемого корпуса и скорости до Посмотрите, может ли он 1.1 или выше быть совершенно понятным для всех, кто должен над этим работать.)
  • Если контекст не узнаваем. Все знают, что в часе есть 60 минут, но умножение или деление на 60 может быть неясным, если нет непосредственных указаний на то, что количество является значением времени или значения ставки.

Это дает нам критерии для жесткого кодирования литералов. Они должны быть неизменными, не сложными для ввода, встречаться только в одном месте или в одном контексте и иметь узнаваемое значение. Нет смысла определять 0 как ARRAY_BEGINNING, например, или 1 как ARRAY_INCREMENT.

Дэвид Торнли
источник
5

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

const string server_var="server_var";

но вы должны иметь

const string MySelectQuery="select * from mytable;";

(при условии, что у вас действительно есть запрос, где вы хотите получить все результаты из определенной таблицы, всегда)

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

const int 8th_bit=255; //or some other obscure naming scheme that equates to 255.

вместо этого используйте

const int AllowGlobalRead=255;

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

Earlz
источник
typedef enum {state_0 = 0, state_1 = 1, state_2 = 2, ...} ... Не смейтесь, я видел, как это было сделано. Ударить этого человека по голове мокрой рыбой!
quick_now
@ очень хорошо, конечно, вы хотели бы что-то более похожееtypedef enum {init_state=0, parse_state=1, evaluation_state=2, ... }
Earlz
6
THIS_NAMING_CONVENTION_IS_RECOMMENDED_FOR_CONSTANTS
StuperUser
4
Для строк вам не нужны только константы. Вы хотите поместить любые видимые пользователем строки в какой-либо файл ресурсов (детали будут зависеть от вашей платформы), чтобы вы могли легко перейти на другой язык.
Дэвид Торнли
Возможно, вы также захотите вставить строки, связанные с бизнес-логикой (например, запросы SQL), в файл ресурсов с каким-либо шифрованием или запутыванием. Это не даст "любопытным" пользователям перепроектировать вашу логику (или схему базы данных).
TMN
4

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

Множество вещей жестко закодировано в любых разумных рамках, и они работают. то есть нет технической причины, по которой я не смогу изменить точку входа приложения C # (static void Main), но жесткое кодирование не создает никаких проблем для любого пользователя (кроме случайного вопроса SO )

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

Так что, ИМХО, глупо не жестко кодировать вещи, которые никогда не меняются (пи, гравитационная постоянная, постоянная в математической формуле - объем мысли в сфере).

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

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

  • изменится ли стоимость
  • как изменение стоимости повлияет на систему
SWeko
источник
4

Хорошая ли идея жестко закодировать значения в наших приложениях?

Я жестко кодирую значения только в том случае, если значения указаны в Спецификации (в окончательном выпуске спецификации), например, ответ HTTP OK всегда будет 200(если он не изменяется в RFC), поэтому вы увидите (в некоторых из моих кодов) ) константы типа:

public static final int HTTP_OK = 200;

В противном случае я храню константы в файле свойств.

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

Бухаке синди
источник
3
  • если значение может измениться и действительно может измениться, то, по возможности, программируйте его, пока прилагаемые усилия не превышают ожидаемый доход
  • некоторые значения не могут быть мягко закодированы; следуйте указаниям Джонатана в этих (редких) случаях
Стивен А. Лоу
источник
3

Я заметил, что каждый раз, когда вы можете извлечь данные из своего кода, это улучшает то, что осталось. Вы начинаете замечать новые рефакторинги и улучшать целые разделы вашего кода.

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

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

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

Билл К
источник
2

Недавно я закодировал функцию MySQL, чтобы правильно рассчитать расстояние между двумя парами широта / долгота. Вы не можете просто сделать пифагор; Линии долготы сближаются по мере того, как широта увеличивается к полюсам, так что возникает какой-то волосатый триг. Дело в том, что я был довольно обеспокоен, стоит ли жестко закодировать значение, представляющее радиус Земли в милях.

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

Дэн Рэй
источник
Да, возможно, но как насчет google.com/moon
Residuum
1

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

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

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

Например, в моем проекте ogre я всегда использую класс ConfigFile для загрузки переменной, которую я записал в файл конфигурации.

jokoon
источник
1

Два случая, когда константы в порядке (на мой взгляд, по крайней мере) в порядке:

  1. Константы, которые не имеют отношения ни к чему другому; Вы можете изменить эти константы, когда захотите, без необходимости что-либо менять. Пример. Ширина столбца сетки по умолчанию.

  2. Абсолютно неизменные, точные, очевидные константы, такие как «количество дней в неделю». days = weeks * 7Замена 7на константу DAYS_PER_WEEKвряд ли дает какую-либо ценность.

user281377
источник
0

Я полностью согласен с Джонатаном, но из всех правил есть исключения ...

"Магическое число в спецификации: Магическое число в коде"

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

Я выполнил несколько взаимодействующих контрактов, где необходимо заполнить сообщения значениями, сопоставленными с базой данных. В большинстве случаев сопоставление довольно прямое и укладывается в общие руководящие указания Джонатана, но я встречал случаи, когда целевая структура сообщения была просто ужасна. Более 80% значений, которые должны были передаваться в структуре, были константами, определяемыми спецификацией удаленной системы. это в сочетании с тем фактом, что структура сообщения была гигантской, заставляла заполнять МНОЖЕСТВО таких констант. В большинстве случаев они не предоставили смысла или причины, просто сказали «положите M здесь» или «поместите 4.10.53.10100.889450.4452 здесь». Я также не пытался поместить комментарий рядом со всеми из них, это сделало бы полученный код нечитаемым.

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

Newtopian
источник
0

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

jwenting
источник
1
Возможно, вам потребуется больше точности для гравитационной постоянной Земли, поэтому жесткое ее кодирование несколько раз может привести к проблемам.
user281377
1
Питер Никто? От отшельников Германа?
Дэвид Конрад
На большинстве широт и высот гравитационное ускорение на Земле в значительной степени составляет 9,81 м / с ^ 2 (конечно, если вы ищете нефть под землей или стреляете из МБР над Северным полюсом, знание изменения гравитации очень важно для гораздо больше знаков после запятой), где гравитационное ускорение на других планетах отличается от другого числа, но насколько я знаю, гравитационная постоянная постоянна во Вселенной. Есть много физики, которые должны были бы измениться, если бы g был переменным.
Tangurena
0

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


источник