Що не так у коментарях, що пояснюють складний код?


236

Багато людей стверджують, що "коментарі повинні пояснювати" чому ", а не" як "". Інші кажуть, що «код повинен бути самодокументованим», а коментарі мають бути мізерними. Роберт К. Мартін стверджує, що (перефразувавши мої власні слова) часто "коментарі - це вибачення за погано написаний код".

Моє запитання таке:

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

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

Англійська мова "розроблена", щоб її легко зрозуміли люди. Java, Ruby або Perl, однак, були розроблені для збалансованості людської читабельності та читабельності на комп’ютері, тим самим ставлячи під загрозу читабельність тексту. Людина може зрозуміти фрагмент англійської мови набагато швидше, що він / вона може зрозуміти фрагмент коду з тим же значенням (поки операція не є тривіальною).

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

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

Але ми всі знаємо, що цього недостатньо. Це прості вказівки - важливі та корисні, але вони не змінюють того, що деякі алгоритми є складними. І тому їх важко зрозуміти, читаючи їх по черзі.

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


14
Якщо це складник, спробуйте його рефакторинг на більш дрібні шматки.
Vaughan Hilts

151
У теорії немає різниці між теорією та практикою. На практиці є.
Скотт Лідлі

5
@mattnz: точніше, під час написання коментаря ви опираєтесь на проблему, яку вирішує цей код. Наступного разу, коли ви відвідаєте, у вас буде менше можливостей щодо цієї проблеми .
Стів Джессоп

26
"Що" робить функція чи метод, повинно бути очевидно з її назви. Як це робиться, видно з його коду. Чому це робиться таким чином, які неявні припущення використовувались, які документи потрібно прочитати, щоб зрозуміти алгоритм тощо - слід зазначити у коментарях.
SK-логіка

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

Відповіді:


408

Простіше кажучи:

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

Нижня лінія:

Пояснювати себе добре, не потребувати цього краще.


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

34
@aecolley Введення коду для пояснення для початку ще краще.
Tulains Córdova

127
Іноді сам пояснювальний код недостатньо ефективний, щоб вирішити проблему з сьогоднішнім HW&SW. І ділова логіка є горезвісно ... закручена. Набір проблем, які мають елегантні програмні рішення, значно менший, ніж набір проблем, які економічно корисно вирішити.
Скотт Лідлі

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

29
@ user61852 Окрім того, що те, що пояснюється тобою, що щойно написав цей код і занурився в нього останній $-період, може бути не поясненням для тебе, хто повинен підтримувати чи редагувати його через п'ять років, не кажучи вже про всі можливі люди, які не ти, що, можливо, доведеться на це дивитися. "Роз'яснення" - туманний святий грааль визначень.
Шадур

110

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

Однак бувають випадки, коли вдало підібраний коментар є найкращим вибором.

  • Якщо саме алгоритм складний і заплутаний, а не лише його реалізація - така, яка записується в математичні журнали і колись називається Алгоритмом Мбого - тоді ви ставите коментар на самому початку впровадження, читаючи щось на кшталт "Це алгоритм Мбого для переобладнання віджетів, спочатку описаний тут: [URL-адреса паперу]. Ця реалізація містить уточнення Еліс та Керол [URL іншого документу]." Не намагайтеся вникати в більш детальну інформацію, ніж це; якщо хтось потребує більш детальної інформації, він, ймовірно, повинен прочитати весь документ.

  • Якщо ви взяли щось, що може бути записане як один або два рядки в якійсь спеціалізованій нотації, і розширили їх у великий глобус імперативного коду, поміщення цих або двох рядків спеціалізованої нотації в коментар вище функції - це хороший спосіб скажіть читачеві, що він повинен робити. Це виняток з аргументу "але що робити, якщо коментар не синхронізується з кодом", оскільки спеціалізовані позначення, ймовірно, набагато простіше знайти помилки, ніж код. (Це навпаки, якщо ви замість цього написали специфікацію англійською мовою.) Хороший приклад - тут: https://dxr.mozilla.org/mozilla-central/source/layout/style/nsCSSScanner.cpp#1057 ...

    /**
     * Scan a unicode-range token.  These match the regular expression
     *
     *     u\+[0-9a-f?]{1,6}(-[0-9a-f]{1,6})?
     *
     * However, some such tokens are "invalid".  There are three valid forms:
     *
     *     u+[0-9a-f]{x}              1 <= x <= 6
     *     u+[0-9a-f]{x}\?{y}         1 <= x+y <= 6
     *     u+[0-9a-f]{x}-[0-9a-f]{y}  1 <= x <= 6, 1 <= y <= 6
    
  • Якщо код є загальним, але містить одну-дві речі, які виглядають надмірно перекрученими, непотрібними або просто невірними, але повинні бути таким чином через причини, тоді ви ставите коментар одразу над підозрілим виглядом, в якому ви заявляєте причини . Ось простий приклад, де єдине, що потребує пояснення, - чому константа має певне значення.

    /* s1*s2 <= SIZE_MAX if s1 < K and s2 < K, where K = sqrt(SIZE_MAX+1) */
    const size_t MUL_NO_OVERFLOW = ((size_t)1) << (sizeof(size_t) * 4);
    if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
        nmemb > 0 && SIZE_MAX / nmemb < size)
      abort();
    

25
Це обурення, 4повинно бути CHAR_BIT / 2;-)
Стів Джессоп

@SteveJessop: Чи що-небудь перешкоджає реалізації, де CHAR_BITSбуло 16, а sizeof (size_t) було 2, але максимальне значення size_t було, наприклад, 2 ^ 20 [size_t, що містить 12 біт для забиття]?
supercat

2
@supercat я не бачу нічого , що , очевидно , виключає , що в C99, що означає , що приклад є технічно невірно. Це може бути взято з (трохи модифікованої версії) OpenBSD reallocarray, і OpenBSD, як правило, не вірить у задоволення можливостей, які не трапляються в їх ABI.
zwol

3
@Zack: Якщо код розроблений на основі припущень POSIX, використання CHAR_BITS може створити враження, що код може працювати зі значеннями, відмінними від 8.
supercat

2
@Zack: Щоб типи неподатних типів точної ширини були корисними, їх семантику потрібно було б визначити незалежно від розміру int. Оскільки це дано uint32_t x,y,z;, значення (x-y) > zзалежить від розміру int. Крім того, мова, призначена для написання надійного коду, повинна дозволяти програмістам розрізняти тип, коли очікується, що обчислення перевищуватимуть діапазон типу, і повинні мовчки переносити їх, порівняно з тим, де слід обходити обчислення, що перевищують діапазон типу, порівняно з тим, де обчислення не очікується перевищення діапазону типу, але ...
supercat

61

Отже, що не так у поясненні складного коду з коментарем?

Це не питання правильного чи неправильного, а «найкращої практики», як визначено у статті Вікіпедії :

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

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

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


42
+1 для "набагато частіше знайти коментований код, який потребує рефакторингу, ніж код, який знову ремонтується, що вимагає коментарів"
Брендон

7
Гаразд, але як часто цей коментар: //This code seriously needs a refactor
Ерік Reppen

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

54

Прийде день, коли ваш прекрасний, ідеально оформлений, добре структурований і читабельний код не працюватиме. Або це не буде працювати досить добре. Або виникне особливий випадок, коли він не працює і потребує коригування.

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

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

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

Тому я не вважаю коментарі вибаченням за поганий код, але, можливо, як пояснення того, чому ви не зробили очевидного. Маючи // The standard approach doesn't work against the 64 bit version of the Frobosticate Libraryдозволить майбутнім розробникам, в тому числі вашого майбутнього себе, звернути увагу на ту частину коду і тестування проти цієї бібліотеки. Звичайно, ви можете також помістити коментарі до свого джерела контролю, але люди будуть дивитись на них лише після того, як щось пішло не так. Вони будуть читати коментарі до кодів, коли вони змінюють код.

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

Коли я пишу код для хобі-проектів, про які я знаю, що ніхто більше ніколи не прочитає, я все-таки коментую частини, які вважаю заплутаними - наприклад, будь-яка 3D-геометрія передбачає математику, з якою я не зовсім домашній - бо знаю, коли повернусь через півроку я зовсім забув, як це робити. Це не вибачення за поганий код, це підтвердження особистого обмеження. Все, що я робив би, залишаючи це без коментарів, - це створити більше роботи для себе в майбутньому. Я не хочу, щоб моєму майбутньому «я» довелося переучувати щось зайве, якщо я можу цього уникнути. Яке можливе значення мала б це?


5
@Christian це? Перший рядок посилається на це твердження, безумовно, але поза цим воно трохи ширше, наскільки я його розумію.
гленатрон

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

6
У багатьох випадках найінформативніша та корисна історична інформація стосується речей, які вважаються, але вирішені проти. Є багато випадків, коли хтось обирає підхід X для чогось, а якийсь інший підхід Y здасться кращим; у деяких із цих випадків Y буде "майже" працювати краще, ніж X, але виявиться, що матиме непереборні проблеми. Якщо Y уникнути через ці проблеми, такі знання можуть допомогти запобігти іншим витрачати час на невдалі спроби реалізувати підхід Y.
supercat

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

1
@Lilienthal, я не думаю, що останній пункт обмежений особистими проектами - він сказав "... Я все ще коментую частини, які вважаю заплутаними".
Wildcard

29

Потреба в коментарях обернено пропорційна рівню абстракції коду.

Наприклад, мова для складання з більшості практичних цілей не зрозуміла без коментарів. Ось уривок з невеликої програми, яка обчислює та друкує терміни серії Фібоначчі :

main:   
; initializes the two numbers and the counter.  Note that this assumes
; that the counter and num1 and num2 areas are contiguous!
;
    mov ax,'00'                     ; initialize to all ASCII zeroes
    mov di,counter                  ; including the counter
    mov cx,digits+cntDigits/2       ; two bytes at a time
    cld                             ; initialize from low to high memory
    rep stosw                       ; write the data
    inc ax                          ; make sure ASCII zero is in al
    mov [num1 + digits - 1],al      ; last digit is one
    mov [num2 + digits - 1],al      ; 
    mov [counter + cntDigits - 1],al

    jmp .bottom         ; done with initialization, so begin

.top
    ; add num1 to num2
    mov di,num1+digits-1
    mov si,num2+digits-1
    mov cx,digits       ; 
    call    AddNumbers  ; num2 += num1
    mov bp,num2         ;
    call    PrintLine   ;
    dec dword [term]    ; decrement loop counter
    jz  .done           ;

    ; add num2 to num1
    mov di,num2+digits-1
    mov si,num1+digits-1
    mov cx,digits       ;
    call    AddNumbers  ; num1 += num2
.bottom
    mov bp,num1         ;
    call    PrintLine   ;
    dec dword [term]    ; decrement loop counter
    jnz .top            ;
.done
    call    CRLF        ; finish off with CRLF
    mov ax,4c00h        ; terminate
    int 21h             ;

Навіть з коментарями це може бути досить складно.

Сучасний приклад: регулярні вирази - це дуже низькі абстракційні конструкції (малі літери, цифри 0, 1, 2, нові рядки тощо). Вони, ймовірно, потребують коментарів у вигляді зразків (Bob Martin, IIRC, це визнає). Ось регулярний вираз, який (я думаю) повинен відповідати URL-адресам HTTP (S) та FTP:

^(((ht|f)tp(s?))\://)?(www.|[a-zA-Z].)[a-zA-Z0-9\-\.]+\.(com|edu|gov|m
+il|net|org|biz|info|name|museum|us|ca|uk)(\:[0-9]+)*(/($|[a-zA-Z0-9\.
+\,\;\?\'\\\+&amp;%\$#\=~_\-]+))*$

У міру просування мов вдосконалення ієрархії абстракцій програміст може використовувати евокативні абстракції (назва змінної, назви функцій, назви класів, назви модулів, інтерфейси, зворотні виклики тощо) для забезпечення вбудованої документації. Нехтувати, щоб скористатися цим, і використовувати коментарі, щоб надрукувати це - ліниво, недоброзичливо і неповажно до обслуговуючого персоналу.

Я маю в виду Numerical Recipes в C переведений в основному дослівно Numerical Recipes в C ++ , який я роблю висновок почав як Numerical Recipes (в FORTAN), з усіма змінними a, aa, b, c, ccі т.д. підтримується з допомогою кожної версії. Алгоритми, можливо, були правильними, але вони не скористалися абстракціями, які надаються мовами. І вони мене відігнали. Зразок із статті доктора Доббса - Швидке перетворення Фур'є :

void four1(double* data, unsigned long nn)
{
    unsigned long n, mmax, m, j, istep, i;
    double wtemp, wr, wpr, wpi, wi, theta;
    double tempr, tempi;

    // reverse-binary reindexing
    n = nn<<1;
    j=1;
    for (i=1; i<n; i+=2) {
        if (j>i) {
            swap(data[j-1], data[i-1]);
            swap(data[j], data[i]);
        }
        m = nn;
        while (m>=2 && j>m) {
            j -= m;
            m >>= 1;
        }
        j += m;
    };

    // here begins the Danielson-Lanczos section
    mmax=2;
    while (n>mmax) {
        istep = mmax<<1;
        theta = -(2*M_PI/mmax);
        wtemp = sin(0.5*theta);
        wpr = -2.0*wtemp*wtemp;
        wpi = sin(theta);
        wr = 1.0;
        wi = 0.0;
        for (m=1; m < mmax; m += 2) {
            for (i=m; i <= n; i += istep) {
                j=i+mmax;
                tempr = wr*data[j-1] - wi*data[j];
                tempi = wr * data[j] + wi*data[j-1];

                data[j-1] = data[i-1] - tempr;
                data[j] = data[i] - tempi;
                data[i-1] += tempr;
                data[i] += tempi;
            }
            wtemp=wr;
            wr += wr*wpr - wi*wpi;
            wi += wi*wpr + wtemp*wpi;
        }
        mmax=istep;
    }
}

Як особливий випадок абстракції, кожна мова має ідіоми / канонічні фрагменти коду для певних загальних завдань (видалення динамічного пов'язаного списку в С), і незалежно від того, як вони виглядають, вони не повинні бути задокументовані. Програмісти повинні вивчити ці ідіоми, оскільки вони неофіційно є частиною мови.

Тож забирайте: недіотичний код, побудований із будівельних блоків низького рівня, яких не уникнути, потребує коментарів. І це потрібно ВАААЙ ​​менше, ніж трапляється.


1
Нікого не може бути дійсно писати такий рядок на мові асемблера: dec dword [term] ; decrement loop counter. З іншого боку, те, чого бракує приклад вашої мови збірки, - це коментар перед кожним "кодовим абзацом", в якому пояснюється, що робить наступний блок коду. У цьому випадку коментар, як правило, еквівалентний одному рядку в псевдокоді, наприклад ;clear the screen, з наступними 7 рядками, які потрібні фактично для очищення екрана.
Скотт Уїтлок

1
Так, є кілька непотрібних зауважень у монтажному зразку, але, справедливо кажучи, він є досить представником стилю "Хороший" збірки. Навіть з прологом одного чи двох рядків абзацу, коду було б дуже важко дотримуватися. Я зрозумів зразок ASM краще, ніж приклад FFT. Я запрограмував FFT на C ++ у середній школі, і це не виглядало як щось подібне, але тоді ми використовували STL, ітератори, функтори досить багато викликів методів. Не так швидко, як монолітна функція, але набагато простіше для читання. Я спробую додати його на відміну від вибірки NRinC ++.
Крістіан Н

Ви мали на увазі ^(((ht|f)tps?)\:\/\/)?(www\.)*[a-zA-Z0-9\-\.]+\.(com|edu|gov|mil|net|org|biz|info|name|museum|us|ca|uk)(\:[0-9]+)*(\/($|[a-zA-Z0-9\.\,\;\?\'\\\+&%\$#\=~_\-]+))*$? Будьте в курсі числових адрес.
izabera

Більш-менш мій погляд: деякі речі, побудовані з абстракцій дуже низького рівня, не легко прочитати чи перевірити. Коментарі (і, щоб не занадто далеко відслідковувати ТЕСТИ) можуть бути корисними, а не шкодити. У той же час, не використовуючи доступні абстракції вищого рівня (: альфа:: число: там, де це доступно), це ускладнює розуміння, навіть з хорошими коментарями, ніж використання абстракцій вищого рівня.
Крістіан Н

3
+1: "The need for comments is inversely proportional to the abstraction level of the code." Досить багато резюмує все там.
Геррат

21

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

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

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


18

Я думаю, що ти читаєш трохи занадто багато в тому, що він говорить. У Вашій скарзі є дві чіткі частини:

Що поганого в поясненні (1) складного алгоритму або (2) довгого і згорнутого фрагмента коду з описовим коментарем?

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

Однак, більшість кодів не (1). Рідко ви будете писати програмне забезпечення, яке є не що інше, як ручне реалізація мьютексу, неясні операції лінійної алгебри з поганою підтримкою бібліотеки та нові алгоритми, відомі лише дослідницькій групі вашої компанії. Більшість кодів складається з викликів бібліотеки / фреймворку / API, вводу-виводу, тестової панелі та тестування модулів.

Це такий код, про який говорить Мартін. І він звертається до вашого питання з цитатою Керніган і Плаугер у верхній частині глави:

Не коментуйте поганий код - перепишіть його.

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

І саме це каже Мартін:

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

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

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


3
Якби розробник, з яким я працював, просто написав "злому рівня бітової точки з плаваючою точкою", щоб пояснити швидкий алгоритм з квадратним коренем - вони мали б спілкуватися зі мною. Поки вони включали посилання на дещо корисніше, я би радий.
Майкл Андерсон

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

2
@trysis haha, так, але у світі, де програмісти відповідальні, а не бізнесмени, вони ніколи не відправлятимуться, оскільки вони назавжди позолочують постійно реконструйовану базу коду у марних пошуках досконалості.
gbjbaanb

4
@PatrickCollins майже все, що я читаю в Інтернеті, - це робити це правильно вперше. Майже ніхто не хоче писати статті про виправлення безладу! Фізики говорять, що "дали ідеальну сферу ..." Comp.Scientists говорять "дали розвиток зеленого поля ..."
gbjbaanb

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

8

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

Нічого як такого. Документування вашої роботи є гарною практикою.

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

На що слід зосередитись, це спрощення та абстрагування складного коду на простіший код, а не думати, що «складний код - це добре, доки він коментується».

В ідеалі ваш код повинен бути простим і документованим.

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

Правда. Ось чому всі ваші публічні алгоритми API повинні бути пояснені в документації.

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

В ідеалі, після написання складного фрагмента коду, ви повинні (не вичерпний список):

  • розглянути проект (тобто планувати його переписати)
  • формалізувати вхідні точки алгоритму / інтерфейси / ролі / тощо (проаналізувати та оптимізувати інтерфейс, формалізувати абстракції, передумови документа, пост-умови та побічні ефекти та випадки помилок документа).
  • писати тести
  • очищення та рефактор

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

[...] деякі алгоритми є складними. І тому їх важко зрозуміти, читаючи їх по черзі.

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

Чи справді так погано пояснювати складний алгоритм за допомогою декількох рядків коментарів щодо його загальної роботи?

Ні - це добре. Додавання кількох рядків коментарів є недостатньою.

Що не так у поясненні складного коду з коментарем?

Те, що у вас не повинно бути складного коду, якщо цього можна уникнути.

Щоб уникнути складного коду, формалізуйте свої інтерфейси, витрачайте ~ 8 разів більше на дизайн API, ніж витрачаєте на реалізацію (Степанов запропонував витратити принаймні 10 разів на інтерфейс, порівняно з реалізацією), і перейдіть до розробки проекту, маючи на увазі, що ви створюєте проект, а не просто пишете якийсь алгоритм.

Проект включає документацію API, функціональну документацію, вимірювання коду / якості, управління проектом тощо. Жоден із цих процесів не є одноразовим, швидким кроком для здійснення (всі вони потребують часу, вимагають продуманості та планування, і всі вони вимагають періодичного повернення до них та перегляду / доповнення ними деталей).


3
"Вам ніколи не слід покладатися на читання реалізації, щоб зрозуміти, що робить API". Іноді це завдано тобі вище за течією, яку ти прагнеш використовувати. У мене був особливо незадовільний проект, залитий коментарями форми "наступний потворний код Хіта Робінсона існує, тому що simpleAPI () не працює належним чином на цьому обладнання, незважаючи на те, що заявляє постачальник".
pjc50

6

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

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

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

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

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


5
Одне корисне місце для коментарів: у науковому коді часто можна мати досить складні обчислення, що включають безліч змінних. З метою розумності програміста має сенс тримати імена змінних дійсно короткими, щоб ви могли дивитись на математику, а не на імена. Але це дуже важко зрозуміти читачеві. Тож короткий опис того, що відбувається (а краще, посилання на рівняння у статті журналу чи подібне) може бути дуже корисним.
naught101

1
@ naught101: так, тим більше, що на папері, на який ви посилаєтесь, також, ймовірно, використовуються однобуквені назви змінних. Зазвичай простіше зрозуміти, що код дійсно слідує за папером, якщо ви використовуєте однакові назви, але це суперечить цілі коду бути самовиясненою (це пояснюється замість паперу ). У цьому випадку коментар, де кожне ім'я визначено, говорить про те, що воно насправді означає, замінює значущі імена.
Стів Джессоп

1
Коли я шукаю щось специфічне в коді (де обробляється цей конкретний випадок?), Я не хочу читати та розуміти абзаци коду, щоб виявити, що це не місце. Мені потрібні коментарі, які в одному рядку узагальнюють те, що робить наступний параграф. Таким чином, я швидко знайду частини коду, пов’язані з моєю проблемою, і пройду нецікаві деталі.
Флоріан F

1
@FlorianF: традиційна відповідь полягає в тому, що назви змінних та функцій повинні приблизно вказувати, про що йдеться в коді, а отже, дозволяти вам переглядати речі, які точно не стосуються того, що ви шукаєте. Я погоджуюсь з вами, що це не завжди вдається, але я не погоджуюся настільки сильно, що, думаю, весь код потрібно коментувати, щоб допомогти в пошуку чи непрочитаному читанні. Але ви маєте рацію, це випадок, коли хтось читає ваш код (свого роду) і законно не потребує його розуміння.
Стів Джессоп

2
@Snowman Люди могли це зробити зі змінними іменами. Я бачив код, де змінна listOfApples містила список бананів. Хтось скопіював код, обробляючи список яблук, і адаптував його для бананів, не турбуючись змінюючи назви змінних.
Флоріан F

5

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

// This code implements the algorithm described in requirements document 239.

або навіть просто

void doPRD239Algorithm() { ...

Безумовно, люди задоволені функціями, названими MatchStringKnuthMorrisPrattабо encryptAESабо partitionBSP. Більш незрозумілі імена варто пояснити в коментарі. Ви також можете додати бібліографічні дані та посилання на папір, з якого ви реалізували алгоритм.

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

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

/* Configure the beam controller and turn on the laser.
The sequence is timing-critical and this code must run with interrupts disabled.
Note that the constant 0xef45ab87 differs from the vendor documentation; the vendor
is wrong in this case.
Some of these operations write the same value multiple times. Do not attempt
to optimise this code by removing seemingly redundant operations.
*/

2
Я б заперечував проти іменування функцій / методів за їх внутрішнім алгоритмом, більшість випадків використовуваний метод повинен бути внутрішнім питанням, якимось чином документуйте верхню частину вашої функції з використовуваним методом, але не називайте це, doPRD239Algorithmщо говорить мені нічого не стосується функції, не шукаючи алгоритм, причина MatchStringKnuthMorrisPrattі encryptAESробота полягає в тому, що вони починаються з опису того, що вони роблять, а далі - з описом методики.
скрагар

5

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

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

Наприклад:

// The getter.
public <V> V get(final K key, Class<V> type) {
  // Has it run yet?
  Future<Object> f = multitons.get(key);
  if (f == null) {
    // No! Make the task that runs it.
    FutureTask<Object> ft = new FutureTask<Object>(
            new Callable() {

              public Object call() throws Exception {
                // Only do the create when called to do so.
                return key.create();
              }

            });
    // Only put if not there.
    f = multitons.putIfAbsent(key, ft);
    if (f == null) {
      // We replaced null so we successfully put. We were first!
      f = ft;
      // Initiate the task.
      ft.run();
    }
  }
  try {
    /**
     * If code gets here and hangs due to f.status = 0 (FutureTask.NEW)
     * then you are trying to get from your Multiton in your creator.
     *
     * Cannot check for that without unnecessarily complex code.
     *
     * Perhaps could use get with timeout.
     */
    // Cast here to force the right type.
    return (V) f.get();
  } catch (Exception ex) {
    // Hide exceptions without discarding them.
    throw Throwables.asRuntimeException(ex);
  }
}

Тут немає спроби констатувати, що кожен крок виконує, все, що він констатує, це те, що він повинен робити.

PS: Я знайшов джерело, на яке я мав на увазі - Кодування жаху: Код говорить вам як, коментарі розповідають чому


8
Перший коментар. Що вже запустили? Те саме стосується інших коментарів. Для тих, хто не знає, що робить код, це марно.
gnasher729

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

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

BTW - коментар " Хіба це запущено" посилається на Futureта вказує на те, що get()слідує перевірка проти nullвиявлення, чи Futureвже запущена - правильно документуючи наміри, а не процес .
OldCurmudgeon

1
@OldCurmudgeon: Ваша відповідь досить близька до того, що я думав, що я просто додам цей коментар як приклад вашої точки зору. Хоча коментар не потрібен для пояснення чистого коду, коментар добре пояснювати, чому кодування було зроблено ОДНІШЕ ЗА ВСІХ. За моїм обмеженим досвідом, коментарі часто корисні для пояснення ідіосинкратії набору даних, над яким працює код, або ділових правил, коди мають застосовуватися. Код для коментування, який додається для виправлення помилки, є хорошим прикладом, якщо ця помилка сталася через те, що припущення про дані було невірним.
Рандалл Стюарт

4

Але ми всі знаємо, що цього недостатньо.

Дійсно? Відколи?

Добре розробленого коду з добрими іменами більш ніж достатньо у переважній більшості випадків. Аргументи проти використання коментарів добре відомі та задокументовані (як ви посилаєтесь).

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


7
Я знаю, що цього недостатньо.
Флоріан F

2
Відколи? Мабуть, ви вже знаєте відповідь на це. "Добре розробленого коду з добрими іменами більш ніж достатньо у переважній більшості випадків". Отже, цього, мабуть, недостатньо у більшості випадків, саме про це запитує запитувач.
Ellesedil

3
Я коли-небудь намагаюся розшифрувати код інших народів, до якого я хотів би додавати коментарі не раз на два роки.
Псалом Огр33,

@ OgrePsalm33 - Чи мають у них маленькі методи і використовують хороші імена? Неправильний код поганий, незалежно від коментарів.
Теластин

2
@Telastyn На жаль, під час роботи над великою базою коду "маленькі" методи та "хороші" імена піддаються кожному розробнику (так це хороший коментар з цього приводу). Розробник, який пише код алгоритму графічної обробки Фларбігана протягом 7 років, може написати щось ідеально зрозуміле для нього та подібних розробників, але був би критичним для нового хлопця, який провів останні 4 роки, розробляючи інфраструктурний код мережі Perbian. Потім, через 2 тижні, експерт із Фларбігана відмовляється.
Псалом Огре3333

2

Основною метою коду є командування комп'ютером зробити щось, тому хороший коментар ніколи не замінює хороший код, оскільки коментарі не можуть бути виконані.

Незважаючи на це, коментарі в джерелі є однією формою документації для інших програмістів (включаючи вас самого). Якщо коментарі стосуються більш абстрактних питань, ніж те, що робиться на кожному кроці, ви робите краще середнього. Цей рівень абстрагування залежить від інструменту, який ви використовуєте. Зауваження, що супроводжують процедуру мовної збірки, зазвичай мають нижчий рівень "абстракції", ніж, наприклад, ця APL A←0⋄A⊣{2⊤⍵:1+3×⍵⋄⍵÷2}⍣{⍺=A+←1}⎕. Я думаю, це, мабуть, заслуговувало б коментаря щодо проблеми, яку вона має вирішити, гммм?


2

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

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

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

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

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


Саме те, про що я думав, коли я не погодився з твердженням "Людина може зрозуміти фрагмент англійської мови набагато швидше, що він / вона може зрозуміти фрагмент коду з тим самим значенням (поки операція не є тривіальною)" Код є завжди менш неоднозначно і більш лаконічно.
stephenbayer

0

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

f1 = (float)(f2+f3); // Force result to be rounded to single precision
f4 = f1-f2;

Ефект явного перенесення на «a» полягає floatв floatтому, щоб змусити результат округнути до однієї точності; таким чином коментар можна розглядати як просто те, що говорить, що робить код. З іншого боку, порівняйте цей код із:

thing.someFloatProperty = (float)(f2*0.1); // Divide by ten

Тут мета амплітуди - не допустити компілятора від задирання найефективнішим способом точного обчислення (f2 / 10) [це точніше, ніж множення на 0,1f, а на більшості машин - швидше, ніж ділення на 10,0f].

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


Я не впевнений, що J. Random програміст, дивлячись на другий приклад, зрозуміє, що константа пишеться 0,1 з поважної причини, а не тому, що оригінальний програміст забув набрати 'f'.
Девід К

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

@DavidK: Метою мого другого прикладу коду було порівняння його з першим фрагментом коду. У другому фрагменті коду намір програміста, ймовірно, має someFloatPropertyнайточніше представлення того, f2/10що він може; Основна мета другого складу - це просто зробити компіляцію коду . Однак у першому прикладі команда очевидно не потрібна для свого звичайного призначення (зміни одного типу компіляції на інший), оскільки операнди вже є float. Коментар служить ясно , що приведення в необхідному для вторинної мети (округлення).
supercat

Я погоджуюся з думкою, що вам не потрібно коментувати (float)акторський склад у другому прикладі. Питання стосується буквальної константи 0.1. Ви пояснили (у наступному абзаці тексту), чому б ми писали 0.1: "точніше, ніж помножити на 0,1f". Я пропоную , що ті слова , які повинні бути в коментарі.
Девід К

@DavidK: Я, безумовно, включу коментар, якби я знав, що 0,1f буде неприпустимо неточним, і використовував би 0,1f, якби я знав, що втрата точності буде прийнятною і що 0,1f насправді буде істотно швидше, ніж 0,1 . Якщо я не знаю, що жодне з цих речей є правдивим, я вважаю за краще, щоб моя звичка кодування полягала в застосуванні doubleдля констант або проміжних обчислень, значення яких не може бути представлене як float[хоча в мовах, які вимагають роздратованих явних подвійних плавців, лінь може бути застосовано використання floatконстант не для швидкості, а для мінімізації роздратування].
supercat

-1

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

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