Використання магічних рядків / номерів [закрито]


31

Це дещо суперечлива тема, і, мабуть, існує стільки думок, скільки є програмістів. Але заради цього я хочу знати, які звичні практики в бізнесі (або на ваших робочих місцях).

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

Не використовуйте в коді буквальні значення чисельних чи рядкових, крім визначення символічних констант. Використовуйте такий шаблон для визначення констант:

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

Є винятки: значення 0, 1 і null майже завжди можна використовувати безпечно. Дуже часто значення 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 поверх свого файлу javascript, якщо цей ідентифікатор використовується лише на 1 місці?

Я читав наступні теми:
StackExchange
StackOverflow
Bytes IT Community
Є ще багато статей, і після прочитання ці деякі моделі з'являються.

Тож моє питання полягає в тому, чи слід використовувати в нашому коді магічні рядки та цифри? Я спеціально шукаю відповіді експертів, які, якщо це можливо, підкріплені посиланнями.


2
Чарівна змінна - це змінна, яка має значення, яке не відображається її змістом. Ціле значення "10" відображає значення числа 10, тому не потрібно робити це постійним. Те саме стосується простору та крапки з комою. З іншого боку, якщо у вас є значення "%% ?? %%", і це якийсь спеціальний роздільник, МОЖЕ бути розміщений як константа, оскільки його вміст не відображає факту, що це роздільник.
Jeroen Vannevel

23
NumberTen = 10Це безглуздо, оскільки число 10 не буде переосмислено. MaxRetryCount = 10У цьому є пункт a, можливо, ми захочемо змінити максимальну кількість спроб. 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)", що ви робите?

За схемою значення-ім'я, яку віддає перевагу вашому колезі, вам доведеться змінити значення літералу ( SemiColon = '|') і жити з кодом, який продовжує використовувати SemiColonдля чогось, що вже не є крапкою з комою. Це призведе до негативних коментарів в оглядах коду . Стихати , що ви могли б змінити ім'я литерала PipeSymbolі пройти і змінити кожне входження SemiColonв PipeSymbol. З такою швидкістю ви, можливо, також просто використали буквально крапку з комою ( ';'), оскільки вам доведеться оцінювати кожне його використання окремо, і ви будете робити однакову кількість змін.

Ідентифікатори для констант повинні бути описовими , що значення робить , а не те , що значення є , і де ваш колега зробив лівий поворот в бур'яни. У описаному вище застосуванні для розбиття на місця призначення крапкою з комою є роздільник поля, і константи повинні бути названі відповідно:

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

Таким чином, коли змінюється роздільник поля, ви змінюєте рівно один рядок коду, декларацію постійної. Хтось, дивлячись на зміну, побачить саме цей один рядок і відразу зрозуміє, що роздільник поля змінився з крапки з комою на символ труби. Залишок коду, який не потрібно було змінювати, оскільки він використовував константу, залишається тим самим, і читачеві не доведеться копати його, щоб побачити, що ще було зроблено з ним.


Я цілком погоджуюся. Кілька десятиліть тому я працював над проектом, де повідомлення надсилалися по сегментах, кожен з яких використовував 8 загальних регістрів. Хтось декларував #define one 1 #define two 2 тощо (або все, що було б еквівалентно, у Коралі Поштового відділення Великобританії, мові вибору тоді). Слово з'явилося високо, що в подальшому полем довжини буде кількість байтів, а не сегментів, тому очевидно, що код був змінений на #define one 8 #define two 16 etc
Mawg

3
Як нерозумно , як імена , як коми або PipeSymbol , здається, що змінюють один до одного з допомогою сценарію буде набагато простіше , ніж змінюється кожен впливає ;на |.
Брандін

Що з випадком, коли заданий лінійний рядок використовується у файлі багато разів, але він не має іншого значення, крім його значення? Наприклад, якщо ви тестуєте, що можете отримати певний ключ на карті в 20 різницевих сценаріях, я повинен визначити константу, як це ?: public static final String MY_KEY_NAME = "MyKeyName"
Jordan McQueen

1
@JordanMcQueen Існує випадок використання голих літералів, якщо (і лише якщо) кожен використовується в точності один раз і більше ніде не потрібен. Якщо це що - щось на зразок кожного сценарію коду, який обробляє інший формат файлу, кожен формат має визначити свою власну константу (наприклад, CSV_RECORD_SEPARATOR, TSV_RECORD_SEPARATORі т.д.).
Blrfl

8

Визначення крапки з комою як константи є зайвим, оскільки крапка з комою вже сама по собі є постійною . Це ніколи не зміниться.

Це не так, як одного дня хтось оголосить "зміну термінології, + це новий крапку з комою зараз", і ваш колега із задоволенням кинеться просто оновити константу (вони сміялися з мене - подивіться на них зараз).

Є також питання послідовності. Я гарантую, що його NumberTenконстанта НЕ буде використана всіма (більшість кодерів не з розуму), тому вона не буде служити тій цілі, яку вона очікувала в будь-якому випадку. Коли апокаліпсис настає, і "десять" буде глобально перенесено на 9, оновлення константи НЕ зробить трюк, тому що це все одно залишить вас з купою буквальних 10s у вашому коді, тому тепер система стає абсолютно непередбачуваною навіть у межах сфери революційного припущення, що "десять" означає "9".

Збереження всіх налаштувань як consts - це те, про що я теж мав би другу думку. Не слід цього робити легковажно.

Які приклади такого типу використання ми вже зібрали? Лінійний термінатор ... Максимальна кількість спроб ... Максимальна кількість коліс ... Ми впевнені, що це ніколи не зміниться?

Вартість полягає в тому, що зміна налаштувань за замовчуванням потребує перекомпіляції програми, а в деяких випадках навіть її залежностей (оскільки числові значення const можуть бути жорстко кодовані під час компіляції).

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


4
"Це ніколи не зміниться". Я думав, що про апостроф (назавжди прив’язаний до значення ASCII 39). Деякі старі програми використовували для згортання апострофа. Але тепер сучасні додатки трактують це значення ASCII як прямий апостроф, сумісний зі старими програмами, і натомість люди часто використовують (ліва одинарна котировка, Unicode 8217 ) для додатків, сумісних із відображенням різного гліфа для згорнутої позначки. Оскільки Європа використовує коми так, як американці використовують періоди в якості десяткової крапки, я вважаю, що я трохи вагаюсь заявляти "ні ... ніколи".
TOOGAM

@TOOGAM добре, ваш приклад виправдовує наявність DecimalPointконстанти - але немає Commaабо Periodконстанти. Це зовсім різниця: колишній позначає функцію , роль або мету значення. «Точка з комою» або «Кома» не підпадають під цю категорію.
Конрад Моравський

Це справедливо для прикладу Десяткової точки. Однак приклад апострофа, схоже, є подібною (або однаковою) категорією як кома (або крапка з двокрапкою).
TOOGAM

@KonradMorawski Semicolon може використовуватися для багатьох цілей, таких як розділення рядка або закінчення рядка. Саме його значення (не значення) слід використовувати для називання констанції. Розглянемо майбутні зміни, тобто завтра ми дозволяємо обробляти 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 Можливо, я повинен був би детально розробитись; керівні принципи кодування, які я бачив, більше в тренді загального правила про те, як хтось десь подумав, що програмне забезпечення має бути написане, а не такі угоди, як ти і Конрад, напевно, думають :-)
Стефан Біллієт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.