Це використання символічного постійного перевиконання?


34

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

for (int i = 0; i < 8; i++){
    for (int j = 0; j < 8; j++){

при цьому він наполягав на тому, щоб це було натомість

for (int i = 0; i < CHESS_CONST; i++){
    for (int j = 0; j < CHESS_CONST; j++){

з якоюсь кращою назвою символів, про яку я зараз не можу подумати.

Зараз, звичайно, я знаю, як правило, уникаю використання магічних чисел, але я відчуваю, що з тих пір

  1. це число ніколи не зміниться;
  2. ім'я не може бути таким описовим в будь-якому випадку, оскільки число використовується в стільки місцях у коді; і
  3. кожен, хто переходить вихідний код для шахової програми, повинен знати достатньо про шахи, щоб знати, що таке 8,

насправді немає потреби в символічній константі.

То що ви думаєте, хлопці? Це надмірність, чи я повинен просто поговоритись із символом?


46
CHESS_CONST набагато гірше, ніж просто використання числа 8, але константа з описовою назвою буде вдосконаленням. Ви кажете, що хтось повинен знати, що означає 8 у коді, але це неправда. Ціле буквене позначення без контексту може означати будь-яку кількість речей, наприклад, кількість ходів, кількість штук на дошці тощо. Описова назва константи зробила б намір зрозумілим, а значить, і кодом простіше зрозуміти.
ЖакБ

43
Особисто те, що мені здається набагато більшим, ніж магічне число, - це назви iта jзмінні циклу. Я не можу за все життя зрозуміти, який з них повинен представляти ранг, а який повинен представляти файл. Ранги варіюються від 1..8, а файли - від a..h, але у вашому випадку обидва iта jколиваються від 0..7, так що мені не допомагає зрозуміти, що є. Чи є якийсь - то міжнародний дефіцит листи кризи я не знаю про це , або що трапилося з перейменуванням їх rankі file?
Йорг W Міттаг

4
Грайте на захисника чорта на секунду; Уявіть, що читаєте чийсь шаховий код, де вони використовували магічне число 8. Чи можете ви припустити, що знаєте, для чого він використовується? Як ви можете бути на 100% впевнені? Чи є якась можливість це може означати щось інше? Чи не було б просто трохи приємніше, якби вам навіть не довелося робити припущення? Скільки часу ви можете витратити на відстеження коду, щоб зрозуміти, чи правильно ваше припущення? Чи витрачаєте ви менше часу, якби код був більш самодокументованим, використовуючи замість нього значущу, проникливу назву?
Бен Коттрелл

16
@JacquesB: Дійсно. Там 8 рангів, 8 файлів, 8 пішаків. Деякі шахові двигуни використовують точкову систему, де певні шматки, певні поля та певні ходи мають до них прикріплені точки, і двигун вибирає хід з найвищим балом. 8 може бути така оцінка. 8 може бути глибиною пошуку за замовчуванням. Це може бути майже все, що завгодно.
Йорг W Міттаг

9
Я б подумав про використання циклу foreach, наприклад foreach(var rank in Ranks). Я б також розглядав можливість об'єднання обох циклів в одну, де кожен елемент є (рангом, файлом) кортежем.
CodesInChaos

Відповіді:


101

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

Навіть коли число ніколи не змінюватиметься протягом життя програми, у вашій програмі можуть бути інші місця, де число 8 відбуватиметься з іншим значенням. Заміна "8" BOARD_WIDTHтуди, де має на увазі ширина дошки, та використання іншого символічного імені, коли мається на увазі інша річ, робить ці різні значення явними, очевидними, а вашу загальну програму - більш зрозумілою та доступною. Це дозволяє вам також здійснити глобальний пошук по вашій програмі (або зворотний пошук символів, якщо це надає ваше середовище), якщо вам потрібно швидко визначити всі місця в коді, які залежать від ширини плати.

Дивіться також цей колишній пост SE.SE для обговорення того, як (або як не робити) вибрати імена для номерів.

Як бічне зауваження, оскільки це обговорювалося тут у коментарях : якщо у вашому реальному коді програми має значення, чи змінна iстосується рядків та jстовпців дошки, чи навпаки, то рекомендується вибрати імена змінних, які зробити розрізнення чітким, як rowі col. Перевага таких імен полягає в тому, що вони роблять неправильний код невірним.


22
Я хотів би додати, якщо ОП написав справді чудовий шаховий двигун, то хотів би розширити його, щоб включити 3D-шахи або інші варіанти, то які 8-ти потрібно змінити, це стане проблемою.
Стів Барнс

32
@SteveBarnes: Я намагаюся уникати подібних аргументів, оскільки це надто легко призводить до поверненого аргументу на кшталт "Я думаю, що це навряд чи коли-небудь робити цю програму іншим видом гри, тому я можу залишити магічне число 8 там, де вона є. "
Док Браун

4
@IllusiveBrian Класичний аргумент "солом'яної людини" з двох причин: (1) просто визначення константи не означає, що програміст все ще не може набрати "8" (будь то випадково, дизайн чи злість!) Та (2) тільки тому, що є константа BOARD_WIDTH = 8, це не робить BOARD_WIDTH правильною постійною для використання в певному місці програми.
alephzero

3
@Blrfl ... або призведе до переобладнання коду. Відповідальність розробника полягає в тому, щоб усвідомити, що може змінитися в майбутньому і відповідно кодувати. Кодування, як вимоги, не зміниться, викликає проблеми, але інша крайність - не краща.
Малькольм

4
@corsiKa Коли змінні циклу відповідають фізичним осям, їх слід назвати x, y або рядок, col або, для шахів, файл, ранг.
user949300

11

Гаразд, ось декілька коментарів:

Позбутися від магічних чисел - чудова ідея. Існує концепція, відома під назвою DRY, яка часто неправильно представлена, але ідея полягає в тому, що ви не дублюєте знання концепцій у своєму проекті. Отже, якщо у вас є клас під назвою ChessBoard, ви можете зберегти константу під назвою BOARD_SIZE або ChessBoard.SIZE, прикріплену до нього. Таким чином, є одне єдине джерело цієї інформації. Також це допомагає читати згодом:

for (int i = 0; i < ChessBoard.SIZE; i++){
  for (int j = 0; j < ChessBoard.SIZE; j++){

Навіть якщо кількість ніколи не змінюється, ваша програма, напевно, краща. Будь-яка людина, яка її читає, знає більше інформації про те, що робить код.

Погане ім’я гірше, ніж ім'я, але це не означає, що щось не слід називати. Просто змініть ім’я. Не кидайте дитину водою з ванни. : p Назва може бути описовою, якщо ви добре розумієте, що вона описує. Потім ця концепція може бути використана для безлічі різних речей.


8
це, здається, лише повторює питання, вже зроблені та пояснені у верхній відповіді
gnat

-7

Те, що ви насправді хочете, - це усунути посилання на константи ad nauseum , незалежно від того, чи вони названі чи голими:

for_each_chess_square (row, col) {
  /*...*/
}

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

8є самоописуванням; це не макрос, який означає щось інше.

Ви Ain't Never Gonna (TM) перетворите це на шахову програму 9х9, і якщо ви коли-небудь це зробите, поширення 8 не складе великих труднощів.

Ми можемо шукати 150 000 баз лінійного коду для маркера 8 і класифікувати, які події означають, що за секунди.

Набагато важливіше - модулювати код, щоб шахові знання були сконцентровані в якомога менше місцях. Краще мати один, два, можливо три шахових модулі, в яких відбувається буквальне 8, ніж тридцять сім модулів, покритих певною шаховою відповідальністю, посилаючись на 8 через символічну назву.

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

Припустимо, що в майбутньому ви хочете підтримати альтернативні розміри плати. У цьому випадку ці петлі повинні будуть змінити, чи вони використовують іменну константу, або 8, оскільки розміри будуть отримані за допомогою якогось виразу, як board.widthі board.height. Якщо у вас BOARD_SIZEзамість цього 8, ці місця буде легше знайти. Так що менше зусиль. Тим НЕ менше, ви не повинні забувати про зусиллі заміни 8з BOARD_SIZEв першу чергу. Загальні зусилля не нижче. Створення одного проходу по коду для зміни 8до BOARD_SIZE, а потім інший , щоб підтримувати альтернативні розміри, не дешевше , ніж просто перехід від 8альтернативної підтримки виміру.

Ми також можемо переконатися в цьому з чисто холодного об'єктивного аналізу ризику / вигоди. У програмі зараз голі константи. Якщо їх замінити постійними, користі немає; програма ідентична. При будь-яких змінах виникає ненульовий ризик. У цьому випадку вона невелика. Проте жоден ризик не повинен сприйматись без користі. Щоб "продати" зміну перед цією міркуванням, ми маємо гіпотезувати перевагу: майбутню вигоду, яка допоможе з іншою програмою: майбутня версія програми, яка зараз не існує. Якщо така програма планується, ця гіпотеза та пов'язане з цим міркування є добросовісними і слід сприймати їх серйозно.

Наприклад, якщо вам не вистачає днів, коли ви додасте більше коду, який ще більше поширить ці константи, можливо, ви захочете їх усунути. Якщо ці екземпляри констант - це приблизно всі екземпляри, які коли-небудь існуватимуть, то навіщо турбуватись.

Якщо ви коли-небудь працюєте над комерційним програмним забезпеченням, застосовуватимуться також аргументи ROI. Якщо програма не продається, а зміна деяких жорстко зафіксованих цифр на константи не покращить продажі, ви не будете компенсовані зусиллями. Зміна має нульову віддачу від вкладення часу. Аргументи рентабельності інвестицій узагальнюють понад гроші. Ви написали програму, вклавши час і зусилля, і щось із неї вийшло: ось ваше повернення, ваше "R". Якщо внести цю зміну поодинці, ви отримаєте більше того "R", яким би воно не було, то будь-якими способами. Якщо у вас є якийсь план для подальшого розвитку, і ця зміна покращує ваше "R", детто. Якщо зміна не має для вас негайного або передбачуваного "R", забудьте про це.


13
8НЕ таке самоописування, це число, яке може означати що завгодно. І, можливо, вони хочуть зробити шахову гру 9х9, чому б ні? Якщо у вас є 150 000 рядків коду, ви не зможете запам'ятати, що 8означає кожен з коду, і навіть якщо ви могли, чому б це зробити? Це просто багато зайвих клопотів. Що стосується модуляризації, заміни 8на щось подібне NUM_RANKSне шкодить модульності, насправді це допомагає, оскільки полегшує повороти коду.
Jerfov2

4
@Kaz Я теж це роблю. І тоді я ввожу значні дивні помилки, бо жменька звичаїв - це не те, що я вважав, що вони є, тому що важко приділяти стільки уваги великій кількості замін, а також бути правильним кожен раз. З цієї причини я уникаю в першу чергу цього сценарію, тому не можу підтримати поради, які посилаються на нього.
doppelgreener

1
«Тим НЕ менше, ви не повинні забувати про зусиллі заміни 8з BOARD_SIZEв першу чергу» - час , яке ви витратили розглядав над кодом , намагаючись зрозуміти , що кожен 8робить у вашій програмі місяців тепер, швидше за все , більше часу , що витрачається на зміну 8з змістовна константа рівня модуля.
Крістіан Дін

1
@doppelganger Цей сценарій вже є. Я не кажу, візьміть шахову програму, яка використовує константи, і замініть їх на 8. Я також не кажу: "Не плануйте використовувати константи в ще не написаній шаховій програмі"
Каз

1
@Kaz Чому ви хочете змусити себе прочитати весь код навколо 8-ї, і, можливо, іноді помилитися з контекстом, коли ви могли просто витратити зайві 3 секунди, надаючи значуще ім’я? Що стосується пізнання фактичного значення константи, то набагато простіше шукати точне значення для змінної, ніж намагатись
відмовити
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.