У яких випадках менше коду не краще? [зачинено]


55

Останнім часом я відновив деякий код на роботі, і думав, що зробив гарну роботу. Я скинув 980 рядків коду до 450 і вдвічі зменшив кількість занять.

Коли показували це моїм колегам, деякі не погоджувались, що це було покращення.

Вони сказали - "менше рядків коду не обов'язково краще"

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

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


145
Розмір коду вимірюється часом, який потрібно прочитати та зрозуміти, а не рядками чи кількістю символів.
Бергі

13
Ваше запитання як написане категорично занадто широке. Рекомендуйте написати новий про конкретні зміни, які ви внесли замість цього.
jpmc26

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

65
Існує цілий сайт обміну стеками, присвячений відповіді на ваше запитання: codegolf.stackexchange.com . :)
Федеріко Полоні

Відповіді:


124

Худенька людина не обов'язково здоровіша за людину із зайвою вагою.

Розповідь про дітей з 980 рядків легше читати, ніж теза з фізики 450 рядків.

Є багато атрибутів, які визначають якість вашого коду. Деякі з них просто обчислюються, як цикломатична складність і складність Галстеда . Інші - більш чітко визначені, такі як згуртованість , читабельність, зрозумілість, розширюваність, надійність, коректність, самодокументація, чистота, доказовість та багато іншого.

Можливо, наприклад, що, скорочуючи загальну довжину коду, ви ввели додаткову необґрунтовану складність і зробили код більш виразним.

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

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


1
@PiersyP просто FYI, одне з керівних принципів, які мене вчили про хороший рефакторинг, - це те, що ми повинні бачити, що цикломатична складність зводиться до квадратного кореня того, що воно було спочатку.
М. А. Ханін

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

6
Гарна робота, хлопці - ви встановили, що десь є "правильна" вага (точна кількість може змінюватися). Навіть в оригінальних публікаціях @Neil сказано, що «ПЕРЕВІГА», на відміну від «важчої людини», і це тому, що там є солодке місце, як і в програмуванні. Додавання коду, що перевищує цей "правильний розмір", - це просто безлад, а видалення рядків нижче цієї точки просто жертвує розумінням заради стислості. Знаючи, де саме цей момент ... Це складний біт.
AC

1
Тільки тому, що вона не потрібна, не означає, що вона не має значення.
Кріс Уолерт

1
@Neil Ви, як правило, праві, але незрозумілий "баланс", на який ви натякаєтесь, є чимось міфом, об'єктивно кажучи. У кожного різне уявлення про те, що таке «хороший баланс». Зрозуміло, що ОП подумав, що він зробив щось добре, а його колеги цього не зробили, але я впевнений, що всі вони думали, що вони мали "правильний баланс", коли писали код.
code_dredd

35

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

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

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


1
Так, безумовно, відокремте інтерфейс користувача від інших речей, це завжди варто. Що стосується того, що ви втрачаєте з поля зору первинну мету, я згоден дещо, але також ви можете переробити щось краще або на шляху до кращого. Як і старий аргумент про Еволюцію («що добра є частиною крила?»), Речі не покращуються, якщо ви ніколи не потребуєте часу на їх вдосконалення. Ви не завжди знаєте, куди їдете, поки не будете добре. Я згоден з тим, що намагаюся з'ясувати, чому колеги непросто, але, можливо, це справді "їхня проблема", а не твоя.

18

На думку приходить цитата, яку часто приписують Альберту Ейнштейну:

Зробити все максимально просто, але не простіше.

Якщо ви перейдете за обробку речей, це може ускладнити читання коду. Оскільки "легко / важко читати" може бути дуже суб'єктивним терміном, я поясню, що саме я маю на увазі під цим: міру ступеня складності, яку матиме кваліфікований розробник при визначенні "що робить цей код?" просто дивлячись на джерело, без допомоги спеціалізованих інструментів.

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

Якщо я скажу var x = 2 + 2;, відразу очевидно, що xце повинно бути цілим числом. Але якщо я скажу var foo = value.Response;, це набагато менш зрозуміло, що fooявляє собою або які його властивості та можливості. Навіть якщо компілятор може легко зробити це, це докладає людині набагато більше пізнавальних зусиль.

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


7
varприклад не є особливо гарним спрощення , так як більша частина часу на читання і розуміння коду включає в себе з'ясування поведінки на певному рівні абстракції, тому знаючи фактичні типи специфічних змінних , як правило, нічого не змінює (це тільки допомагає зрозуміти нижчі абстракції). Кращим прикладом можуть бути декілька рядків простого коду, if ((x = Foo()) != (y = Bar()) && CheckResult(x, y)) зібраного в єдине згорнуте висловлювання - наприклад, потрібен час, аби дізнатися про типи xчи yне допомогти.
Бен Коттрелл

15

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

Але давайте подивимось під іншим кутом зору. Ми припускаємо, що новий код вважатиметься вищим від кваліфікованих програмістів, які бачать 2 частини коду, не маючи додаткових знань про культуру компанії, базу коду чи дорожню карту. Вже тоді є безліч причин заперечувати проти нового коду. Для стислості я зателефоную "Люди, що критикують новий код" Pecritenc :

  • Стабільність. Якщо старий код, як відомо, був стабільним, стабільність нового коду невідома. Перш ніж використовувати новий код, його ще потрібно перевірити. Якщо з якихось причин належне тестування не доступне, зміни є досить великою проблемою. Навіть якщо тестування доступні, Pecritenc може подумати, що зусилля не варто (незначного) вдосконалення коду.
  • Продуктивність / масштабування Старий код міг би масштабуватися краще, і Pecritenc припускає, що продуктивність стане проблемою вниз, оскільки клієнти та функції незабаром * накопичуються.
  • Розширюваність. Старий код, можливо, дозволив легко ввести деякі функції, які Pecritenc передбачає додати незабаром *.
  • Ознайомлення. Старий код може повторно використовувати шаблони, які використовуються в 5 інших місцях кодової бази компанії. У той же час новий код використовує химерний зразок, про який в цей момент чула лише половина компанії.
  • Помада на свині. Пекритенк може подумати, що і старий, і новий код є сміттям або не мають значення, тому робить будь-яке порівняння між ними безглуздим.
  • Гордість. Pecritenc, можливо, був оригінальним автором коду і не любить людей, що вносять масові зміни в його код. Він може навіть сприймати поліпшення як легку образу, тому що вони означають, що він мав би зробити краще.

4
+1 для "Pecritenc", і дуже приємний підсумок обґрунтованих заперечень, які слід заздалегідь врахувати перед попередньою підготовкою.

1
І +1 для "розширюваності" - я вважав, що в початковому коді можуть бути функції або класи, призначені для використання в майбутньому проекті, тому абстракції можуть здатися зайвими або непотрібними, але лише в контексті однієї програми.
Даррен Рінгер

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

@nocomprende З будь-якої причини ви використовували передбачуваний, попередньо розглянутий і попередній фактор? Метод, схожий на Пекритенка, можливо?
Milind R

@MilindR Мабуть, заздалегідь задумане, схильність чи, можливо, особисті переваги? Або, можливо, просто немає причин, космічне злиття кофакторів, заплутане в змові. Немає ідеї, насправді. Як щодо тебе?

1

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

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

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

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


3
"бабусі та дідусі"! хо-ха! Просто стежте за заняттями Адама та Єви. (І клас Бога звичайно) До цього він був без форм, і порожній.

1

Це повністю залежить. Я працював над проектом, який не допускає булевих змінних як функціональних параметрів, а натомість вимагає виділення enumдля кожного параметра.

Тому,

enum OPTION1 { OPTION1_OFF, OPTION1_ON };
enum OPTION2 { OPTION2_OFF, OPTION2_ON };

void doSomething(OPTION1, OPTION2);

набагато більше багатослівного, ніж

void doSomething(bool, bool);

Однак,

doSomething(OPTION1_ON, OPTION2_OFF);

набагато читає, ніж

doSomething(true, false);

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


0

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

Наприклад, у веб-додатку Скажімо, у вас є сторінка адміністратора, на якій ви індексуєте всі продукти, які по суті є тим самим кодом (індексом), який ви використовували в домашній сторінці, щоб .. просто індексувати продукти.

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

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


0
  • Коли менше коду не виконує таку ж роботу, як більше коду. Рефакторинг для простоти хороший, але ви повинні подбати про те, щоб не спрощувати проблемний простір, з яким зустрічається це рішення. 980 рядків коду може обробляти більше кутових справ, ніж 450.
  • Коли менше коду не виходить так витончено, як більше коду. Я бачив пару завдань "ref *** toring", виконаних на коді, щоб видалити "непотрібні" пробні помилки та інші помилки у випадку помилок. Неминучим результатом було замість того, щоб показати діалогове вікно з приємним повідомленням про помилку та те, що може зробити користувач, додаток вийшов з ладу або YSODed.
  • Коли менше коду менш підтримується / розширюється, ніж більше коду. Рефакторинг на стислість коду часто видаляє "непотрібні" конструкції коду в інтересах LoC. Проблема полягає в тому, що такі конструкції коду, як паралельні декларації інтерфейсу, вилучені методи / підкласи тощо, необхідні, якщо цей код коли-небудь повинен робити більше, ніж зараз, або робити це інакше. У крайньому випадку, певні рішення, пристосовані до конкретної проблеми, можуть не працювати взагалі, якщо визначення проблеми зміниться трохи.

    Один приклад; у вас є список цілих чисел. Кожне з цих цілих чисел має у списку повторне значення, крім одного. Ваш алгоритм повинен знайти це непарне значення. Загальноприйнятне рішення полягає в порівнянні кожного числа з кожним іншим числом, поки ви не знайдете в списку число, яке не має дупелю, що є N ^ 2-разовою операцією. Ви також можете створити гістограму за допомогою хештеля, але це дуже неефективно для простору. Однак ви можете зробити це лінійним часом і постійним простором, використовуючи побітну операцію XOR; XOR кожне ціле число проти запущеного "усього" (починаючи з нуля), і в кінці кінцевою сумою буде значення вашого непарного цілого числа. Дуже елегантний. Поки вимоги не змінюються, і більше ніж одне число у списку може бути неспареним або цілі числа включатимуть нуль. Тепер ваша програма або повертає сміття, або неоднозначні результати (якщо він повертає нуль, чи це означає, що всі елементи є парними, або що непарний елемент дорівнює нулю?). Така проблема "розумних" реалізацій в реальному програмуванні.

  • Коли менше коду, менше самодокументування, ніж більше коду. Вміння читати сам код і визначати, що він робить, є вирішальним для розвитку команди. Надавши алгоритм brain-f ***, ви написали, що він чудово виконує молодший розробник і просить його налаштувати його, щоб трохи змінити вихід, не вдасться отримати вас дуже далеко. Чимало старших дияволів також матимуть проблеми з цією ситуацією. Можливість зрозуміти в будь-який момент, що робить код, і що може зіткнутися з ним, є ключовим для середовища розвитку команди (і навіть соло; я гарантую вам, що спалах генія, який ви мали, коли ви писали 5 -лінійний метод вилікувати рак вже давно не піде, коли ти повернешся до цієї функції і хочеш вилікувати хворобу Паркінсона.)

0

Комп'ютерним кодом потрібно зробити ряд речей. "Мінімалістичний" код, який не робить цього, не є гарним кодом.

Наприклад, комп'ютерна програма повинна охоплювати всі можливі (або, як мінімум, всі можливі випадки). Якщо фрагмент коду охоплює лише один "базовий регістр" і ігнорує інші, це не дуже добре код, навіть якщо він короткий.

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

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


Обов’язок на очах у глядача.

-2

Обчислювальна продуктивність. При оптимізації паралельних трубопроводів або запуску деталей коду паралельно вам може бути корисно, наприклад, не циклічно від 1 до 400, але від 1 до 50, і помістити 8 примірників аналогічного коду в кожен цикл. Я не припускаю, що це було у вашій ситуації, але це приклад, коли більше рядків краще (ефективні).


4
Хороший компілятор повинен краще, ніж пересічний програміст, знати, як розгортати петлі для певної архітектури комп'ютера, але загальна точка справедлива. Я одного разу переглянув вихідний код для рутинного множення матриць із бібліотеки високопродуктивних файлів Cray. Матричне множення - це три вкладені петлі і приблизно 6 рядків коду, чи не так? Неправильно - звичайна бібліотека склала приблизно 1100 рядків коду плюс аналогічна кількість рядків коментарів, що пояснювали, чому це було так довго!
алефзеро

1
@alephzero Ух, я хотів би побачити цей код, це повинен бути просто Cray Cray.

@alephzero, хороші компілятори можуть зробити багато, але, на жаль, не все. Яскрава сторона - це те, що цікавить програмування!
Ганс Янссен

2
@alephzero Справді, хороший код множення матриці не просто голить трохи часу (тобто зменшує його на постійний коефіцієнт), він використовує абсолютно інший алгоритм з різною асимптотичною складністю, наприклад алгоритм Страссена приблизно O (n ^ 2.8) а не O (n ^ 3).
Артур Такка
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.