Komentarze do czystego kodu a dokumentacja klasy

83

Rozmawiam z moimi nowymi kolegami o komentarzach. Oboje lubimy Clean Code , i jestem całkowicie w porządku z faktem, że należy unikać komentarzy do kodu wbudowanego, a nazwy klas i metod powinny być używane do wyrażania tego, co robią.

Jednak jestem wielkim fanem dodawania małych podsumowań klas, które próbują wyjaśnić cel klasy i to, co faktycznie reprezentuje, przede wszystkim po to, aby łatwo było utrzymać zasadę pojedynczej odpowiedzialności . Przyzwyczaiłem się również do dodawania jednoetapowych podsumowań do metod, które wyjaśniają, co ma zrobić ta metoda. Typowym przykładem jest prosta metoda

public Product GetById(int productId) {...}

Dodaję następujące podsumowanie metody

/// <summary>
/// Retrieves a product by its id, returns null if no product was found.
/// </summary

Uważam, że fakt, że metoda zwraca null, powinien zostać udokumentowany. Deweloper, który chce wywołać metodę, nie powinien otwierać mojego kodu, aby sprawdzić, czy metoda zwraca wartość null lub zgłasza wyjątek. Czasami jest to część interfejsu, więc programista nawet nie wie, który kod źródłowy jest uruchomiony?

Jednak moi koledzy uważają, że tego rodzaju komentarze są „ zapachem kodu ” i że „komentarze są zawsze błędami” ( Robert C. Martin ).

Czy istnieje sposób na wyrażenie i przekazanie tego rodzaju wiedzy bez dodawania komentarzy? Ponieważ jestem wielkim fanem Roberta C. Martina, jestem trochę zdezorientowany. Czy streszczenia są takie same jak komentarze, a zatem zawsze zawodzą?

To nie jest pytanie o komentarze w linii.

Bjorn
źródło
38
Robert Martin powiedział „Komentarze to zawsze niepowodzenia”? Cóż, więc jest skrajnym ekstremistą i należy go wziąć ze szczyptą soli. (Tak, wiem, że pisze tak w celach retorycznych, aby przekazać swoje przesłanie. Chodzi mi o to, powinieneś również .)
Kilian Foth,
18
Książki wuja Boba powinny zawierać 1 kg worka soli ...
AK_
6
Jeśli śledzisz Roberta Martina, dokumentacja przypadku zerowego powinna być testem. Oznacza to, że powinieneś mieć test pokazujący, w którym przypadku metoda może zwrócić null. Alternatywnie, ponieważ jest to Java, adnotacja @Nullable byłaby również lepsza niż komentarz.
Martin Epsz,
15
@Bjorn Posiadam kopię Clean Code i przeczytałem ją od początku do końca. Tak, wujek Bob woli, aby kod sam się dokumentował, ale istnieje wiele przykładów komentarzy w jego własnym kodzie w książce . Chodzi o to, że jeśli czujesz się zmuszony do napisania komentarza, spróbuj naprawdę mocno zmienić kod zamiast dodawać komentarz, ale nie odrzucaj komentarzy całkowicie (nawet komentarzy w linii).
6
Metodę należy nazwać TryGetById, a komentarz należy usunąć.
usr

Odpowiedzi:

116

Jak powiedzieli inni, istnieje różnica między komentarzami dokumentującymi API a komentarzami wbudowanymi. Z mojej perspektywy główna różnica polega na tym, że komentarz wbudowany jest czytany obok kodu , podczas gdy komentarz do dokumentacji jest czytany obok podpisu tego, co komentujesz.

Biorąc to pod uwagę, możemy zastosować tę samą zasadę DRY . Czy komentarz mówi to samo co podpis? Spójrzmy na twój przykład:

Pobiera produkt według jego identyfikatora

Ta część po prostu powtarza to, co już widzimy z nazwy GetByIdplus typ zwrotu Product. Rodzi to także pytanie, na czym polega różnica między „pobieraniem” a „odzyskiwaniem” i jaki kod namiarny względem komentarza ma to rozróżnienie. Jest to więc niepotrzebne i nieco mylące. Jeśli już, przeszkadza to naprawdę przydatna, druga część komentarza:

zwraca null, jeśli nie znaleziono żadnego produktu.

Ach! Jest to coś, czego na pewno nie wiemy na podstawie samego podpisu i dostarcza użytecznych informacji.


Teraz przejdź o krok dalej. Gdy ludzie mówią o komentarzach, gdy zapach pachnie, pytanie nie brzmi, czy kod taki, jaki jest, wymaga komentarza, ale czy komentarz wskazuje, że kod można lepiej napisać, aby wyrazić informacje w komentarzu. To właśnie oznacza „zapach kodu” - to nie znaczy „nie rób tego!”, Oznacza „jeśli to robisz, może to oznaczać, że wystąpił problem”.

Więc jeśli twoi koledzy powiedzą ci, że ten komentarz o wartości null jest zapachem kodu, powinieneś po prostu zapytać ich: „W porządku, jak mam to wyrazić?” Jeśli mają wykonalną odpowiedź, nauczyłeś się czegoś. Jeśli nie, prawdopodobnie zabiją ich skargi.


Jeśli chodzi o ten konkretny przypadek, ogólnie wiadomo, że problem zerowy jest trudny. Istnieje powód, dla którego podstawy kodu są wypełnione klauzulami ochronnymi, dlaczego sprawdzanie wartości zerowej jest popularnym warunkiem umów o kod, dlaczego istnienie wartości zerowej nazwano „błędem w wysokości miliarda dolarów”. Nie ma tak wielu wykonalnych opcji. Jednak jednym z popularnych w C # jest Try...konwencja:

public bool TryGetById(int productId, out Product product);

W innych językach idiomatyczne może być użycie typu (często nazywanego jak Optionallub Maybe) w celu wskazania wyniku, który może, ale nie musi:

public Optional<Product> GetById(int productId);

W pewnym sensie ta postawa anty-komentarzowa gdzieś nas zaprowadziła: przynajmniej zastanawialiśmy się, czy ten komentarz reprezentuje zapach i jakie mogą być dla nas alternatywy.

To, czy rzeczywiście powinniśmy preferować je w stosunku do oryginalnego podpisu, to zupełnie inna debata, ale mamy przynajmniej opcje wyrażania za pomocą kodu zamiast komentowania tego, co dzieje się, gdy nie znajdzie się żadnego produktu. Powinieneś przedyskutować ze swoimi kolegami, które z tych opcji są lepsze i dlaczego, i mam nadzieję, że pomogą ci wyjść poza ogólne dogmatyczne wypowiedzi na temat komentarzy.

Ben Aaronson
źródło
9
Lub Linq-equivalent z Try..., ...OrDefault, który powróci default(T), jeśli klauzula doprowadziłoby do pustego wyniku.
Bart van Nierop
4
Naprawdę doceniam twoje rozróżnienie między komentarzami kodu wbudowanego i komentarzami do dokumentacji a podanymi przykładami :)
Rachel
2
Możliwe wartości zwracane przez funkcję powinny być widoczne po jej sygnaturze. TryGetValueWzór jest rozsądny sposób to zrobić w C #, ale w językach najbardziej funkcjonalne mają lepszy sposób reprezentowania brakujących wartości. Przeczytaj więcej tutaj
AlexFoxGill
2
@BenAaronson: Jeśli ktoś chce mieć ogólny interfejs, który może obsługiwać kowariancję, można użyć go T TryGetValue(params, ref bool success)dla dowolnego typu T lub T TryGetValue(params), z zerowym wskazaniem niepowodzenia, dla typu T ograniczonego przez klasę, ale TryGetXXwzór, który zwraca, booljest niezgodny z kowariancją.
supercat
6
W Javie 8 możesz zwrócić an, Optional<Product>aby wskazać, że produkt może nie zostać zwrócony z metody
Wim Deblauwe
102

Cytat Roberta C. Martina jest wyjęty z kontekstu. Oto cytat z nieco większym kontekstem:

Nic nie może być tak pomocne, jak dobrze umieszczony komentarz. Nic nie może zagracać modułu bardziej niż frywolne dogmatyczne komentarze. Nic nie może być tak szkodliwe, jak stary, szorstki komentarz, który propaguje kłamstwa i dezinformację.

Komentarze nie są jak Lista Schindlera. Nie są „czystym dobrem”. Rzeczywiście, komentarze są w najlepszym razie złem koniecznym. Gdyby nasze języki programowania były wystarczająco wyraziste lub gdybyśmy mieli talent do subtelnego posługiwania się tymi językami, aby wyrazić naszą intencję, nie potrzebowalibyśmy zbyt wiele komentarzy - być może wcale.

Właściwe użycie komentarzy ma zrekompensować nam brak wyrażenia się w kodzie. Zauważ, że użyłem słowa brak. Miałem to na myśli. 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.

Więc kiedy znajdziesz się w sytuacji, w której musisz napisać komentarz, zastanów się i sprawdź, czy nie ma sposobu, aby zmienić tabele i wyrazić się w kodzie. Za każdym razem, gdy wyrażasz się w kodzie, powinieneś poklepać się po plecach. Za każdym razem, gdy piszesz komentarz, powinieneś skrzywić się i czuć brak umiejętności wyrażania.

(Skopiowano z tego miejsca , ale oryginalny cytat pochodzi z Clean Code: A Handbook of Agile Software Craftsmanship )

To, jak ten cytat sprowadza się do „Komentarze są zawsze porażkami”, jest dobrym przykładem tego, jak niektórzy ludzie wyciągną rozsądny cytat z kontekstu i zamieni go w głupi dogmat.


Dokumentacja API (jak javadoc) ma udokumentować API, aby użytkownik mógł z niego korzystać bez konieczności odczytywania kodu źródłowego . W takim przypadku dokumentacja powinna wyjaśniać, co robi metoda. Teraz możesz argumentować, że „Pobiera produkt według jego identyfikatora” jest zbędny, ponieważ jest już wskazany przez nazwę metody, ale informacje, które nullmożna zwrócić, są zdecydowanie ważne w dokumentacji, ponieważ nie jest to w żaden sposób oczywiste.

Jeśli chcesz uniknąć konieczności komentarza, musisz usunąć podstawowy problem (którym jest użycie nulljako prawidłowej wartości zwracanej), czyniąc interfejs API bardziej wyraźnym. Na przykład możesz zwrócić jakiś rodzaj Option<Product>, więc sam podpis typu wyraźnie informuje, co zostanie zwrócone, jeśli produkt nie zostanie znaleziony.

Ale w każdym razie nie jest realistyczne udokumentowanie API w pełni tylko poprzez nazwy metod i podpisy typów. Użyj komentarzy do wszelkich dodatkowych nieoczywistych informacji, które użytkownik powinien wiedzieć. Załóżmy, że dokumentacja API z DateTime.AddMonths()BCL:

Metoda AddMonths oblicza wynikowy miesiąc i rok, biorąc pod uwagę lata przestępne i liczbę dni w miesiącu, a następnie dostosowuje część dzienną wynikowego obiektu DateTime. Jeśli wynikowy dzień nie jest prawidłowym dniem w wynikowym miesiącu, używany jest ostatni ważny dzień wynikowego miesiąca. Na przykład 31 marca + 1 miesiąc = 30 kwietnia. Część czasu wynikowego obiektu DateTime pozostaje taka sama jak to wystąpienie.

Nie ma możliwości wyrażenia tego za pomocą nazwy i podpisu metody! Oczywiście twoja dokumentacja klasowa może nie wymagać takiego poziomu szczegółowości, to tylko przykład.


Komentarze wbudowane też nie są złe.

Złe komentarze są złe. Na przykład komentarze, które wyjaśniają tylko to, co można w prosty sposób zobaczyć z kodu, klasycznym przykładem jest:

// increment x by one
x++;

Komentarze, które wyjaśniają coś, co można wyjaśnić przez zmianę nazwy zmiennej lub metody lub w inny sposób restrukturyzację kodu, to zapach kodu:

// data1 is the collection of tasks which failed during execution
var data1 = getData1();

Takie są uwagi, które Martin szykuje. Komentarz jest symptomem braku napisania czystego kodu - w tym przypadku należy użyć zrozumiałych nazw dla zmiennych i metod. Sam komentarz oczywiście nie jest problemem, problem polega na tym, że potrzebujemy komentarza, aby zrozumieć kod.

Ale komentarze powinny być użyte do wyjaśnienia wszystkiego, co nie jest oczywiste z kodu, np. Dlaczego kod jest napisany w pewien nieoczywisty sposób:

// need to reset foo before calling bar due to a bug in the foo component.
foo.reset()
foo.bar();

Komentarze, które wyjaśniają, co robi nadmiernie skomplikowany fragment kodu, są również zapachem, ale poprawka nie polega na zakazaniu komentarzy, poprawka polega na naprawieniu kodu! Mówiąc inaczej, zdarza się zawiły kod (miejmy nadzieję, że tylko tymczasowo do momentu refaktoryzacji), ale żaden zwykły programista nie pisze za pierwszym razem idealnego czystego kodu. Kiedy zdarza się zawiły kod, znacznie lepiej jest napisać komentarz wyjaśniający, co robi, niż nie pisać komentarza. Ten komentarz ułatwi również później refaktoryzację.

Czasami kod jest nieuchronnie złożony. Może to być skomplikowany algorytm lub kod zmniejszający przejrzystość ze względu na wydajność. Ponownie komentarze są konieczne.

JacquesB
źródło
13
Istnieje również przypadek, w którym twój kod radzi sobie z sytuacją, która jest po prostu skomplikowana i żaden prosty kod nie jest w stanie sobie z tym poradzić.
gnasher729
6
Dobra uwaga, gnasher. Wydaje się, że często tak się dzieje, gdy trzeba zoptymalizować fragment kodu pod kątem wydajności.
JacquesB
9
Nawet komentarz x++może być dobry, jeśli jest to coś w rodzaju „zwiększaj x jeden, zawijaj, jeśli jest to UINT32_MAX”; każdy, kto zna specyfikację języka, wiedziałby, że zwiększenie uint32_towijania będzie zawijać, ale bez komentarza można nie wiedzieć, czy takie owijanie było oczekiwaną częścią implementowanego algorytmu.
supercat
5
@ l0b0: Mam nadzieję, że żartujesz!
JacquesB
9
@ l0b0 Nie ma czegoś takiego jak kod tymczasowy. Kod nigdy nie jest refaktoryzowany, ponieważ firma była zadowolona z wyniku i nie zatwierdzi finansowania w celu jego naprawy. Za pięć lat niektórzy młodsi programiści zobaczą ten kod, nawet nie używasz WizBug 4.0, ponieważ zastąpiłeś go Bugtrocity v9, więc „Bug123” nic dla niego nie znaczy. Teraz myśli, że tak właśnie powinien wyglądać stały kod, i jest okropnym deweloperem przez całą swoją karierę. Pomyśl o dzieciach. Nie pisz kodu tymczasowego.
corsiKa
36

Istnieje różnica między komentowaniem kodu a dokumentowaniem kodu .

  • Komentarze są potrzebne, aby zachować kod później, czyli zmienić sam kod.

    Komentarze mogą rzeczywiście być postrzegane jako problematyczne. Skrajnym punktem byłoby powiedzenie, że zawsze wskazują na problem, albo w kodzie (kod zbyt trudny do zrozumienia), albo w języku (język nie może być wystarczająco wyrazisty; na przykład nullmożna wyrazić fakt, że metoda nigdy nie zwraca wartości poprzez umowy Code w języku C #, ale nie ma sposobu, aby wyrazić to za pomocą kodu, powiedzmy, PHP).

  • Potrzebna jest dokumentacja , aby móc korzystać z opracowanych obiektów (klas, interfejsów). Grupy docelowej jest inna: to nie osoby, które będą utrzymywać swój kod i zmienić go, że mówimy o tutaj, ale osoby, które ledwo trzeba używać go.

    Usuwanie dokumentacji, ponieważ kod jest wystarczająco przejrzysty, jest szalone, ponieważ dokumentacja ma na celu umożliwienie korzystania z klas i interfejsów bez konieczności odczytywania tysięcy wierszy kodu .

Arseni Mourzenko
źródło
Tak, ale przynajmniej część argumentu Martina polega na tym, że przy nowoczesnych praktykach programistycznych testy są dokumentacją, a nie samym kodem. Zakładając, że kod jest testowany za pomocą systemu testowego typu BDD, np. Specflow, same testy są bezpośrednio czytelnym opisem zachowania metody („biorąc pod uwagę bazę danych produktów, gdy GetById jest wywoływany z identyfikatorem prawidłowego produktu, wtedy odpowiedni obiekt Product zostaje zwrócony [...] po wywołaniu GetById z niepoprawnym identyfikatorem produktu, wówczas zwracane jest null "lub coś w tym rodzaju).
Jules
13

Wygląda na to, że twój kolega czyta książki, przyjmuje to, co mówią, i stosuje to, czego się nauczył, bez zastanowienia i bez uwzględnienia kontekstu.

Twój komentarz na temat tego, co robi funkcja, powinien być taki, abyś mógł wyrzucić kod implementacyjny, czytam komentarz funkcji i mogę napisać zamiennik implementacji, bez żadnych problemów.

Jeśli komentarz nie mówi mi, czy zostanie zgłoszony wyjątek, czy też zostanie zwrócone zero, nie mogę tego zrobić. Ponadto, jeśli komentarz nie mówi , czy wyjątek jest zgłaszany, czy też zwracany jest zero, wówczas przy każdym wywołaniu funkcji należy upewnić się, że kod działa poprawnie, niezależnie od tego, czy wyjątek zostanie zgłoszony, czy zwrócony zero.

Więc twój kolega jest całkowicie w błędzie. Idź dalej i przeczytaj wszystkie książki, ale pomyśl sam.

PS. Widziałem twoją linię „czasami jest to część interfejsu, więc nawet nie wiesz, jaki kod jest uruchomiony.” Nawet z funkcjami wirtualnymi nie wiesz, który kod działa. Co gorsza, jeśli napisałeś abstrakcyjną klasę, nie ma nawet żadnego kodu! Jeśli więc masz klasę abstrakcyjną z funkcją abstrakcyjną, komentarze dodane do funkcji abstrakcyjnej są jedyną rzeczą, którą musi poprowadzić implementator konkretnej klasy. Te komentarze mogą być również jedyną rzeczą, która może poprowadzić użytkownika klasy, na przykład jeśli wszystko, co masz, to klasa abstrakcyjna i fabryka zwracająca konkretną implementację, ale nigdy nie widzisz żadnego kodu źródłowego implementacji. (I oczywiście nie powinienem patrzeć na kod źródłowy jednej implementacji).

gnasher729
źródło
Kod nie skomentowałem przez 10 lat. Komentarze są wzdęte, śmieci. Obecnie nikt nie komentuje kodu. Koncentrujemy się na dobrze sformułowanym i nazwanym kodzie, małych modułach, odsprzęganiu itp. Dzięki temu kod jest czytelny, a nie komentarze. Testy zapewniają, że jeśli wyrzucisz kod, nic nie pójdzie źle, nie komentarze. Testy pokazują, jak używasz pisanego kodu, jak je nazywasz i dlaczego one istnieją. Jesteś zbyt starą szkołą, musisz się uczyć testowania i czyszczenia kodu, przyjacielu.
PositiveGuy
12

Istnieją dwa rodzaje komentarzy do rozważenia - widoczne dla osób posiadających kod i używane do generowania dokumentacji.

Rodzaj komentarza, do którego odnosi się wujek Bob, jest widoczny tylko dla osób mających kod. To, za czym on opowiada, to forma SUSZENIA . Dla osoby, która patrzy na kod źródłowy, kod źródłowy powinien być potrzebną dokumentacją. Nawet w przypadku, gdy ludzie mają dostęp do kodu źródłowego, komentarze nie zawsze są złe. Czasami algorytmy są skomplikowane lub musisz uchwycić, dlaczego podejmujesz nieoczywiste podejście, aby inni nie złamali kodu, jeśli spróbują naprawić błąd lub dodać nową funkcję.

Komentarze, które opisujesz, są dokumentacją API. Są to rzeczy widoczne dla osób korzystających z Twojej implementacji, ale mogą nie mieć dostępu do Twojego kodu źródłowego. Nawet jeśli mają dostęp do kodu źródłowego, mogą pracować nad innymi modułami i nie patrzeć na kod źródłowy. Dla tych osób przydatne byłoby udostępnienie tej dokumentacji w IDE podczas pisania kodu.

Thomas Owens
źródło
Szczerze mówiąc, nigdy nie myślałem o DRY stosującym się do kodu + komentarzy, ale ma to sens. Coś w rodzaju „przyrostu X” w odpowiedzi @ JacquesB.
7

Wartość komentarza mierzona jest wartością przekazywanych informacji minus wysiłek potrzebny do ich przeczytania i / lub zignorowania. Więc jeśli przeanalizujemy komentarz

/// <summary>
/// Retrieves a product by its id, returns null if no product was found.
/// </summary>

pod względem wartości i kosztów widzimy trzy rzeczy:

  1. Retrieves a product by its idpowtarza to, co mówi nazwa funkcji, więc jest to koszt bez wartości. Należy go usunąć.

  2. returns null if no product was foundjest bardzo cenną informacją. Prawdopodobnie skraca to czas, w którym inni koderzy będą musieli spojrzeć na implementację funkcji. Jestem pewien, że oszczędza więcej czytania niż sam koszt czytania, który sam sobie przedstawia. Powinien zostać.

  3. Linie

    /// <summary>
    /// </summary>
    

    nie wolno przenosić żadnych informacji. Są czystym kosztem dla czytelnika komentarza. Mogą być uzasadnione, jeśli twój generator dokumentacji ich potrzebuje, ale w takim przypadku prawdopodobnie powinieneś pomyśleć o innym generatorze dokumentacji.

    To jest powód, dla którego użycie generatorów dokumentacji jest dyskusyjnym pomysłem: na ogół wymagają one wielu dodatkowych komentarzy, które nie zawierają żadnych informacji lub powtarzają oczywiste rzeczy, tylko ze względu na dopracowany dokument.


Obserwacja, której nie znalazłem w żadnej z pozostałych odpowiedzi:

Nawet komentarze, które nie są konieczne do zrozumienia / używania kodu, mogą być bardzo cenne. Oto jeden z takich przykładów:

//XXX: The obvious way to do this would have been ...
//     However, since we need this functionality primarily for ...
//     doing this the non-obvious way of ...
//     gives us the advantage of ...

Prawdopodobnie dużo tekstu, całkowicie niepotrzebne do zrozumienia / użycia kodu. Wyjaśnia jednak powód, dla którego kod wygląda tak, jak wygląda. Powstrzyma ludzi od krótkiego spojrzenia na kod, zastanowi się, dlaczego nie jest to zrobione w oczywisty sposób, i zacznie refaktoryzować kod, dopóki nie zorientuje się, dlaczego kod został napisany w ten sposób. I nawet jeśli czytnik jest wystarczająco inteligentny, aby nie przejść bezpośrednio do refaktoryzacji, wciąż muszą dowiedzieć się, dlaczego kod wygląda tak, jak wygląda, zanim zdadzą sobie sprawę, że najlepiej pozostać taki, jaki jest. Ten komentarz mógłby dosłownie zaoszczędzić godziny pracy. Zatem wartość jest wyższa niż koszt.

Podobnie, komentarze mogą komunikować zamiary kodu, a nie tylko jego działanie. I mogą namalować duży obraz, który zwykle gubi się w najdrobniejszych szczegółach samego kodu. Jako taki, masz rację, opowiadając się za komentarzami klasy. Najbardziej cenię komentarze klasowe, jeśli wyjaśniają one intencje klasy, jej interakcje z innymi klasami, sposób, w jaki należy ją wykorzystywać itp. Niestety, nie jestem wielkim autorem takich komentarzy ...

cmaster
źródło
2
Wow - zmień generator dokumentów, ponieważ wymaga on kilku dodatkowych wierszy HTML do analizy? Lepiej nie.
corsiKa
2
@corsiKa YMMV, ale dla jednego wolałbym generator dokumentacji, który redukuje koszty komentarzy do minimum. Oczywiście wolałbym również czytać dobrze napisany plik nagłówka niż dokumentację doxygen, która nie jest zsynchronizowana z rzeczywistym kodem. Ale, jak powiedziałem, YMMV.
cmaster
4
Nawet jeśli nazwa metody doskonale opisuje jej cel, powtórzenie tego celu przy użyciu języka naturalnego może ułatwić komuś czytającemu powiązanie tego celu z następującymi zastrzeżeniami. Przekształcenie tego, co opisano w nazwie, będzie na tyle krótkie, że nawet jeśli wartość będzie niska, koszt również będzie niski. W związku z tym nie zgadzam się z pierwszą częścią twojego postu. +1 za drugą część. Dokumentacja alternatywnych podejść, które zostały ocenione i odrzucone, może być niezwykle cenna, ale takich informacji rzadko bierze się pod uwagę, na jaką zasługują.
supercat
GetByIdrodzi pytanie, co to jest id, a także, co dostać skąd. Komentarz do dokumentacji powinien umożliwiać środowisku programistycznemu wyświetlanie odpowiedzi na te pytania. O ile nie zostanie to wyjaśnione w innym miejscu w komentarzach do modułu, byłoby to również jedno miejsce, w którym można by powiedzieć, dlaczego i tak dostaniemy identyfikator.
hyde
Komentarze cios, czysty kod (samoopisanie), TDD (częste uzyskiwanie informacji zwrotnych i często uzyskiwanie informacji zwrotnych na temat twojego projektu) oraz testy (dają ci pewność i zachowanie dokumentu)! TESTY TESTY ludzi. Nikt tutaj o tym nie mówi. obudź się
PositiveGuy
4

Kod niekomentowany to zły kod. Jest to powszechny (jeśli nie uniwersalny) mit, że kod można odczytać w taki sam sposób jak, powiedzmy, angielski. Musi być interpretowany i dla każdego, oprócz najbardziej trywialnego kodu, który wymaga czasu i wysiłku. Ponadto każdy ma inny poziom umiejętności czytania i pisania w danym języku. Różnice między pisarzem a stylami kodowania i możliwościami czytelnika stanowią silne bariery dla dokładnej interpretacji. Jest to także mit, że intencję autora można wywnioskować z implementacji kodu. Z mojego doświadczenia wynika, że ​​rzadko dodawanie dodatkowych komentarzy jest błędne.

Robert Martin i in. traktuj to jako „nieprzyjemny zapach”, ponieważ być może kod został zmieniony, a komentarze nie. Mówię, że to dobra rzecz (dokładnie w ten sam sposób, w jaki do gazu domowego dodaje się „nieprzyjemny zapach”, aby ostrzec użytkownika o wycieku). Czytanie komentarzy stanowi przydatny punkt wyjścia do interpretacji rzeczywistego kodu. Jeśli się zgadzają, zwiększysz zaufanie do kodu. Jeśli różnią się, oznacza to, że wykryłeś ostrzegawczy zapach i musisz zbadać sprawę dalej. Lekarstwem na „nieprzyjemny zapach” nie jest jego usunięcie, lecz uszczelnienie wycieku.

Vince O'Sullivan
źródło
2
Biorąc pod uwagę, że jesteś inteligentnym autorem, a tekst jest na korzyść inteligentnej publiczności; Narysujesz linię zgodnie ze zdrowym rozsądkiem. Oczywiście, na obecnym etapie, przykład jest głupi, ale to nie znaczy, że nie może istnieć żaden dobry powód, aby wyjaśnić komentarzem, dlaczego kod zawiera w tym momencie i ++.
Vince O'Sullivan
8
i++; // Adjust for leap years.
Vince O'Sullivan
5
„Robert Martin i wsp. Uważają go za„ nieprzyjemny zapach ”, ponieważ być może kod został zmieniony, a komentarze nie.” To tylko część zapachu. Najgorszy zapach pochodzi z pomysłu, że programista postanowił nie pisać kodu w bardziej opisowy sposób, zamiast tego postanowił „uszczelnić wyciek” komentarzem. Jego twierdzenie jest takie, że zamiast klepnąć komentarz „// dostosuj dla lat przestępnych”, prawdopodobnie należy mieć metodę „adjustForLeapYears ()” w samym kodzie (lub coś podobnego). Dokumentacja ma postać testów wykorzystujących logikę roku przestępnego.
Eric King,
3
Zastanowiłbym się nad dodaniem wywołania metody w celu zastąpienia jednego wiersza kodu nadmiarem komentarza, zwłaszcza że nazwa metody jest tak naprawdę tylko komentarzem oznaczającym fragment kodu i nie gwarantuje większej dokładności dokumentacji. (Gdyby linia ta występowała w dwóch lub więcej miejscach, wówczas wywołanie metody byłoby oczywiście właściwym rozwiązaniem.)
Vince O'Sullivan
1
@Jay Powodem jest to, że wyraźne abstrakcje (np. Poprzez wprowadzenie metod) regułą. Nie robienie tego, ponieważ możesz skończyć z metodą, która ma jedną linię, jest wyjątkiem. Pozwólcie, że sparafrazuję prawdziwą arbitralną zasadę: „Użyj struktur języka programowania (zwykle metod i klas), aby wprowadzić abstrakcje, chyba że implementację tej abstrakcji można wyrazić jako jeden wiersz kodu, w takim przypadku określ abstrakcję w języku naturalnym poprzez dodanie komentarz."
Eric
3

W niektórych językach (na przykład F #) cały komentarz / dokumentacja może być faktycznie wyrażona w podpisie metody. Wynika to z faktu, że w języku F # null nie jest ogólnie dozwoloną wartością, chyba że jest wyraźnie dozwolony.

To, co jest powszechne w F # (a także w kilku innych językach funkcjonalnych) to to, że zamiast null używasz typu opcji, Option<T>który może być Nonealbo Some(T). Język zrozumiałby to i zmusiłby cię (lub ostrzec, jeśli tego nie zrobisz) w obu przypadkach, gdy próbujesz go użyć.

Na przykład w języku F # możesz mieć podpis, który wygląda tak

val GetProductById: int -> Option<Product>

I wtedy byłaby to funkcja, która przyjmuje jeden parametr (int), a następnie zwraca produkt lub wartość Brak.

A potem możesz użyć tego w ten sposób

let product = GetProduct 42
match product with
| None -> printfn "No product found!"
| Some p -> DoThingWithProduct p

Otrzymasz ostrzeżenia kompilatora, jeśli nie pasujesz do obu możliwych przypadków. Nie ma więc możliwości uzyskania wyjątków zerowej referencji (chyba że oczywiście zignorujesz ostrzeżenia kompilatora) i wiesz wszystko, czego potrzebujesz, patrząc na sygnaturę funkcji.

Oczywiście wymaga to, aby Twój język został zaprojektowany w ten sposób - czego nie jest w wielu popularnych językach, takich jak C #, Java, C ++ itp. W obecnej sytuacji może Ci to nie być pomocne. Ale (mam nadzieję) miło wiedzieć, że istnieją języki, które pozwalają wyrażać tego rodzaju informacje w sposób statyczny, bez uciekania się do komentarzy itp. :)

wasatz
źródło
1

Jest tu kilka doskonałych odpowiedzi i nie chcę powtarzać tego, co mówią. Ale pozwól mi dodać kilka komentarzy. (Nie jest przeznaczona gra słów).

Istnieje wiele stwierdzeń, które inteligentni składają - na temat rozwoju oprogramowania i wielu innych tematów - które są bardzo dobrymi radami, gdy są rozumiane w kontekście, ale które są głupie, niż wyjmowanie z kontekstu lub zastosowanie w niewłaściwych sytuacjach lub śmieszne skrajności.

Pomysł, że kod powinien być samodokumentujący, jest jednym z takich doskonałych pomysłów. Ale w prawdziwym życiu istnieją ograniczenia dotyczące praktyczności tego pomysłu.

Jednym z haczyków jest to, że język może nie zapewniać funkcji dokumentujących to, co należy udokumentować w sposób jasny i zwięzły. Wraz z poprawą języków komputerowych staje się to coraz mniejszym problemem. Ale nie sądzę, że całkowicie zniknęło. W czasach, gdy pisałem w asemblerze, sensowne było umieszczenie komentarza w stylu „cena całkowita = cena pozycji niepodlegających opodatkowaniu plus cena pozycji podlegających opodatkowaniu plus cena pozycji podlegających opodatkowaniu * stawka podatkowa”. Dokładnie to, co było w rejestrze w danym momencie, niekoniecznie było oczywiste. Wykonanie prostej operacji wymagało wielu kroków. Itd. Ale jeśli piszesz w nowoczesnym języku, taki komentarz byłby tylko powtórzeniem jednej linii kodu.

Zawsze denerwuję się, gdy widzę komentarze typu „x = x + 7; // dodaj 7 do x”. Na przykład dziękuję, gdybym zapomniał, co oznacza znak plus, co mogłoby być bardzo pomocne. To, co naprawdę mnie dezorientuje, to wiedza o tym, co to jest „x” lub dlaczego w tym konkretnym momencie trzeba było dodać do niego 7. Ten kod można samokontraktować, nadając bardziej znaczącą nazwę „x” i używając stałej symbolicznej zamiast 7. Na przykład, jeśli napisałeś „total_price = total_price + MEMBERSHIP_FEE;”, to prawdopodobnie komentarz wcale nie jest potrzebny .

Wspaniale jest powiedzieć, że nazwa funkcji powinna dokładnie powiedzieć, co robi funkcja, aby wszelkie dodatkowe komentarze były zbędne. Mam dobre wspomnienia z czasu, gdy napisałem funkcję, która sprawdzała, czy numer pozycji znajduje się w naszej tabeli pozycji bazy danych, zwracając wartość prawda czy fałsz, i którą nazwałem „ValidateItemNumber”. Wydawało się, że to porządne imię. Potem przyszedł ktoś inny i zmodyfikował tę funkcję, aby również utworzyć zamówienie na przedmiot i zaktualizować bazę danych, ale nigdy nie zmienił nazwy. Teraz nazwa była bardzo myląca. Brzmiało to tak, jakby zrobił jedną małą rzecz, podczas gdy naprawdę zrobił znacznie więcej. Później ktoś nawet wziął udział w sprawdzaniu poprawności numeru przedmiotu i zrobił to gdzie indziej, ale nadal nie zmienił nazwy. Więc nawet jeśli funkcja nie miała teraz nic wspólnego z sprawdzaniem poprawności numerów pozycji,

Ale w praktyce często nie jest możliwe, aby nazwa funkcji całkowicie opisała, co robi funkcja, gdyby nazwa funkcji nie była tak długa, jak kod tworzący tę funkcję. Czy nazwa powie nam dokładnie, jakie weryfikacje są wykonywane dla wszystkich parametrów? Co dzieje się na warunkach wyjątkowych? Wyjaśnić każdą możliwą niejednoznaczność? W pewnym momencie nazwa będzie tak długa, że ​​stanie się po prostu myląca. Zaakceptowałbym String BuildFullName (imię String, nazwisko String) jako przyzwoitą sygnaturę funkcji. Chociaż nie wyjaśnia to, czy nazwa jest wyrażona jako „pierwsza ostatnia”, czy „ostatnia, pierwsza”, czy jakaś inna odmiana, to co robi, jeśli jedna lub obie części nazwy są puste, jeśli nakłada ograniczenia na łączną długość i co robi, jeśli zostanie przekroczone,

Sójka
źródło