Чи коли-небудь гарна ідея твердого коду значень у наших додатках? Або завжди правильно називати ці типи значень динамічно, якщо вони потребують змін?
pi
може змінитися ...
Чи коли-небудь гарна ідея твердого коду значень у наших додатках? Або завжди правильно називати ці типи значень динамічно, якщо вони потребують змін?
pi
може змінитися ...
Відповіді:
Так, але зробіть це очевидним .
Зробіть:
Не:
diameter = 2 * radius
чи diameter = RADIUS_TO_DIAMETER_FACTOR * radius
? Дійсно є кутові випадки, коли магічне число може бути кращим рішенням.
diameter = radius << 1
? Я гадаю, що це теж могло бути diameter = radius << RADIUS_TO_DIAMETER_BITS_TO_SHIFT
.
diameter = radius.toDiameter()
Що зараз мені здається дивним у цьому питанні і запитаннях, це те, що ніхто насправді не намагався чітко визначити "жорсткий код" або, що ще важливіше, альтернативи.
Т.Л., д - р: Так, це іноді хороша ідея значень важко коду, але немає простого правила, щоб , коли ; це повністю залежить від контексту.
Питання звужує його до значень , які я вважаю магічними числами , але відповідь на те, чи є вони гарною ідеєю, стосується того, для чого вони насправді використовуються!
Кілька прикладів "жорстко закодованих" значень:
Значення конфігурації
Я плачу, коли бачу подібні твердження command.Timeout = 600
. Чому 600? Хто це вирішив? Це було раніше часу, і хтось підняв тайм-аут як хак, замість того, щоб виправити основні проблеми ефективності? Або це насправді якесь відоме і задокументоване очікування часу на обробку?
Це не повинні бути магічні числа чи константи, їх слід екстерналізувати у файлі конфігурації чи базі даних десь із значущим ім'ям, оскільки їх оптимальне значення значною мірою або повністю визначається середовищем, у якому працює програма.
Математичні формули
Формули, як правило, досить статичні, так що природа постійних значень всередині насправді не особливо важлива. Об'єм піраміди становить (1/3) b * h. Чи нас хвилює, звідки взявся 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
в обробці помилок, капіче? Не може бути ніяких виправдань.
Я, мабуть, залишив кілька сценаріїв, але думаю, що охоплює більшість із них.
Так, так, іноді прийнятна практика жорсткого кодування матеріалів. Тільки не лінуйтеся про це; це має бути свідоме рішення, а не звичайний старий неохайний код.
Існують різні причини присвоєння ідентифікатора номеру.
Це дає нам критерії для жорсткого кодування літералів. Вони повинні бути незмінними, не важкими для набору, що виникають лише в одному місці чи контексті, і мають впізнаваний зміст. Немає сенсу визначати 0 як, наприклад, ARRAY_BEGINNING, або 1 як ARRAY_INCREMENT.
Як доповнення до інших відповідей. Використовуйте константи для рядків, коли це можливо. Звичайно, ви цього не хочете
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;
Звичайно, поряд з константами, знайте, коли використовувати нумератори. Наведений вище випадок, ймовірно, добре впишеться в один.
typedef enum {init_state=0, parse_state=1, evaluation_state=2, ... }
Це залежить від того, що ви вважаєте жорстким кодуванням. Якщо ви намагаєтеся уникати будь-яких речей з жорстким кодом, ви опиняєтесь на території програмного кодування та створюєте систему, якою може керувати лише творець (і це остаточний жорсткий код )
У будь-яких розумних рамках дуже багато речей, і вони працюють. тобто немає жодної технічної причини, чому я не зміг би змінити точку входу додатка C # (статичний недійсний Main), але жорстке кодування, яке не створює жодних проблем жодному користувачеві (крім випадкових запитань SO )
Я використовую правило, що все, що може і зміниться, не впливаючи на стан всієї системи, повинно бути заплутаним.
Отже, ІМХО, нерозумно не твердо кодувати речі, які ніколи не змінюються (пі, гравітаційна константа, константа в математичній формулі - думай об'єм сфери).
Також нерозумно не твердо кодувати речі чи процеси, які матимуть вплив на вашу систему, що потребуватиме програмування в будь-якому випадку, тобто марно дозволяти користувачеві додавати динамічні поля до форми, якщо будь-яке додане поле вимагатиме від розробника технічного обслуговування увійдіть і напишіть якийсь сценарій, який змусить цю справу працювати. Крім того, нерозумно (і я це бачив кілька разів у корпоративному середовищі) створювати якийсь інструмент конфігурації, тому нічого не важко закодовано, але тільки ним можуть користуватися лише розробники в ІТ-відділі, і користуватися ним лише трохи легше, ніж зробити це у Visual Studio.
Отже, підсумок, чи має бути рівнокодована річ, є функцією двох змінних:
Чи коли-небудь гарна ідея твердого коду значень у наших додатках?
Значення жорсткого коду, тільки якщо значення вказані в Специфікації (під час остаточного випуску специфікації), наприклад, відповідь HTTP OK завжди буде 200
(якщо вона не зміниться в RFC), так що ви побачите (у деяких моїх кодах ) константи типу:
public static final int HTTP_OK = 200;
В іншому випадку я зберігаю константи у файлі властивостей.
Причина, чому я вказав специфікації, полягає в тому, що зміна констант в специфікаціях вимагає управління змінами, в якому зацікавлені сторони переглянуть зміни та затвердять / не схвалюють. Це ніколи не відбувається за ніч, і на затвердження потрібні місяці / роки. Не забувайте, що багато розробників використовують технічні характеристики (наприклад, HTTP), тому змінити це означає зламати мільйони систем.
Я помітив, що щоразу, коли ви можете отримати дані зі свого коду, це покращує те, що залишилося. Ви починаєте помічати нові рефактори та вдосконалювати цілі розділи коду.
Це просто гарна ідея працювати над вилученням констант, не вважайте це дурним правилом, подумайте про це як про можливість кодування краще.
Найбільшою перевагою було б те, як ви можете знайти подібні константи, є єдиною різницею в групах коду - абстрагування їх у масивах допомогло мені зменшити деякі файли на 90% від їх розміру та виправити досить багато помилок копіювання та вставки. .
Я ще не бачив єдиної переваги в тому, щоб не витягувати дані.
Нещодавно я зашифрував функцію MySQL, щоб правильно обчислити відстань між двома парами lat / long. Ви не можете просто робити піфагор; Лінії довготи зближуються, коли широта збільшується до полюсів, отже, тут задіяний якийсь волохатий триггер. Справа в тому, що я був дуже розчулений, чи варто жорстко кодувати значення, що представляє радіус Землі в милях.
Я в кінцевому підсумку це робив, хоча факт: lat / lng лінії набагато ближче один до одного, скажімо, на Місяці. І моя функція різко занижує відстані між точками на Юпітері. Я подумав, що шанси на веб-сайт, який я будую, має місце позаземного розташування, щоб увійти, досить малий.
Ну, це залежить, якщо ваша мова складена. Якщо це не складено, це не велика справа, ви просто відредагуйте вихідний код, навіть якщо він буде делікатним для непрограміста.
Якщо ви програмуєте на компільованій мові, це, очевидно, не є гарною ідеєю, оскільки якщо змінні змінюються, вам доведеться перекомпілювати, що є великою витратою часу, якщо ви хочете скорегувати цю змінну.
Вам не потрібно робити якийсь слайдер або інтерфейс, щоб динамічно змінювати його змінну, але найменше, що ви могли зробити, це текстовий файл.
Наприклад, з моїм проектом ogre, я завжди використовую клас ConfigFile для завантаження змінної, яку я записав у конфігураційний файл.
Два випадки, коли константи (на мій погляд, принаймні) ОК:
Константи, що стосуються нічого іншого; ви можете змінювати ці константи коли завгодно, не змінюючи нічого іншого. Приклад: Ширина стовпця сітки за замовчуванням.
Абсолютно незмінні, точні, очевидні константи, типу "кількість днів на тиждень". days = weeks * 7
Заміна 7
на постійну DAYS_PER_WEEK
навряд чи дає якесь значення.
Я повністю погоджуюся з Джонатаном, але, як всі правила, є винятки ...
"Магічне число в специфікації: Магічне число в коді"
В основному зазначається, що будь-які магічні числа, які залишаються в специфікації після розумних спроб отримати для них описовий контекст, повинні бути відображені як такі в коді. Якщо магічні числа залишаються в коді, слід докласти всіх зусиль, щоб виділити їх і чітко пов'язати їх з точкою походження.
Я виконав декілька договорів взаємодії, де потрібно заповнити повідомлення значеннями, відображеними з бази даних. У більшості випадків картографування є досить прямим уперед та підходитиме до загальних керівних ліній Джонатана, але я стикався з випадками, коли цільова структура повідомлення була просто жахливою. Понад 80% значень, які мали бути передані в структурі, були константами, що застосовуються специфікацією віддаленої системи. це поєднується з тим, що структура повідомлення була загальноприйнятою, що багато таких констант довелося заповнювати. У більшості випадків вони не надавали сенсу чи причини, просто сказали "покласти М сюди" або "покласти сюди 4.10.53.10100.889450.4452". Я не намагався поставити коментар поруч із усіма ними, це зробило б отриманий код нечитабельним.
Це було сказано, коли ви думаєте про це ... це майже все, щоб зробити це очевидним ...
Якщо ви жорстко кодуєте значення гравітаційної константи Землі, ніхто не піде на це. Якщо ви жорстко кодуєте IP-адресу вашого проксі-сервера, у вас виникнуть проблеми.
Переважно ні, але я вважаю, що варто зазначити, що у вас виникне найбільше проблем, коли ви почнете дублювати жорстко закодоване значення. Якщо ви не дублюєте його (наприклад, використовуєте його лише один раз при впровадженні класу), то не використовувати константу може бути добре.