Komentarze w kodzie

Ilustracja do artykułu: przedstawia karteczki samoprzylepne do notatek typu post-it oraz kilka cieńkopisów.
Obraz Melinda Fiorino z Pixabay

Od początku swojego kursu na YouTube konsekwentne pomijałem temat komentarzy, mimo że jest to temat niezwykle łatwy do zrozumienia. Robiłem to z pełną premedytacją – uważałem, że są ważniejsze rzeczy do pokazania, a w przypadku komentarzy ważniejsze niż umiejętność ich pisania jest świadomość kiedy lepiej ich nie pisać.

Od kiedy oprócz kursu na YouTube mam bloga, w głowie przyświeca mi myśl, że ten temat powinien być poruszony właśnie w formie wpisu, a nie filmu. No to zaczynamy!

Czym jest komentarz?

Photo by Susan Chaaban from FreeImages

W pierwszych odcinkach kursu starałem się przemycać treści opowiadające o dobrych praktykach w kodowaniu. Mówiłem Wam o tym, że każdy kod musi być napisany w taki sposób, że jeśli za rok do niego zajrzycie, to musicie dokładnie rozumieć o co chodzi. Komentarz w kodzie może być jednym z narzędzi objaśniających zamiary programisty. Jest to kod, który jest pomijany przez kompilator, widoczny tylko dla programisty oglądającego plik z kodem. Może zawierać dowolny tekst w dowolnym formacie. Użytkownik nie zobaczy komentarza w programie, bo nie do tego komentarz został stworzony.

Poniżej przykłady komentarzy:

/* HELLO WORLD
   Program wyświetlający napis Hello World! w konsoli
   Autor: Microsoft
*/
namespace HelloWorld
{
    class Program
    {
        static void Main(string[] args)
        {
            // Poniższa linia wyświetla napis Hello World.
            System.Console.WriteLine("Hello World!");

            // Koniec programu
        }
    }
}

Jeśli nie wychwyciłeś gdzie znajdują się komentarze, to już śpieszę z wyjaśnieniem. Pierwszy komentarz typu wieloliniowego znajduje się w liniach 1-4. Dwa kolejne komentarze typu jednoliniowego znajdują się w liniach 11 i 14.

Komentarze wieloliniowe

Komentarz wieloliniowy musimy otworzyć i zamknąć. Otwieramy go znakami /* natomiast zamykamy tymi samymi znakami w odwrotnej kolejności: */. Pomiędzy otwarciem a zamknięciem komentarza wstawiamy jego treść. Jeśli pominiemy znak zamknięcia, to cały nasz kod od momentu otwarcia komentarza staje się komentarzem.

/* To jest mój komentarz wieloliniowy.
Mogę go przedłużać o tyle linii ile chcę.

Mogę przedzielić ten komentarz pustymi liniami, nie ma problemu.
                               Nie będzie też błędem technicznym rozpoczęcie pisania od połowy linii, po użyciu spacji.
*/  A to jest znak zamknięcia komentarza. To co w tej chwili piszę jest już poza obrębem komentarza i spowoduje błąd kompilatora.

Podobnie jak to co jest w tej linii - również nie powinno się tutaj znaleźć.

Z moich obserwacji wynika, że komentarze wieloliniowe są o wiele mniej popularne od jednoliniowych. Sam wolę korzystać z tych drugich, nawet gdy chcę zrobić komentarz na wiele linii. Według mnie komentarze jednoliniowe wyglądają bardziej estetycznie.

Komentarze jednoliniowe

W przypadku komentarzy jednoliniowych mamy do czynienia z otwarciem komentarza, które oznaczamy dwoma slashami: //. Natomiast zamknięciem komentarza (czy tego chcesz czy nie) zawsze jest koniec linii.

// No to zaczynamy od komentarza jednoliniowego.
int a = 5; // Komentarz można zacząć również tutaj.
// Ale napisanie czegoś takiego nie przyniesie większego skutku. int b = 6;

// Można też zrobić tak,
// że dłuższy tekst piszemy w liniach
// jedna pod drugą
// i każda zaczyna się od otwarcia komentarza.
      // Można też zrobić to samo
      // po zrobieniu wcięć.
// Każde // kolejne // otwarcie // komentarza // w tej samej linii // jest // ignorowane.
// Nie zamkniemy też komentarza w ten sposób, */ sam widzisz.

Dobre praktyki

Ilustracja do artykułu - pokazuje mnóstwo karteczek z notatkami z chińskimi literami.
Obraz Jeon Sang-O z Pixabay

Niewiele jest rzeczy tak pomocnych, jak dobrze umieszczony komentarz. Jednocześnie nic tak nie zaciemnia modułu, jak kilka zbyt dogmatycznych komentarzy. Nic nie jest tak szkodliwe, jak stary komentarz szerzący kłamstwa i dezinformację.

Komentarze nie są jak „Lista Schindlera”. Nie są one „czystym dobrem”. W rzeczywistości komentarze są w najlepszym przypadku koniecznym złem. Jeżeli nasz język programowania jest wystarczająco ekspresyjny (…) nie będziemy potrzebować zbyt wielu komentarzy.

Robert C. Martin – „Czysty kod – podręcznik dobrego programisty”

I teraz staje się jasne dlaczego nie pokazywałem Ci do tej pory nic na temat komentarzy. Powyższy fragment książki, która powinna się znaleźć w bibliotece każdego programisty, mówi wiele o dobrych praktykach w komentowaniu.

Problem z komentarzami jest taki, że ich treść ma tendencję do dezaktualizowania się niezwykle szybko. Bardzo często zdarza się, że kod jest edytowany przez programistę, ale komentarze pozostają bez zmian, nawet gdy dawno straciły swoją aktualność. Powoduje to, że niezwykle często treść komentarzy kłamie. Dlatego w większości przypadków zamiast komentować kod lepiej jest go poprawić w taki sposób, by komentarz nie był potrzebny. Kod napisany w taki sposób nazywamy po angielsku self-documenting code, czyli samodokumentujący się kod.

Self-documenting code

Weźmy np. sytuację, gdy chcemy przeliczyć tygodnie na sekundy.

// Ilość tygodni
int t = 3;

// Przeliczenie tygodni na sekundy. Sposób liczenia: Ilość tygodni * ilość dni w tygodniu * ilość godzin w dobie * ilość minut w godzinie * ilość sekund w minucie
int s = t * 7 * 24 * 60 * 60;

Odnotujemy ogromną poprawę już po samej zmianie nazw zmiennych. Po takim zabiegu napisanie komentarza jest już prawie nieuzasadnione.

int liczbaTygodni = 3;

// Przeliczenie tygodni na sekundy. Sposób liczenia: Liczba tygodni * liczba dni w tygodniu * liczba godzin w dobie * liczba minut w godzinie * liczba sekund w minucie
int liczbaSekund = liczbaTygodni * 7 * 24 * 60 * 60;

Jeszcze lepszym wyjściem byłoby utworzenie statycznej metody PrzeliczTygodnieNaSekundy(int liczbaTygodni). Taki zabieg skutecznie wyeliminuje potrzebę pisania komentarza.

int liczbaTygodni = 3;
int liczbaSekund = PrzeliczTygodnieNaSekundy(liczbaTygodni);

(...)

public static int PrzeliczTygodnieNaSekundy(int liczbaTygodni)
{
    return liczbaTygodni * 7 * 24 * 60 * 60;
}

Pozostaje kwestia opisania wewnątrz metody sposobu liczenia, ale po pierwsze można uznać, że dla innych programistów oglądających nasz kod sposób liczenia jest oczywisty (nie powinniśmy pisać komentarzy dotyczących oczywistych rzeczy – takie komentarze wyłącznie zaciemniają kod). Jeśli uznasz, że Twoje równanie nie jest oczywiste – to również poszczególne liczby w równaniu możesz zamienić na zmienne (lub nawet lepiej stałe) o odpowiednich nazwach i wykorzystać je w równaniu.

return liczbaTygodni * liczbaDniWTygodniu * liczbaGodzinWDobie * liczbaMinutWGodzinie * liczbaSekundWMinucie;

Ważne, by nie zostawiać uporządkowania swojego kodu na później. To tak nie działa! Prawdopodobnie jeśli napiszesz komentarz zamiast uporządkować kod, to ten nieporządek zostanie tam na dłuższy czas lub na zawsze. Dlatego…

Obraz Roy Buri z Pixabay

OK! Jak na pierwsze zetknięcie z komentarzami – chyba tyle informacji wystarczy :). Jeśli chcecie już teraz dowiedzieć się więcej o dobrych praktykach w komentowaniu kodu, koniecznie zaopatrzcie się w książkę Czysty kod. Podręcznik dobrego programisty. autorstwa Roberta C. Martina zwanego również Wujkiem Bobem (Uncle Bob). Tak jak już wspominałem zarówno w kursie wideo, jak i wcześniej w tym wpisie – jest to książka niezbędna dla każdego programisty. Dlatego warto w nią zainwestować. Jeśli przerobiłeś moje wszystkie dotychczasowe lekcje w kursie – myślę, że bez problemu tę książkę okiełznasz. Być może trafisz na kilka fragmentów, których nie zrozumiesz od razu – ale nie przejmuj się. Do tej książki i tak trzeba co jakiś czas wrócić.

2 Replies to “Komentarze w kodzie

  1. Rozumiem intencje ale pisanie tak długich nazw zmiennych procedur czy klas powoduje, że w zasadzie zostajemy skazani na korzystanie z podpowiedzi. Opisy długości kilkunastu znaków jestem skłonny zaakceptować ale nie takie które liczą 40 znaków i więcej. Główną zaletą tworzenia kodu przy użyciu krótkich nazw jest możliwość szybkiego przeniesienia myśli na papier (kod programu). Zgadzam się, że taki kod fajnie wygląda w prostych przykładach ale tam gdzie realizujemy złożone algorytmy to po prostu nie działa (patrzcie zapis wzorów matematycznych). Co więcej w tym przypadku komentarze i tak będą niezbędne by wyjaśnić intencje. Moim zdaniem by kod był samokomentujący nie może irytować. Źle jest gdy ktoś pisze zupełnie inaczej niż my (wcięcia, notacja, architektura, nazewnictwo). Problem pojawia się nie tylko w sytuacji gdy metody liczą wiele linii ale także wtedy gdy mamy złożoną strukturę klasową. Próba zrozumienia kodu, gdy musimy przechodzić przez wiele wywołań znacząc utrudnia czytanie a komentarze mogą znacząco ten proces ułatwić i skrócić.

    1. Dzięki za komentarz!
      Szanuję Twój punkt widzenia, bo programowanie nie jest dziedziną, w której na 100% można powiedzieć, że jakiś sposób postępowania jest prawidłowy. Przykładowo wiele praktyk w programowaniu obiektowym uznanych za jedyne słuszne, w programowaniu funkcyjnym są uznane za rażący code smell. A żeby było śmieszniej w dzisiejszych czasach często oba paradygmaty są mieszane. Z Twojego komentarza natomiast wyczuwam, że jesteś zwolennikiem programowania proceduralnego, mam rację?
      Ja osobiście, mimo że od momentu gdy napisałem ten wpis nabrałem dużego dystansu do cytowanej w nim książki popularnego „Wujka Boba”, to jednak moje podejście do komentarzy i self documenting code pozostaje niezmienione (zwłaszcza jeśli mówimy o OOP; w FP którego w międzyczasie stałem się fanem jest nieco inna bajka i tam bardzo często są wykorzystywane nazwy zmiennych takie jak np. x, y, n). Na co dzień jako .NET Developer zajmuję się programowaniem aplikacji o zastosowaniu biznesowym i choć niewątpliwie nazywanie jest trudne, to jednak nie napotykam większych problemów w stosowaniu podejścia self-documenting code z naprawdę minimalną (wręcz mikroskopijną) liczbą komentarzy, podobnie jak moi koledzy z zespołu. Nazwy o długości 40 znaków to faktycznie lekka przesada i zgadzam się, że trzeba dążyć do tego by nazwa była jak najbardziej zwięzła. Ale jeśli stajemy przed koniecznością nazwania czegoś przy pomocy „wypracowania”, to być może trzeba się zastanowić, czy to coś co nazywamy nie ma „na głowie” zbyt dużej odpowiedzialności – może trzeba podzielić to na mniejsze części nazwane krótszymi nazwami?
      Odnosząc się do śledzenia kodu: o ile architektura aplikacji jest czysta (np. onion architecture), a kod nie przypomina spaghetti, nie ma większego problemu w śledzeniu kolejnych małych fragmentów kodu. W przypadku C# większość programistów używa Visual Studio, niewielka część używa Visual Studio Code i Ridera. Bardzo niewielu programistów używa innych rozwiązań. Wyżej wymienione IDE/edytory pozwalają na sprawne śledzenie kodu (np. przy pomocy CTRL + click przenosimy się do definicji elementu na który klikamy). Występują w nich również rozwiązania takie jak Code Lens, które dodatkowo ułatwiają śledzenie wszelkich powiązań z danym fragmentem kodu. W tej sytuacji, w mojej opinii naprawdę nie ma wymówek na unikanie dzielenia kodu na mniejsze, dobrze nazwane moduły. To jest jedna z istotniejszych spraw w pisaniu czystego kodu.
      Natomiast wspomniałeś również o opisywaniu równań matematycznych – być może tu się właśnie kryje różnica w moim i Twoim podejściu. Jak wyżej wspomniałem – tworzę aplikacje biznesowe, a w takich aplikacjach bardzo rzadko jest konieczność stosowania wzorów matematycznych. Ogólnie C# wg mnie nie jest do takich celów najlepszym możliwym wyborem. Poza tym we wzorze matematycznym użycie zmiennej o nazwie x (nawet w OOP) to jest wg mnie całkowicie normalna rzecz (choć może faktycznie zależy to trochę od wzoru).

Dodaj komentarz