Розділити обчислення вартості повернення та виписку про повернення однорядковими методами?


26

У мене з колегою було обговорено питання про порушення returnтвердження та твердження, яке обчислює повернене значення у два рядки.

Наприклад

private string GetFormattedValue()
{
    var formattedString = format != null ? string.Format(format, value) : value.ToString();
    return formattedString;
}

замість

private string GetFormattedValue()
{
    return format != null ? string.Format(format, value) : value.ToString();
}

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

Моє запитання полягає в тому, якщо все-таки є дійсним питання писати код менш зрозумілим, просто щоб полегшити налагодження? Чи є якісь додаткові аргументи для варіанта з розділеним розрахунком та returnвисловленням?


18
Не працює над VS, але припустимо, що ви не можете встановити умовну точку розриву для складного виразу (або було б складно ввести), тому, ймовірно, ставте призначити та повернутись в окремі заяви, просто для зручності. Компілятор, ймовірно, придумав би той самий код для обох.
tofro

1
Це, можливо, залежить від мови, особливо в мовах, де змінні мають (можливо складну) поведінку об'єкта замість поведінки вказівника. Заява @Paul K, ймовірно, стосується мов з поведінкою вказівника, мов, де об’єкти мають просту ціннісну поведінку, та мов із зрілими, якісними компіляторами.
MSalters

4
"оскільки VisualStudio дозволяє нам дуже детально перевірити висловлювання, коли виконання зупинено через перерву" - це так. Отже, як отримати повернене значення, якщо функція повернула структуру з більш ніж одним членом? (а підтримка цієї функції в кращому випадку плямиста. Є багато комбінацій, коли ви взагалі не отримуєте значення повернення).
Во

2
Яким чином "дуже детальний огляд тверджень" у налагоджувальнику, який хтось сьогодні використовує, робить його поганим варіантом написання коду, щоб легко налагодити його в будь-якому відладчику?
Ян

16
Роздратуйте його далі, зменшивши все тіло функції доprivate string GetFormattedValue() => string.Format(format ?? "{0}", value);
Грем

Відповіді:


46

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

  • додаткова змінна не "пояснює" нічого, що не зрозуміло з оточуючої назви методу
  • вислів стає ще довшим, тому (трохи) менш читабельним

Більше того, новіші версії налагоджувача Visual Studio можуть у більшості випадків показувати повернене значення функції, не вводячи зайву змінну (але будьте обережні, ознайомтеся з цим старшим повідомленням SO та різними відповідями ).

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


Я також згоден, що напевно є випадки, коли це корисно, без сумніву.
Пол Кертчер

2
Я зазвичай використовую resultяк ім'я змінної. Не так довше і простіше налагодження
edc65

26
@ edc65: таке загальне ім'я, як result часто, просто додає шум коду і рідко збільшує читабельність, саме в цьому і полягає моя відповідь. Це може бути виправдано в контексті, коли це допомагає налагоджувати, але я б уникав при використанні налагоджувача, якому не потрібна окрема змінна.
Doc Brown

6
@JonHanna шлях інструмент довгий по мені. Назва resultпередає інформацію про те, що це значення, яке є результатом функції, так що ви можете переглянути її до того, як функція повернеться.
edc65

1
@ edc65, але це виглядає корисною. Тож зараз, читаючи ваш код, я не відразу розумію, що це не так. Таким чином ваш код став менш читабельним.
Джон Ханна

38

З огляду на факти, які:

а) На остаточний код немає впливу, оскільки компілятор оптимізує змінну в сторону.

b) Окремий розділ розширює можливість налагодження.

Я особисто прийшов до висновку, що це хороша практика відокремлювати їх 99% часу.

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


9
Це правильна відповідь для мене. Це полегшує встановлення точки розриву і бачити значення при налагодженні, і не має недоліків, про які я знаю.
Меттью Джеймс Бріггс

Для точки b, у Visual Studio Code, просто поставте точку розриву на звороті та додайте вираз: GetFormattedValue (), який покаже результат при попаданні точки перелому, тому додаткова лінія не потрібна. Але простіше бачити місцевих жителів із додатковою лінією, оскільки це не зажадає додавання додаткового вираження у відладчику. Отже, справді питання особистої переваги.
Джон Рейнор

3
@JonRaynor для повернення значення, поставте точку розриву на кінцевий кронштейн функції. Він ловить повернене значення, навіть у функціях з декількома поверненнями.
Балдрік

16

Часто введення змінної просто для того, щоб назвати якийсь результат, дуже корисно, коли це робить код більш самодокументованим. У цьому випадку це не є фактором, оскільки ім'я змінної дуже схоже на ім'я методу.

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

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


1
"Я вважаю, що обидва рішення є заплутаними, оскільки умовний оператор використовується без необхідності." - Це не справжній світовий приклад, я просто повинен був щось скласти, швидко. Це, мабуть, не найкращий приклад.
Пол Кертчер

4
+1, по суті кажучи, це різниця "під радаром", що (за інших рівних речей) не варто цього турбувати.
TripeHound

3
@Mindwin, коли я використовую потрійний оператор, я зазвичай розбиваю його на кілька рядків, щоб було зрозуміло, що є справжнім випадком, а що - хибним.
Артуро Торрес Санчес

2
@ ArturoTorresSánchez Я теж це роблю, але замість a ?і a :я використовую if() {і } else {- - - - \\ :)
Mindwin

3
@Mindwin, але я не можу цього зробити, коли я перебуваю посеред виразу (як ініціалізатор об'єкта)
Arturo Torres Sánchez

2

У відповідь на ваші запитання:

Моє запитання полягає в тому, якщо все-таки є дійсним питання писати код менш зрозумілим, просто щоб полегшити налагодження?

Так. Насправді, частина вашого попереднього твердження мені здається (без образи) трохи недалекоглядною (див. Жирний шрифт нижче) " Його аргументом було те, що колишній варіант простіше налагодити - що є досить маленькою заслугою , оскільки VisualStudio дозволяє нам дуже детально перевірити висловлювання, коли виконання припинено через пункт пробою ».

Полегшення налагодження (майже) ніколи не викликає « малих заслуг », оскільки, за деякими оцінками, 50% часу програміста витрачається на налагодження ( Reverseble Debugging Software ).

Чи є якісь додаткові аргументи для варіанту із розрізненим розрахунком та випискою повернення?

Так. Деякі розробники стверджують, що розрахунок розбиття легше читати. Це, звичайно, допомагає при налагодженні, але також допомагає, коли хтось намагається розшифрувати будь-які бізнес-правила, які ваш код може виконувати чи застосовувати.

ПРИМІТКА: Правила бізнесу можуть бути краще подані в базі даних, оскільки вони можуть часто змінюватися. Тим не менш, чітке кодування в цій області все ще має першорядне значення. ( Як побудувати двигун бізнес-правил )


1

Я б пішов ще далі:

private string GetFormattedValue()
{
    if (format != null) {
        formattedString = string.Format(format, value);
    } else {
        formattedString = value.ToString()
    }
    return formattedString;
}

Чому?

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

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


Моя краща альтернатива - якщо ви "отримуєте повернення якомога швидше натовпу", а не проти багаторазового повернення методу:

private string GetFormattedValue()
{
    if (format != null) {
        return string.Format(format, value);
    }

    return value.ToString();
}

Отже, ви можете переглянути останнє повернення, щоб побачити, що таке за замовчуванням.

Важливо бути послідовним - і всі ваші методи дотримуються тієї чи іншої конвенції.


1
Перший приклад здається поганою практикою, оскільки value.ToString()його викликають без потреби, коли формат є недійсним. У загальному випадку це може включати нетривіальні обчислення та може зайняти більше часу, ніж версія, що включає рядок формату. Розглянемо, наприклад, a, valueякий зберігає PI до мільйона десяткових знаків, і рядок формату, який вимагає лише перших кількох цифр.
Стів

1
чому б не private string GetFormattedValue() => string.Format(format ?? "{0}", value); вплинути на те саме, і використовувати тести одиниць, щоб забезпечити правильність замість того, щоб покладатися на відладчик.
Берін Лорич

1
Хоча я згоден, тернар може бути менш зрозумілим, нульовий термінатор може зробити речі більш зрозумілими. Принаймні в цьому випадку.
Берін Лорич

1
Шановний щоденнику, сьогодні я прочитав, що написання чіткого та стислого коду з використанням відомих (існує вже близько 40 років) парадигм, ідіом та операторів є, цитата, подвійна цитата є розумною подвійною цитатою, цитата - але натомість написання надмірно багатослівного коду порушує DRY і не використовуючи вищезгадані оператори, ідіоми і парадигми , а замість того, щоб намагатися уникати всього , що могло б , здається загадковим тільки п'ять - річний без попереднього програмування фону - це ясність замість цього. Чорт, я, мабуть, старенький, дорогий мій щоденнику ... Я повинен був навчитися йти, коли мав можливість.
vaxquis

1
"Використання потрійних операторів для більш складної логіки було б нечитабельним" Хоча це дійсно так (і я бачив, що люди мають надскладну логіку), це не стосується коду ОП, а також не є специфічним для тернарних операторів. Єдине, що я можу сказати з повною впевненістю, це те, що лінія занадто довга. gist.github.com/milleniumbug/cf9b62cac32a07899378feef6c06c776 - це я б переформатував.
тисячоліття

1

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

Люди також недооцінюють час, який вони проводять налагодження. Вони недооцінюють, скільки часу вони можуть витратити, переслідуючи довгу помилку. З тестуванням я знаю відразу, коли додав помилку. Це дозволяє мені виправити помилку негайно, перш ніж вона може повзати та сховатися. Є кілька речей, які більш засмучують чи витрачають час, ніж налагодження. Чи не було б це пекло набагато швидше, якби ми просто не створили помилок в першу чергу?


Мартін Фаулер - людина розумна, і мені подобалося читати його (і ваші) погляди. Хоча я твердо вірю, що тестування необхідне і що на це потрібно витратити більше часу, той факт, що ми всі помилкові люди, говорить про те, що жодна кількість тестувань не усуне всіх помилок. Отже, налагодження завжди буде частиною процесу розробки та підтримки програми.
tale852150

1

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

Щодо фокусу вашого питання, переміщення повернутого оператора на посилання змінною ...

Це запитання робить два припущення, з якими я не згоден:

  1. Що другий варіант зрозуміліший чи простіший для читання (я кажу, що навпаки правда), і

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

Виконуючи щось із названої змінної рідко коли-небудь ускладнює читання, майже завжди відбувається навпаки. Конкретний спосіб, коли хтось це робить, може спричинити проблеми, як, наприклад, коли оверлонд самодокументації робить це, var thisVariableIsTheFormattedResultAndWillBeTheReturnValue = ...очевидно, це погано, але це окрема проблема. var formattedText = ...добре.

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

Загалом, і ви запитували загальне правило (ваш приклад був саме таким, приклад узагальненої форми), всі пункти, зроблені на користь варіанту 1 (2-лайнера), є правильними. Це хороші вказівки. Але вказівки повинні бути гнучкими. Наприклад, проект, над яким я зараз працюю, має максимум 80 символів на рядок, тому я розбиваю багато рядків, але я зазвичай знаходжу рядки 81-85 символів, які було б незручно розділити або зменшити читабельність, і я залишаю їх межа.

Оскільки це навряд чи додасть значення, я б не робив 2 рядки для конкретного наведеного прикладу. Я б зробив варіант 2 (1-лайнер), оскільки очки недостатньо сильні, щоб зробити інакше в цьому випадку.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.