Использование магических строк / чисел [закрыто]

31

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

На моем рабочем месте у нас есть строгие правила кодирования. Один из разделов посвящен магическим строкам / числам. В нем говорится (для C #):

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

public class Whatever  
{  
   public static readonly Color PapayaWhip = new Color(0xFFEFD5);  
   public const int MaxNumberOfWheels = 18;  
}

Есть исключения: значения 0, 1 и нуль почти всегда можно использовать безопасно. Очень часто значения 2 и -1 тоже в порядке. Строки, предназначенные для ведения журнала или трассировки, освобождаются от этого правила. Литералы допускаются, когда их значение ясно из контекста и не подлежит будущим изменениям.

mean = (a + b) / 2; // okay  
WaitMilliseconds(waitTimeInSeconds * 1000); // clear enough

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

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

Я хотел бы сделать это, чтобы иметь некоторые научно обоснованные аргументы при споре с одним из моих коллег, который собирается объявить такие константы, как:

private const char SemiColon = ';';
private const char Space = ' ';
private const int NumberTen = 10;

Другой пример будет (и этот в JavaScript):

var someNumericDisplay = new NumericDisplay("#Div_ID_Here");

Вы помещаете DOM ID поверх вашего файла JavaScript, если этот идентификатор используется только в 1 месте?

Я прочитал следующие темы:
StackExchange ИТ-сообщество
StackOverflow
Bytes
Есть еще много статей, и после прочтения появляются некоторые шаблоны.

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

Даниэль Грушчик
источник
2
Магическая переменная - это переменная, которая имеет значение, которое не отражено в ее содержимом. Целочисленное значение «10» отражает значение числа 10, поэтому нет необходимости делать его постоянным. То же самое касается пробела и точки с запятой. С другой стороны, если у вас есть значение «%% ?? %%», и это какой-то пользовательский разделитель, то он ДОЛЖЕН быть помещен как константа, поскольку его содержимое не отражает тот факт, что он является разделителем.
Йерун Ванневель
23
NumberTen = 10Это бессмысленно, так как число 10 не будет переопределено. MaxRetryCount = 10Это имеет точку а, мы можем захотеть изменить максимальное количество повторов. private const char SemiColon = ';'; Тупой. private const char LineTerminator = ';'; Умная.
Майк
1
Актуальный вопрос не ясен.
Тулаинс Кордова

Ответы:

89

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

private const char SemiColon = ';';
private const char Space = ' ';
private const int NumberTen = 10;

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

Допустим, задача вашего кода - анализ потока записей, которые содержат поля, разделенные точками с запятой ( a;b;c) и сами разделенные пробелами ( a;b;c d;e;f). Если тот, кто написал вашу спецификацию, позвонит вам через месяц и скажет: «мы ошиблись, поля в записях разделены символами канала ( a|b|c d|e|f)», что вы делаете?

Согласно схеме «значение как имя», которую предпочитает ваш коллега, вам придется изменить значение literal ( SemiColon = '|') и жить с кодом, который продолжает использовать SemiColonдля чего-то, что больше не является точкой с запятой. Это приведет к негативным комментариям в обзорах кода . Чтобы уменьшить это, вы можете изменить имя литерала на PipeSymbolи пройти и изменить каждое вхождение SemiColonна PipeSymbol. С такой скоростью вы могли бы просто использовать буквальную точку с запятой ( ';') в первую очередь, потому что вам придется оценивать каждое его использование по отдельности, и вы будете вносить такое же количество изменений.

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

private const char FieldSeparator = ';';    // Will become '|' a month from now
private const char RecordSeparator = ' ';
private const int MaxFieldsPerRecord = 10;

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

Blrfl
источник
Я абсолютно согласен. Несколько десятилетий назад я работал над проектом, в котором сообщения отправлялись по сегментам, каждый из которых использовал 8 общих регистров. Кто-то объявил и #define one 1 #define two 2 т. Д. (Или что-то подобное было в почтовом отделении «Корал», тогдашнем языке выбора) #define one 8 #define two 16
Пришло слухи,
3
Как глупо , как имена , как запятой или PipeSymbol , кажется, сменяющих друг к другу с помощью сценария будет намного проще , чем меняется каждый влияет ;на |.
Брандин
Как насчет случая, когда данный строковый литерал используется в файле много раз, но он не имеет никакого значения, кроме своего значения? Например, если вы тестируете, что можете получить определенный ключ на карте в 20 разностных сценариях, я должен определить константу следующим образом ?: public static final String MY_KEY_NAME = "MyKeyName"
Джордан Маккуин
1
@JordanMcQueen Необходимо использовать голые литералы, если (и только если) каждый используется ровно один раз и больше нигде не нужен. Если это что - то вроде каждого сценария кода, который обрабатывает другой формат файла, каждый формат должен определить свою собственную константу (например, CSV_RECORD_SEPARATOR, TSV_RECORD_SEPARATORи т.д.).
Blrfl
8

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

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

Там также вопрос последовательности. Я гарантирую, что его NumberTenконстанта НЕ будет использоваться всеми (большинство программистов не в своем уме), поэтому она не будет служить какой-либо цели, которую она ожидала в любом случае. Когда наступит апокалипсис, и «десятка» будет глобально изменена до 9, обновление константы НЕ даст результата, потому что она все равно оставит вас с кучей литералов 10в вашем коде, так что теперь система становится совершенно непредсказуемой даже внутри области видимости. революционного предположения, что «десять» означает «9».

Сохранение всех настроек как conts - это то, о чем я тоже подумаю. Не следует делать это легко.

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

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

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

Конрад Моравский
источник
4
«Это никогда не изменится». Раньше я думал об апострофе (навсегда привязанном к значению ASCII 39). Некоторые старые приложения раньше скручивали апостроф. Но теперь современные приложения воспринимают это значение ASCII как прямой апостроф, совместимый со старыми приложениями, и вместо этого люди часто используют (оставленную одинарную кавычку, Unicode 8217 ) для приложений, совместимых с отображением другого символа на скрученном знаке. Поскольку Европа использует запятые так же, как американцы используют точки в качестве десятичной точки, я немного сомневаюсь в том, чтобы объявить «не ... никогда».
TOOGAM
@TOOGAM хорошо, ваш пример обосновывает , имеющий DecimalPointпостоянный - но не Commaили Periodпостоянные. Это большая разница: первая обозначает функцию , роль или цель значения. «Точка с запятой» или «запятая» не подпадают под эту категорию.
Конрад Моравский
Это верно для примера с десятичной точкой. Тем не менее, пример апострофа кажется похожей (или идентичной) категорией как запятая (или точка с запятой).
TOOGAM
@KonradMorawski Точка с запятой может использоваться для многих целей, таких как разбиение строки или окончание строки. Именно это значение (а не значение) должно использоваться для именования константы. Подумайте о будущих изменениях, то есть завтра мы разрешим обрабатывать 20 записей, поэтому константа с именем NumberTen находится вне контекста, тогда как maxRecord все равно будет в порядке.
MaxZoom
5
private const char SemiColon = ';';
private const char Space = ' ';
private const int NumberTen = 10;

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

private const char StatementTerminator = ';';
private const char Delimiter = ' ';
private const int  BalanceInquiryCode = 10;

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

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

Джон Боде
источник
3

Там не должно быть ничего спорного об этом. Суть не в том, использовать магические числа или нет, а в том, чтобы иметь читаемый код.
Рассмотрим разницу между: if(request.StatusCode == 1)и if(request.HasSucceeded). В этом случае, я бы сказал, что последний гораздо удобнее для чтения, но это не значит, что вы никогда не сможете получить подобный код int MaxNumberOfWheels = 18.

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

Стефан Биллиет
источник
13
Водители должны быть достаточно зрелыми, чтобы суметь судить, по какой стороне дороги они едут;)
Конрад Моравский
2
Результат оценочного вызова может варьироваться даже среди зрелых разработчиков, поэтому даже произвольные рекомендации по кодированию предназначены для улучшения читабельности за счет согласованности. Это не связано с тем, что создание константы NumberTen не имеет смысла.
Майк Партридж
1
Я бы не стал настаивать на том, что они должны быть формальными, проставлены штампы и т. Д., Они могут быть неформальными, но с ними следует договориться, и это уже выходит за рамки простого использования индивидуальной зрелости суждения. Но вы удалили свой комментарий сейчас Стефан :)
Конрад Моравский
1
@StefanBilliet - совсем нет. Моя точка зрения заключается в том, что читаемость улучшается за счет согласованности. Проблема здесь не в самой директиве по кодированию, а в экстремальных недоразумениях.
Майк Партридж
@MikePartridge Может быть, я должен был разработать; руководящие принципы кодирования, которые я видел, больше относятся к общей книге правил о том, как кто-то где-то считал, что программное обеспечение должно быть написано, а не о соглашениях, как вы и Конрад, вероятно, думают :-)
Стефан Биллиет