Co jest złego w komentarzach wyjaśniających złożony kod?


236

Wiele osób twierdzi, że „komentarze powinny wyjaśniać„ dlaczego ”, ale nie„ jak ””. Inni twierdzą, że „kod powinien być samodokumentujący”, a komentarze powinny być rzadkie. Robert C. Martin twierdzi, że (przeformułowany na moje własne słowa) często „komentarze są przeprosinami za źle napisany kod”.

Moje pytanie jest następujące:

Co jest złego w wyjaśnianiu złożonego algorytmu lub długiego i skomplikowanego fragmentu kodu z komentarzem opisowym?

W ten sposób zamiast innych programistów (w tym ciebie), którzy muszą czytać cały algorytm wiersz po wierszu, aby dowiedzieć się, co on robi, mogą po prostu przeczytać przyjazny opisowy komentarz napisany zwykłym angielskim.

Angielski jest „zaprojektowany”, aby był łatwy do zrozumienia dla ludzi. Java, Ruby lub Perl zostały jednak zaprojektowane tak, aby zrównoważyć czytelność dla ludzi i czytelność komputera, tym samym zmniejszając czytelność tekstu dla ludzi. Człowiek może zrozumieć kawałek angielskiego znacznie szybciej, niż on / ona może zrozumieć fragment kodu o tym samym znaczeniu (o ile operacja nie jest trywialna).

Więc po napisaniu złożonego fragmentu kodu napisanego w języku programowania częściowo zrozumiałym dla człowieka, dlaczego nie dodać opisowego i zwięzłego komentarza wyjaśniającego działanie kodu w przyjaznym i zrozumiałym języku angielskim?

Niektórzy powiedzą: „kod nie powinien być trudny do zrozumienia”, „zmniejszaj funkcje”, „używaj opisowych nazw”, „nie pisz kodu spaghetti”.

Ale wszyscy wiemy, że to nie wystarczy. Są to jedynie wytyczne - ważne i przydatne - ale nie zmieniają faktu, że niektóre algorytmy są złożone. Dlatego trudno je zrozumieć, czytając je wiersz po wierszu.

Czy naprawdę tak źle jest wyjaśniać złożony algorytm z kilkoma liniami komentarzy na temat jego ogólnego działania? Co jest złego w wyjaśnianiu skomplikowanego kodu za pomocą komentarza?


14
Jeśli jest tak skomplikowany, spróbuj przefakturować go na mniejsze kawałki.
Vaughan Hilts

151
Teoretycznie nie ma różnicy między teorią a praktyką. W praktyce jest.
Scott Leadley,

5
@mattnz: bardziej bezpośrednio, kiedy piszesz komentarz, jesteś zanurzony w problemie, który rozwiązuje ten kod. Przy następnej wizycie będziesz miał mniejsze możliwości radzenia sobie z tym problemem .
Steve Jessop

26
„Co” robi funkcja lub metoda, powinno wynikać z nazwy. Jak to wynika z kodu. Dlaczego tak się dzieje, jakie domniemane założenia zostały zastosowane, które dokumenty należy przeczytać, aby zrozumieć algorytm itp. - powinny być w komentarzach.
SK-logic

11
Wydaje mi się, że wiele odpowiedzi poniżej celowo źle interpretuje twoje pytanie. Nie ma nic złego w komentowaniu kodu. Jeśli uważasz, że musisz napisać komentarz wyjaśniający, musisz.
Tony Ennis,

Odpowiedzi:


408

W kategoriach laika:

  • Nie ma nic złego w komentarzach per se. Niepoprawne jest pisanie kodu, który wymaga tego rodzaju komentarzy, lub założenie, że pisanie skomplikowanego kodu jest w porządku, pod warunkiem, że wyjaśnisz go przyjaznym językiem angielskim.
  • Komentarze nie aktualizują się automatycznie po zmianie kodu. Dlatego często komentarze nie są zsynchronizowane z kodem.
  • Komentarze nie ułatwiają testowania kodu.
  • Przeprosiny nie są złe. To, co zrobiłeś, wymaga przeprosin za (pisanie kodu, który nie jest łatwo zrozumiały) jest złe.
  • Programista, który potrafi pisać prosty kod w celu rozwiązania złożonego problemu, jest lepszy niż ten, który pisze złożony kod, a następnie pisze długi komentarz wyjaśniający, co robi jego kod.

Dolna linia:

Wyjaśnienie siebie jest dobre, lepiej nie trzeba tego robić.


91
Często usprawiedliwienie wydawania kodu przepisywania pieniędzy przez pracodawcę jest bardziej zrozumiałe, gdy dobry komentarz może wykonać zadanie w znacznie krótszym czasie. Sumienny programista musi za każdym razem posługiwać się swoją oceną.
aecolley

34
@ aecolley Pisanie intuicyjnego kodu na początek jest jeszcze lepsze.
Tulains Córdova

127
Czasami zrozumiały kod nie jest wystarczająco wydajny, aby rozwiązać problem z dzisiejszym HW&SW. A logika biznesowa jest notorycznie ... kręta. Podzbiór problemów z eleganckimi rozwiązaniami programowymi jest znacznie mniejszy niż zestaw problemów, które są ekonomicznie użyteczne do rozwiązania.
Scott Leadley,

62
@rwong: i odwrotnie, często piszę więcej komentarzy w logice biznesowej, ponieważ ważne jest, aby dokładnie pokazać, jak kod jest zgodny z podanymi wymaganiami: „jest to linia, która uniemożliwia nam wszystkim pójście do więzienia za oszustwo za drutem w sekcji Cokolwiek kodeks karny ”. Jeśli jest to tylko algorytm, programista może zorientować się, jaki jest cel od zera, jeśli to absolutnie konieczne. Do logiki biznesowej potrzebny jest prawnik i klient w tym samym pokoju w tym samym czasie. Być może mój „zdrowy rozsądek” należy do innej domeny niż przeciętny programista aplikacji ;-)
Steve Jessop

29
@ user61852 Tyle, że to, co jest oczywiste dla ciebie, który właśnie napisałeś ten kod i spędziłeś w nim ostatni $ okres, może nie być samoobjaśniające dla ciebie, który musi go utrzymać lub edytować za pięć lat, nie mówiąc już o wszystkich potencjalni ludzie, którzy nie są tobą, mogą na to spojrzeć. „Samoobjaśniające” to mglisty święty graal definicji.
Shadur

110

Istnieje wiele różnych powodów, dla których kod może być skomplikowany lub mylący. Do najczęstszych przyczyn są najlepiej zająć refaktoryzacji kodu, aby to mniej skomplikowane, nie dodając komentarze wszelkiego rodzaju.

Są jednak przypadki, w których dobrze wybrany komentarz jest najlepszym wyborem.

  • Jeśli to sam algorytm jest skomplikowany i mylący, a nie tylko jego implementacja - taka, która jest zapisywana w czasopismach matematycznych i jest dalej określana jako Algorytm Mbogo - wtedy umieszczasz komentarz na samym początku implementacji, czytając coś w rodzaju „To jest algorytm Mbogo do ponownego tworzenia widżetów, pierwotnie opisany tutaj: [URL papieru]. Ta implementacja zawiera udoskonalenia Alice i Carol [URL innego papieru]”. Nie próbuj wchodzić w więcej szczegółów; jeśli ktoś potrzebuje więcej szczegółów, prawdopodobnie musi przeczytać cały artykuł.

  • Jeśli wziąłeś coś, co można zapisać jako jeden lub dwa wiersze w jakiejś specjalnej notacji, i rozwinąłeś go do dużego globu kodu rozkazującego, umieszczenie jednego lub dwóch wierszy specjalnej notacji w komentarzu nad funkcją jest dobrym sposobem na powiedz czytelnikowi, co powinien zrobić. Jest to wyjątek z „ale co jeśli komentarz wysiada zsynchronizowany z kodem” argumentu, ponieważ wyspecjalizowane notacja jest chyba o wiele łatwiej znaleźć błędy w niż kod. (Odwrotnie, jeśli zamiast tego napisałeś specyfikację w języku angielskim.) Dobry przykład znajduje się tutaj: 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
    
  • Jeśli kod jest ogólnie prosty, ale zawiera jedną lub dwie rzeczy, które wyglądają na zbyt skomplikowane, niepotrzebne lub po prostu źle, ale muszą być w ten sposób z przyczyn, wówczas umieszczasz komentarz bezpośrednio nad podejrzanie wyglądającym fragmentem, w którym podajesz powody . Oto prosty przykład, w którym jedyne, co wymaga wyjaśnienia, to dlaczego stała ma określoną wartość.

    /* 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
To oburzenie, 4powinno być CHAR_BIT / 2;-)
Steve Jessop

@SteveJessop: Czy cokolwiek wykluczałoby implementację, w której CHAR_BITSbyło 16, a sizeof (size_t) wynosiła 2, ale maksymalna wartość size_t wynosiła np. 2 ^ 20 [size_t zawierający 12 bitów wypełniających]?
supercat

2
@ superuper Nie widzę niczego, co oczywiście wykluczałoby to w C99, co oznacza, że ​​ten przykład jest technicznie niepoprawny. Zdarza się, że pochodzi z (nieco zmodyfikowanej wersji) OpenBSD reallocarray, a OpenBSD ogólnie nie wierzy w zaspokojenie możliwości, które nie występują w ich ABI.
zwolnić

3
@Zack: Jeśli kod jest zaprojektowany zgodnie z założeniami POSIX, użycie CHAR_BITS może sprawiać wrażenie, że kod może działać z wartościami innymi niż 8.
supercat

2
@Zack: Aby przydatne były nieoznaczone typy o dokładnej szerokości, ich semantyka musiałaby zostać zdefiniowana niezależnie od wielkości int. W tej uint32_t x,y,z;chwili znaczenie (x-y) > zzależy od wielkości int. Co więcej, język zaprojektowany do pisania solidnego kodu powinien pozwolić programistom na rozróżnienie między typem, w którym oczekuje się, że obliczenia przekroczą zakres tego typu i powinien po cichu zawinąć, w przeciwieństwie do jednego, w którym obliczenia przekraczające zakres tego typu powinny się zatrzymać, w porównaniu z tym, w którym obliczenia nie powinny przekraczać zakresu tego typu, ale ...
supercat

61

Więc co jest złego w wyjaśnianiu skomplikowanego kodu komentarzem?

To nie jest kwestia dobra czy zła, ale „najlepszej praktyki”, zgodnie z definicją w artykule na Wikipedii :

Najlepszą praktyką jest metoda lub technika, która konsekwentnie wykazuje lepsze wyniki niż te uzyskane innymi środkami, i która jest stosowana jako punkt odniesienia.

Najlepszą praktyką jest więc najpierw poprawienie kodu i użycie języka angielskiego, jeśli nie jest to możliwe.

To nie jest prawo, ale o wiele bardziej powszechne jest znajdowanie skomentowanego kodu, który wymaga refaktoryzacji, niż kod refaktoryzowany, który wymaga komentarzy, najlepsza praktyka to odzwierciedla.


42
+1 dla „jest o wiele bardziej powszechne znajdowanie skomentowanego kodu wymagającego refaktoryzacji niż refaktoryzowanego kodu wymagającego komentarzy”
Brandon

7
Okej, ale jak często ten komentarz: //This code seriously needs a refactor
Erik Reppen

2
Oczywiście każda tak zwana najlepsza praktyka niepoparta rygorystycznymi badaniami naukowymi jest jedynie opinią.
Blrfl,

54

Nadejdzie dzień, w którym twój piękny, perfekcyjnie wykonany, dobrze skonstruowany i czytelny kod nie zadziała. Albo to nie zadziała wystarczająco dobrze. Lub pojawi się szczególny przypadek, w którym nie działa i wymaga dostosowania.

W tym momencie musisz zrobić coś, co zmieni rzeczy, aby działało poprawnie. W szczególności w przypadku problemów z wydajnością, ale także często w scenariuszach, w których jedna z bibliotek, interfejsów API, usług internetowych, klejnotów lub systemów operacyjnych, z którymi pracujesz, nie działa zgodnie z oczekiwaniami, możesz w końcu zaproponować sugestie, które nie są z konieczności nieeleganckie, ale są sprzeczne z intuicją lub nieoczywiste.

Jeśli nie masz żadnych komentarzy wyjaśniających, dlaczego wybrałeś to podejście, istnieje bardzo duża szansa, że ​​ktoś w przyszłości (i że ktoś może nawet być tobą) spojrzy na kod, zobacz, jak można to „naprawić” coś bardziej czytelnego i eleganckiego i niechcący cofnie poprawkę, ponieważ nie wygląda jak poprawka.

Gdyby każdy zawsze pisał idealny kod, byłoby oczywiste, że kod, który wygląda niedoskonale, działa w oparciu o jakąś trudną interwencję ze świata rzeczywistego, ale nie tak to działa. Większość programistów często pisze mylący lub nieco splątany kod, więc kiedy go napotykamy, naturalną skłonnością jest jego uporządkowanie. Przysięgam, że moje poprzednie ja jest prawdziwym idiotą, ilekroć czytam stary kod, który napisałem.

Nie uważam więc komentarzy za przeprosiny za zły kod, ale może za wyjaśnienie, dlaczego nie zrobiłeś rzeczy oczywistej. Dzięki // The standard approach doesn't work against the 64 bit version of the Frobosticate Librarytemu przyszli programiści, w tym twoi przyszli ja, będą mogli zwracać uwagę na tę część kodu i testować tę bibliotekę. Jasne, możesz umieścić komentarze również w swoich zatwierdzeniach kontroli źródła, ale ludzie będą na nie patrzeć tylko wtedy, gdy coś pójdzie nie tak. Będą czytać komentarze do kodu, gdy zmieniają kod.

Ludzie, którzy mówią nam, że zawsze powinniśmy pisać teoretycznie idealny kod, nie zawsze są ludźmi z dużym doświadczeniem w programowaniu w rzeczywistych środowiskach. Czasem trzeba napisać kod, który działa do pewnego poziomu, czasem trzeba współpracować z niedoskonałymi systemami. Nie oznacza to, że nie możesz tego zrobić w elegancki i dobrze napisany sposób, ale nieoczywiste rozwiązania wymagają wyjaśnienia.

Kiedy piszę kod dla projektów hobbistycznych, o których wiem, że nikt nigdy nie przeczyta, wciąż komentuję części, które są dla mnie mylące - na przykład każda geometria 3D obejmuje matematykę, z którą nie jestem w pełni w domu - ponieważ wiem, kiedy wrócę za sześć miesięcy całkowicie zapomnę, jak to zrobić. To nie jest przeprosiny za zły kod, to potwierdzenie osobistego ograniczenia. Jedyne, co bym zrobił, pozostawiając to bez komentarza, to stworzyć dla siebie więcej pracy w przyszłości. Nie chcę, aby moje przyszłe ja musiało nauczyć się czegoś niepotrzebnie, jeśli mogę tego teraz uniknąć. Jaka miałaby to wartość?


5
@Christian to jest? Pierwsza linia odnosi się do tego stwierdzenia, oczywiście, ale poza tym jest nieco szersza, jak rozumiem.
glenatron

9
„Przysięgam, że moje poprzednie ja jest prawdziwym idiotą za każdym razem, gdy czytam stary kod, który napisałem”. Cztery lata w mojej karierze programistycznej i zdarza mi się, że zdarza się to za każdym razem, gdy patrzę na coś starszego niż 6 miesięcy.
Ken

6
W wielu przypadkach najbardziej pouczające i użyteczne informacje historyczne dotyczą rzeczy, które są rozważane, ale których się nie podejmuje. W wielu przypadkach ktoś wybiera podejście X do czegoś, a inne podejście Y wydaje się lepsze; w niektórych przypadkach Y „prawie” działa lepiej niż X, ale okazuje się, że ma pewne problemy nie do pokonania. Jeśli Y uniknięto z powodu tych problemów, taka wiedza może pomóc zapobiec marnowaniu czasu na nieudane próby wdrożenia podejścia Y.
supercat

4
Na co dzień często używam komentarzy w toku - nie są tam na dłuższą metę, ale umieszczenie notatek do zrobienia lub krótkiej sekcji przypominającej mi, co zamierzam zrobić, może być przydatne przypomnienie rano.
glenatron

1
@Lilienthal, nie sądzę, że ostatni para ogranicza się do osobistych projektów - powiedział „… Nadal komentuję części, które uważam za mylące”.
Wildcard

29

Potrzeba komentarzy jest odwrotnie proporcjonalna do poziomu abstrakcji kodu.

Na przykład język asemblera jest w większości praktycznych niezrozumiały bez komentarzy. Oto fragment małego programu, który oblicza i drukuje terminy z serii Fibonacciego :

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             ;

Nawet z komentarzami grok może być dość skomplikowany.

Nowoczesny przykład: Regeksy to często konstrukcje o bardzo niskiej abstrakcji (małe litery, cyfry 0, 1, 2, nowe linie itp.). Prawdopodobnie potrzebują komentarzy w formie próbek (Bob Martin, IIRC, potwierdza to). Oto regex, który (myślę) powinien pasować do adresów URL HTTP (S) i 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;%\$#\=~_\-]+))*$

W miarę postępu języków w hierarchii abstrakcji programiści mogą używać sugestywnych abstrakcji (nazwa zmiennej, nazwy funkcji, nazwy klas, nazwy modułów, interfejsy, wywołania zwrotne itp.) W celu zapewnienia wbudowanej dokumentacji. Zaniedbanie skorzystania z tego i wykorzystanie komentarzy na papierze jest leniwe, szkoda i brak szacunku dla opiekuna.

Mam na myśli Numerical Recipes in C tłumaczone głównie verbatim do Numerical Recipes in C ++ , co wnoszę rozpoczął jako Numerical Recipes (w Fortan), ze wszystkimi zmiennymi a, aa, b, c, cc, itd utrzymywany przez każdej wersji. Algorytmy mogły być poprawne, ale nie korzystały z abstrakcji podanych języków. I odpierdolili mnie. Próbka z artykułu dr Dobbs - Szybka transformata Fouriera :

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;
    }
}

Jako szczególny przypadek dotyczący abstrakcji, każdy język ma idiomy / fragmenty kodu kanonicznego do niektórych typowych zadań (usuwanie dynamicznie połączonej listy w C) i bez względu na to, jak wyglądają, nie powinny być dokumentowane. Programiści powinni nauczyć się tych idiomów, ponieważ są nieoficjalnie częścią języka.

Tak więc na wynos: nieidiomatyczny kod zbudowany z bloków niskiego poziomu, których nie można uniknąć, wymaga komentarzy. A to jest konieczne WAŻSZE mniej niż to się dzieje.


1
Nikt nie powinien być naprawdę napisanie wiersza takiego w asemblerze: dec dword [term] ; decrement loop counter. Z drugiej strony brakuje tego w języku asemblera komentarz przed każdym „akapitem kodu” wyjaśniającym, co robi następny blok kodu. W takim przypadku komentarz byłby zwykle równoważny pojedynczej linii w pseudokodzie, takiej jak ;clear the screen, po której następuje 7 linii, których faktycznie zajmuje wyczyszczenie ekranu.
Scott Whitlock,

1
Tak, w zbiorze montażowym znalazłem coś, co uważam za niepotrzebne komentarze, ale szczerze mówiąc, jest to dość reprezentatywne dla stylu „dobrego” montażu. Nawet przy prologu z jednym lub dwoma wierszami kod byłby naprawdę trudny do naśladowania. Zrozumiałem próbkę ASM lepiej niż przykład FFT. Zaprogramowałem FFT w C ++ w szkole, i nie wyglądało to tak, ale potem używaliśmy STL, iteratorów, funktorów i kilku wywołań metod. Nie tak szybki jak funkcja monolityczna, ale o wiele łatwiejszy do odczytania. Spróbuję dodać go do kontrastu z próbką NRinC ++.
Kristian H

Miałeś na myśli ^(((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\.\,\;\?\'\\\+&%\$#\=~_\-]+))*$? Uważaj na adresy numeryczne.
izabera

Mniej więcej mój punkt widzenia: niektóre rzeczy zbudowane z bardzo abstrakcyjnych poziomów nie są łatwe do odczytania lub weryfikacji. Komentarze (i, aby nie zejść zbyt daleko, TESTY) mogą być przydatne, a nie szkodzić. Jednocześnie niestosowanie dostępnych abstrakcji wyższego poziomu (: alpha:: num: gdzie dostępne) utrudnia zrozumienie, nawet przy dobrych komentarzach, niż używanie abstrakcji wyższego poziomu.
Kristian H

3
+1: "The need for comments is inversely proportional to the abstraction level of the code." Prawie wszystko podsumowuje.
Gerrat

21

Nie sądzę, aby w komentarzach w kodzie było coś nie tak. Pomysł, że komentarze są w jakiś sposób złe, moim zdaniem wynika z tego, że niektórzy programiści posunęli się za daleko. W tej branży jest dużo modnego grania, szczególnie w skrajnych poglądach. Gdzieś po drodze skomentowany kod stał się ekwiwalentem złego kodu i nie jestem pewien, dlaczego.

Komentarze mają problemy - musisz je aktualizować podczas aktualizacji kodu, do którego się odnoszą, co zdarza się zdecydowanie zbyt rzadko. Wiki lub coś takiego jest bardziej odpowiednim zasobem do dokładnej dokumentacji twojego kodu. Twój kod powinien być czytelny bez konieczności komentowania. Uwagi dotyczące kontroli wersji lub zmiany powinny być tam, gdzie opisujesz dokonane zmiany kodu.

Żadne z powyższych nie unieważnia jednak używania komentarzy. Nie żyjemy w idealnym świecie, więc jeśli którakolwiek z powyższych przyczyn zawiedzie z jakiegokolwiek powodu, wolałbym, aby niektóre komentarze zostały odrzucone.


18

Myślę, że trochę za dużo czytasz w tym, co mówi. Twoja skarga składa się z dwóch części:

Co jest złego w wyjaśnianiu (1) złożonego algorytmu lub (2) długiego i skomplikowanego fragmentu kodu z komentarzem opisowym?

(1) jest nieuniknione. Nie sądzę, żeby Martin się z tobą nie zgodził. Jeśli piszesz coś w rodzaju szybkiego odwrotnego pierwiastka kwadratowego , będziesz potrzebować komentarzy, nawet jeśli jest to po prostu „hakowanie złego zmiennoprzecinkowego poziomu”. Z wyjątkiem czegoś prostego, takiego jak DFS lub wyszukiwanie binarne, mało prawdopodobne jest, aby osoba czytająca Twój kod miała doświadczenie z tym algorytmem, więc myślę, że w komentarzach na ten temat powinna być przynajmniej wzmianka.

Jednak większość kodu nie jest (1). Rzadko napiszesz oprogramowanie, które jest niczym innym jak ręcznie walcowanymi implementacjami mutex, niejasnymi operacjami algebry liniowej przy słabym wsparciu biblioteki i nowymi algorytmami znanymi tylko grupie badawczej Twojej firmy. Większość kodu składa się z wywołań biblioteki / frameworka / API, IO, płyty głównej i testów jednostkowych.

Jest to rodzaj kodu, o którym mówi Martin. I odpowiada na twoje pytanie cytatem z Kernighana i Plaughera na górze rozdziału:

Nie komentuj złego kodu - przepisz go.

Jeśli masz długie, skomplikowane sekcje w kodzie, nie udało ci się utrzymać kodu w czystości . Najlepszym rozwiązaniem tego problemu nie jest napisanie komentarza o długości akapitu w górnej części pliku, aby pomóc przyszłym programistom w jego rozwiązaniu; najlepszym rozwiązaniem jest przepisanie go.

I dokładnie to mówi Martin:

Właściwe stosowanie komentarzy ma na celu zrekompensowanie naszego braku wyrażenia się w kodzie ... Komentarze są zawsze błędami. Musimy je mieć, ponieważ nie zawsze możemy dowiedzieć się, jak wyrazić się bez nich, ale ich użycie nie jest powodem do świętowania.

To jest twój (2). Martin zgadza się, że długi, zawiły kod wymaga komentarzy - ale winę za ten kod ponosi na barkach programisty, który go napisał, a nie jakiś mglisty pomysł, że „wszyscy wiemy, że to nie wystarczy”. Twierdzi, że:

Przejrzysty i wyrazisty kod z kilkoma komentarzami jest znacznie lepszy od zaśmieconego i złożonego kodu z dużą ilością komentarzy. Zamiast spędzać czas na pisaniu komentarzy wyjaśniających bałagan, który popełniłeś, poświęć go czyszczeniu bałaganu.


3
Gdyby programista, z którym pracowałem, po prostu napisał „hakowanie złego zmiennoprzecinkowego poziomu bitowego” w celu wyjaśnienia algorytmu szybkiego pierwiastka kwadratowego - rozmawiali ze mną. Tak długo, jak zawierają odniesienie do czegoś bardziej użytecznego, byłbym szczęśliwy.
Michael Anderson

8
Nie zgadzam się w jeden sposób - komentarz wyjaśniający, jak działa coś złego, jest o wiele szybszy. Biorąc pod uwagę kod, który prawdopodobnie nie zostanie ponownie dotknięty (chyba większość kodu), wtedy komentarz jest lepszym rozwiązaniem biznesowym niż duże refaktoryzowanie, które często wprowadza błędy (ponieważ poprawka, która zabija błąd polegający na błędzie, nadal jest błędem). Idealny świat doskonale zrozumiałego kodu nie jest dla nas dostępny.
gbjbaanb

2
@trysis haha, tak, ale w świecie, w którym programiści są odpowiedzialni, a nie biznesmeni, nigdy nie wysyłają, ponieważ na zawsze pozłacają stale odnowioną bazę kodu w próżnym poszukiwaniu doskonałości.
gbjbaanb

4
@PatrickCollins prawie wszystko, co czytam w Internecie, dotyczy robienia tego dobrze za pierwszym razem. Prawie nikt nie chce pisać artykułów o naprawianiu bałaganu! Fizycy mówią „biorąc pod uwagę idealną kulę ...” Naukowcy twierdzą „biorąc pod uwagę rozwój od podstaw ...”
gbjbaanb

2
Najlepszym rozwiązaniem jest przepisanie go w nieskończonym czasie; ale biorąc pod uwagę czyjąś bazę kodu, typowe terminy korporacyjne i rzeczywistość; czasami najlepiej jest komentować, dodać TODO: Refaktor i wprowadzić ten refaktor do następnej wersji; i ta poprawka, która musiała zostać wykonana wczoraj, wykonana teraz. Rzecz w tej idealistycznej rozmowie o refaktoryzacji polega na tym, że nie bierze ona pod uwagę tego, jak rzeczy naprawdę działają w miejscu pracy; czasami istnieją wyższe priorytety i wkrótce wystarczające terminy, które zapobiegną ustaleniu starszego kodu niskiej jakości. Tak to jest.
hsanders

8

Co jest złego w wyjaśnianiu złożonego algorytmu lub długiego i skomplikowanego fragmentu kodu z komentarzem opisowym?

Nic jako takiego. Dokumentowanie swojej pracy to dobra praktyka.

To powiedziawszy, masz tutaj fałszywą dychotomię: pisanie czystego kodu kontra pisanie udokumentowanego kodu - nie ma między nimi sprzeciwu.

Na czym powinieneś się skupić, to uprościć i wyodrębnić złożony kod w prostszy, zamiast myśleć „złożony kod jest w porządku, o ile jest komentowany”.

W idealnym przypadku kod powinien być prosty i udokumentowany.

W ten sposób zamiast innych programistów (w tym ciebie), którzy muszą czytać cały algorytm wiersz po wierszu, aby dowiedzieć się, co on robi, mogą po prostu przeczytać przyjazny opisowy komentarz napisany zwykłym angielskim.

Prawdziwe. Właśnie dlatego wszystkie publiczne algorytmy API powinny zostać wyjaśnione w dokumentacji.

Więc po napisaniu złożonego fragmentu kodu napisanego w języku programowania częściowo zrozumiałym dla człowieka, dlaczego nie dodać opisowego i zwięzłego komentarza wyjaśniającego działanie kodu w przyjaznym i zrozumiałym języku angielskim?

Idealnie, po napisaniu złożonego fragmentu kodu powinieneś (nie wyczerpująca lista):

  • uważają go za wersję roboczą (tj. planują go ponownie napisać)
  • sformalizować punkty wejścia algorytmu / interfejsy / role / etc (analizować i optymalizować interfejs, formalizować abstrakcje, warunki wstępne dokumentu, warunki dodatkowe i skutki uboczne oraz przypadki błędów dokumentu).
  • pisać testy
  • porządki i refaktoryzacja

Żaden z tych kroków nie jest trywialny (tzn. Każdy może potrwać kilka godzin), a nagrody za wykonanie go nie są natychmiastowe. W związku z tym kroki te są (prawie) zawsze zagrożone (przez deweloperów skracających rogi, menedżerów skracających rogi, terminy, ograniczenia rynkowe / inne realne warunki, brak doświadczenia itp.).

[...] niektóre algorytmy są złożone. Dlatego trudno je zrozumieć, czytając je wiersz po wierszu.

Nigdy nie powinieneś polegać na czytaniu implementacji, aby dowiedzieć się, co robi interfejs API. Kiedy to robisz, implementujesz kod klienta na podstawie implementacji (zamiast interfejsu), a to oznacza, że ​​twoje połączenie modułów jest już piekłem, potencjalnie wprowadzasz nieudokumentowane zależności z każdym nowym wierszem kodu, który piszesz, i są już dodaje dług techniczny.

Czy naprawdę tak źle jest wyjaśniać złożony algorytm z kilkoma liniami komentarzy na temat jego ogólnego działania?

Nie - to dobrze. Dodanie kilku wierszy komentarzy nie wystarczy.

Co jest złego w wyjaśnianiu skomplikowanego kodu za pomocą komentarza?

Fakt, że nie powinieneś mieć skomplikowanego kodu, jeśli można tego uniknąć.

Aby uniknąć skomplikowanego kodu, sformalizuj swoje interfejsy, wydaj ~ 8 razy więcej na projekt interfejsu API niż na implementację (Stepanov zasugerował, aby wydać co najmniej 10x na interfejs w porównaniu z implementacją), i przejdź do opracowania projektu ze świadomością, że tworzysz projekt, a nie tylko piszesz jakiś algorytm.

Projekt obejmuje dokumentację API, dokumentację funkcjonalną, pomiary kodu / jakości, zarządzanie projektem i tak dalej. Żaden z tych procesów nie jest jednorazowym, szybkim krokiem do wykonania (wszystkie wymagają czasu, wymagają przemyślenia i planowania, a wszystkie wymagają okresowego powrotu do nich i przeglądu / uzupełnienia ich szczegółami).


3
„Nigdy nie powinieneś polegać na czytaniu implementacji, aby dowiedzieć się, co robi interfejs API”. Czasami jest to narzucone przez upstream, którego używasz. Miałem szczególnie niezadowalający projekt wypełniony komentarzami w formie „następujący brzydki kod Heatha Robinsona istnieje, ponieważ simpleAPI () nie działa poprawnie na tym sprzęcie pomimo tego, co twierdzi sprzedawca”.
pjc50,

6

zamiast innych programistów (w tym ciebie), którzy muszą czytać cały algorytm wiersz po wierszu, aby dowiedzieć się, co on robi, mogą po prostu przeczytać przyjazny opisowy komentarz napisany zwykłym angielskim.

Uznałbym to za niewielkie nadużycie „komentarzy”. Jeśli programista chce przeczytać coś zamiast całego algorytmu, to po to jest dokumentacja funkcji. OK, więc dokumentacja funkcji może faktycznie pojawiać się w komentarzach w źródle (być może do wyodrębnienia za pomocą narzędzi doc), ale chociaż syntaktycznie jest to komentarz w odniesieniu do twojego kompilatora, powinieneś rozważyć je osobno dla różnych celów. Nie uważam, że „komentarze powinny być rzadkie” ma oznaczać „dokumentacja powinna być rzadka”, a nawet „prawa autorskie powinny być rzadkie”!

Komentarze w funkcji są przeznaczone dla kogoś do przeczytania, a także do kodu. Jeśli więc masz kilka wierszy w kodzie, które są trudne do zrozumienia i nie możesz ich ułatwić, to komentarz jest użyteczny dla czytelnika jako symbol zastępczy dla tych wierszy. Może to być bardzo przydatne, gdy czytelnik próbuje po prostu uzyskać ogólną treść, ale istnieje kilka problemów:

  • Komentarze niekoniecznie są prawdziwe, podczas gdy kod robi to, co robi. Czytelnik wierzy ci na słowo, a to nie jest idealne.
  • Czytelnik jeszcze nie rozumie samego kodu, więc dopóki nie wrócą do niego później, nadal nie są uprawnieni do modyfikowania go lub ponownego użycia. W takim razie co oni robią, czytając to?

Istnieją wyjątki, ale większość czytelników będzie musiała zrozumieć sam kod. Komentarze powinny być napisane, aby pomóc, a nie zastępować, dlatego ogólnie zaleca się, aby komentarze mówiły „dlaczego to robisz”. Czytelnik, który zna motywację do wykonania kilku kolejnych wierszy kodu, ma większą szansę zobaczyć, co robią i jak.


5
Jedno przydatne miejsce na komentarze: w kodzie naukowym często można wykonywać obliczenia, które są dość złożone i obejmują wiele zmiennych. Dla rozsądku programisty rozsądne jest, aby nazwy zmiennych były naprawdę krótkie, abyś mógł patrzeć na matematykę, a nie na nazwy. Ale to naprawdę utrudnia zrozumienie dla czytelnika. Tak więc krótki opis tego, co się dzieje (lub lepiej, odniesienie do równania w artykule w czasopiśmie lub podobnym), może być naprawdę pomocny.
naught101

1
@ naught101: tak, zwłaszcza, że ​​w referacie, do którego się odnosisz, prawdopodobnie używane są również jednoliterowe nazwy zmiennych. Zwykle łatwiej jest zauważyć, że kod rzeczywiście podąża za tekstem, jeśli używasz tych samych nazw, ale jest to sprzeczne z celem, który polega na tym, że kod nie wymaga wyjaśnień ( zamiast tego jest wyjaśniony w artykule ). W tym przypadku komentarz, w którym każda nazwa jest zdefiniowana, mówiąc, co to właściwie oznacza, zastępuje sensowne nazwy.
Steve Jessop

1
Kiedy szukam czegoś konkretnego w kodzie (gdzie jest obsługiwany ten konkretny przypadek?), Nie chcę czytać i rozumieć paragrafów kodu, aby odkryć, że to jednak nie jest to miejsce. Potrzebuję komentarzy podsumowujących w jednym wierszu, co robi następny akapit. W ten sposób szybko zlokalizuję części kodu związane z moim problemem i pominie nieciekawe szczegóły.
Florian F

1
@FlorianF: tradycyjną odpowiedzią jest to, że nazwy zmiennych i funkcji powinny z grubsza wskazywać, o co chodzi w kodzie, a zatem pozwalają przeglądać rzeczy, które z pewnością nie dotyczą tego, czego szukasz. Zgadzam się z tobą, że to nie zawsze się udaje, ale nie zgadzam się tak mocno, że uważam, że cały kod musi zostać skomentowany, aby pomóc w wyszukiwaniu lub czytaniu. Ale masz rację, jest to przypadek, w którym ktoś czyta twój kod (w pewnym sensie) i nie musi go rozumieć.
Steve Jessop,

2
@Snowman Ludzie mogą to robić przy użyciu nazw zmiennych. Widziałem kod, w którym zmienna listOfApples zawierała listę bananów. Ktoś skopiował kod przetwarzający listę Jabłek i dostosował go do Bananów, nie zawracając sobie głowy zmianą nazw zmiennych.
Florian F

5

Często musimy robić skomplikowane rzeczy. Z pewnością należy je udokumentować w celu przyszłego zrozumienia. Czasami właściwym miejscem dla tej dokumentacji jest kod, w którym dokumentacja może być aktualizowana za pomocą kodu. Ale zdecydowanie warto rozważyć osobną dokumentację. Może to być łatwiejsze do zaprezentowania innym osobom, w tym schematów, kolorowych zdjęć i tak dalej. Zatem komentarz jest po prostu:

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

a nawet po prostu

void doPRD239Algorithm() { ...

Z pewnością ludzie są zadowoleni z funkcji o nazwie MatchStringKnuthMorrisPrattlub encryptAESlub partitionBSP. Bardziej niejasne nazwy są warte wyjaśnienia w komentarzu. Możesz także dodać dane bibliograficzne i link do artykułu, z którego zaimplementowałeś algorytm.

Jeśli algorytm jest złożony, nowatorski i nieoczywisty, zdecydowanie jest wart dokumentu, nawet jeśli tylko do wewnętrznego obiegu firmy. Sprawdź dokument w celu kontroli źródła, jeśli martwisz się, że go zgubisz.

Istnieje inna kategoria kodu, która nie jest tak algorytmiczna jak biurokratyczna. Musisz skonfigurować parametry dla innego systemu lub współpracować z cudzymi błędami:

/* 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
Argumentowałbym przeciwko nazwaniu funkcji / metod po ich wewnętrznym algorytmie, przez większość czasu stosowana metoda powinna być przedmiotem wewnętrznej troski, za wszelką cenę udokumentuj górną część swojej funkcji za pomocą zastosowanej metody, ale nie nazywaj tego, doPRD239Algorithmco mi mówi nic o funkcji bez konieczności szukania algorytmu, powodem MatchStringKnuthMorrisPratti encryptAESpracy jest to, że zaczynają się od opisu tego, co robią, a następnie opisują metodologię.
scragar

5

I zapomnieć, gdzie czytałem, ale tam jest ostra i wyraźna linia pomiędzy tym, co powinno pojawić się w kodzie i co powinien pojawić się jako komentarz.

Uważam, że powinieneś komentować swoje zamiary, a nie algorytm . Tj skomentować to, co masz na myśli to zrobić, a nie na to, co robisz .

Na przykład:

// 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);
  }
}

Tutaj nie ma próby określenia, co wykonuje każdy krok, wszystko, co mówi, to, co ma zrobić.

PS: Znalazłem źródło, o którym mówiłem - Coding Horror: Code Tells How How, Comments Tell You Why


8
Pierwszy komentarz: czy już działa? Co już działa? To samo dotyczy innych komentarzy. Dla kogoś, kto nie wie, co robi kod, jest to bezużyteczne.
gnasher729,

1
@ gnasher729 - Usunięty z kontekstu prawie każdy komentarz będzie bezużyteczny - ten kod jest demonstracją dodawania komentarzy, które wskazują zamiary, a nie próby opisania . Przykro mi, że to nic dla ciebie nie robi.
OldCurmudgeon

2
Opiekun tego kodu nie będzie miał kontekstu. Nie jest szczególnie trudno zrozumieć, co robi kod, ale komentarze nie pomagają. Jeśli piszesz komentarze, nie spiesz się i skoncentruj się na ich pisaniu.
gnasher729,

BTW - komentarz Has it run yet odnosi się do Futurei wskazuje, że get()po sprawdzeniu przeciwko nullwykrywa się, czy Futurejuż został uruchomiony - poprawnie dokumentując zamiar, a nie proces .
OldCurmudgeon

1
@OldCurmudgeon: Twoja odpowiedź jest wystarczająco zbliżona do tego, co myślałem, że dodam ten komentarz jako przykład twojego zdania. Chociaż komentarz nie jest potrzebny do wyjaśnienia czystego kodu, komentarz JEST dobry, aby wyjaśnić, dlaczego kodowanie zostało wykonane JEDEN SPOSÓB NAD INNYM. Z mojego ograniczonego doświadczenia, komentarze są często przydatne do wyjaśnienia osobliwości zbioru danych, nad którym pracuje kod, lub reguł biznesowych, które kod ma egzekwować. Kod komentujący dodany w celu naprawienia błędu jest dobrym przykładem, jeśli błąd wystąpił, ponieważ założenie dotyczące danych było błędne.
Randall Stewart

4

Ale wszyscy wiemy, że to nie wystarczy.

Naprawdę? Od kiedy?

Dobrze zaprojektowany kod z dobrymi nazwami w większości przypadków jest wystarczający. Argumenty przeciwko używaniu komentarzy są dobrze znane i udokumentowane (jak się odwołujesz).

Ale są to wytyczne (jak wszystko inne). W rzadkim przypadku (z mojego doświadczenia, mniej więcej raz na 2 lata), w którym sytuacja byłaby gorsza, gdyby przekształciła się w mniejsze czytelne funkcje (ze względu na potrzeby w zakresie wydajności lub spójności), to kontynuuj - dodaj długi komentarz wyjaśniający, co to właściwie jest robiąc (i dlaczego naruszasz najlepsze praktyki).


7
Wiem, że to nie wystarczy.
Florian F

2
Od kiedy? Najwyraźniej znasz już odpowiedź na to pytanie. „W większości przypadków dobrze zaprojektowany kod z dobrymi nazwami to więcej niż wystarczające ”. Prawdopodobnie nie wystarcza to w mniejszości przypadków, a dokładnie o to pyta pytający.
Ellesedil

3
Zawsze próbuję rozszyfrować kod innych ludzi, do których chciałbym dodawać komentarze więcej niż raz na dwa lata.
Ogre Psalm33

@ OgrePsalm33 - Czy mają małe metody i używają dobrych nazwisk? Zły kod jest zły, niezależnie od komentarzy.
Telastyn

2
@Telastyn Niestety, podczas pracy na dużej bazie kodu, „małe” metody i „dobre” nazwy są subiektywne dla każdego programisty (więc jest to dobry komentarz, jeśli o to chodzi). Deweloper piszący kod algorytmu graficznego przetwarzania Flarbigan przez 7 lat może napisać coś doskonale dla niego i podobnych programistów, ale byłby tajemniczy dla nowego faceta, który spędził ostatnie 4 lata na tworzeniu kodu infrastruktury sieci Perbian. Następnie, 2 tygodnie później, ekspert Flarbigan rezygnuje.
Ogre Psalm33

2

Głównym celem kodu jest polecenie komputerowi zrobienia czegoś, więc dobry komentarz nigdy nie zastąpi dobrego kodu, ponieważ komentarzy nie można wykonać.

To powiedziawszy, komentarze w źródle są jedną z form dokumentacji dla innych programistów (w tym ciebie). Jeśli komentarze dotyczą bardziej abstrakcyjnych problemów niż to, co robi kod na każdym kroku, radzisz sobie lepiej niż przeciętnie. Ten poziom abstrakcji różni się w zależności od używanego narzędzia. Komentarze towarzyszące procedurom asemblera mają na ogół niższy poziom „abstrakcji” niż, na przykład, ta APL A←0⋄A⊣{2⊤⍵:1+3×⍵⋄⍵÷2}⍣{⍺=A+←1}⎕. Myślę, że prawdopodobnie zasługuje na komentarz na temat problemu, który ma rozwiązać, hmmm?


2

Jeśli kod jest trywialny, nie wymaga komentarza. Jeśli kod nie jest trywialny, komentarz wyjaśniający najprawdopodobniej również nie będzie trywialny.

Problem z nietrywialnym językiem naturalnym polega na tym, że wielu z nas nie jest zbyt dobrych w czytaniu lub pisaniu. Jestem pewien, że twoje umiejętności komunikacji pisemnej są doskonałe, ale jednak ktoś z mniejszym zrozumieniem języka pisanego może źle zrozumieć twoje słowa.

Jeśli bardzo się starasz pisać w języku naturalnym, którego nie da się źle zinterpretować, powstaje coś w rodzaju dokumentu prawnego (a jak wszyscy wiemy, są one bardziej szczegółowe i trudniejsze do zrozumienia niż kod).

Kod powinien być najbardziej zwięzłym opisem twojej logiki i nie powinno być wielu dyskusji na temat znaczenia twojego kodu, ponieważ twój kompilator i platforma mają ostatnie słowo.

Osobiście nie powiedziałbym, że nigdy nie powinieneś pisać komentarza. Trzeba tylko zastanowić się, dlaczego Twój kod wymaga komentarza i jak możesz to naprawić. To wydaje się być częstym tematem w odpowiedziach tutaj.


Dokładnie to, o czym myślałem, gdy nie zgadzałem się ze stwierdzeniem „Człowiek może zrozumieć kawałek angielskiego znacznie szybciej, niż on / ona może zrozumieć fragment kodu o tym samym znaczeniu (o ile operacja nie jest trywialna)” Kod jest zawsze mniej dwuznaczne i bardziej zwięzłe.
stephenbayer

0

Jedną z kwestii, o których jeszcze nie wspomniano, jest to, że czasami precyzyjne komentowanie tego, co robi fragment kodu, może być pomocne w przypadkach, gdy język używa określonej składni do wielu celów. Na przykład zakładając, że wszystkie zmienne są typu float, rozważ:

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

Efektem jawnego rzutowania na floatto floatjest wymuszenie zaokrąglenia wyniku do pojedynczej precyzji; komentarz można zatem postrzegać jako po prostu powiedzenie, co robi kod. Z drugiej strony porównaj ten kod do:

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

Tutaj celem obsady jest zapobieganie skwierczeniu się kompilatora przy najbardziej efektywnym sposobie dokładnego obliczania (f2 / 10) [jest bardziej dokładny niż pomnożenie przez 0,1f, a na większości maszyn jest szybszy niż dzielenie przez 10,0f].

Bez komentarza ktoś, kto przeglądałby poprzedni kod, mógłby pomyśleć, że obsada została dodana w błędnym przekonaniu, że będzie to konieczne, aby zapobiec komplikacji kompilatora i że nie jest potrzebny. W rzeczywistości rzutowanie służy temu, aby robić dokładnie to, co mówi specyfikacja językowa: wymusić zaokrąglenie wyniku obliczeń do pojedynczej precyzji, nawet na komputerach, na których zaokrąglanie byłoby droższe niż utrzymanie wyniku w wyższej precyzji. Biorąc pod uwagę, że obsada floatmoże mieć wiele różnych znaczeń i celów, komentarz może określić, które znaczenie jest zamierzone w danym scenariuszu, może pomóc wyjaśnić, że rzeczywiste znaczenie jest zgodne z intencją.


Nie jestem pewien, czy J. Random Programmer, patrząc na drugi przykład, zda sobie sprawę, że stała jest zapisywana jako 0,1 z ważnego powodu, a nie dlatego, że oryginalny programista zapomniał wpisać „f”.
David K

Zwłaszcza podczas debugowania nigdy nie zakładasz, że coś zostało zrobione z ważnego powodu.
gnasher729

@DavidK: Celem mojego drugiego przykładowego kodu było porównanie go z pierwszym fragmentem kodu. W drugim fragmencie kodu, intencją programisty jest prawdopodobnie someFloatPropertyposiadanie najdokładniejszej reprezentacji f2/10, jaką może; dlatego głównym celem drugiego rzutowania jest po prostu skompilowanie kodu . W pierwszym przykładzie rzutowanie jednak wyraźnie nie jest potrzebne do jego normalnego celu (zmiana jednego typu czasu kompilacji na inny), ponieważ operandy już są float. Komentarz ma na celu wyjaśnienie, że obsada jest potrzebna do drugiego celu (zaokrąglenia).
supercat

Zgadzam się z opinią, że (float)w drugim przykładzie nie trzeba komentować obsady. Pytanie dotyczy stałej dosłownej 0.1. Wyjaśniłeś (w następnym akapicie tekstu), dlaczego napisalibyśmy 0.1: „jest dokładniejszy niż pomnożyć przez 0,1 f”. Sugeruję, że to są słowa, które powinny znajdować się w komentarzu.
David K

@DavidK: Z pewnością zamieściłbym komentarz, gdybym wiedział, że 0.1f byłby niedopuszczalnie niedokładny, i użyłby 0.1f, gdybym wiedział, że utrata precyzji byłaby do przyjęcia i że 0.1f faktycznie byłby znacznie szybszy niż 0,1 . Jeśli nie wiem, czy którakolwiek z tych rzeczy jest prawdą, wolałbym, aby mój zwyczaj kodowania polegał na używaniu doublestałych lub obliczeń pośrednich, których wartości mogą nie być reprezentowalne, ponieważ float[choć w językach wymagających irytujących jawnych rzutów metodą podwójnej zmiennoprzecinkowej, lenistwo może naciskać na użycie floatstałych nie dla prędkości, ale dla zminimalizowania irytacji].
supercat

-1

Komentarze wyjaśniające działanie kodu są formą powielania. Jeśli zmienisz kod, a następnie zapomnisz zaktualizować komentarze, może to powodować zamieszanie. Nie mówię, nie używaj ich, po prostu używaj ich rozsądnie. Akceptuję maksymę wujka Boba: „Komentuj tylko to, czego kod nie może powiedzieć”.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.