standard kodowania

O standardach pisania kodu programu powiedziano już bardzo wiele. Jest to jeden z najbardziej wyrazistych aspektów ludzkiego wymiaru programowania — styl kodu nie ma przecież żadnego znaczenia dla kompilatora i jego "jakość" to wyłącznie subiektywne odczucia programisty. A jednak sprawa od zawsze budzi wiele emocji — kod może być "elegancki" i "ładny" albo "brzydki" i wtedy taktowany jako produkt niskiej jakości (niezależnie od jakości skompilowanego programu). Nic dziwnego zatem, że powstają nawet oficjalne wytyczne na temat stylu kodowania, np. Suna dla Javy lub Microsoftu dla .NET-u oraz dziesiątki innych opracowań (prywatnych lub korporacyjnych), które zazwyczaj bazują na powyższych lub innych sławnych źródłach typu "C++ Coding Standard" Todda Hoffa.

Dzisiaj zupełnie przypadkowo natknąłem się na dokument "IDesign C# Coding Standard". Trochę się wczytałem i zamieszczam poniżej małą polemikę z częścią pierwszą tego dokumentu — "Naming Conventions and Style". Aby za bardzo nie przynudzać i nie ograniczać się wyłącznie do języka C#, pominąłem kilka punktów całkiem specyficznych dla platformy .NET a równocześnie całkiem oczywistych.

"IDesign C# Coding Standard" (wersja 1.81) przedstawia następujące postulaty:

  1. Nazwy klas i metod wg PascalCase.
    Oczywiste. Konwencja stosowana we wszystkich nowoczesnych (zwłaszcza obiektowych) językach programowania. Właściwie to nie wybór — to konieczność.
  2. Nazwy zmiennych i argumentów wg camelCase.
    Mniej oczywiste, ale i tak całkiem jasne. Taka koncepcja zapewnia spójność nazewnictwa i wydaje mi się lepsza niż pomysły nazywania zmiennych np. samymi małymi literami ze znakiem _ w środku.
  3. Nazwy interfejsów od I.
    Jasne. Litera I może się nieprzyjemnie kojarzyć z notacją węgierską (oznaczanie typów zmiennych literami na początku nazwy), ale interfejs jest na tyle specyficzną konstrukcją, że zasługuje na takie oznaczenie.
  4. Nazwy prywatnych zmiennych klas od m_.
    Nie stosuj(ę|emy) i nie lubię takich koncepcji. Kod musi się dobrze czytać, a różne dziwne przedrostki tylko to utrudniają. Częściej zamiast tego daje się this., tym bardziej, że każde rozsądne IDE po wprowadzeniu kropki podpowiada składowe klasy. ;-)
  5. Nazwy klas atrybutów zakończone na Attribute.
    Może, choć nie nalegam (prawdopodobnie z braku doświadczenia z takimi metodami).
  6. Nazwy klas wyjątków zakończone na Exception.
    Jak najbardziej.
  7. Nazwy metod w postaci akcja-podmiot.
    Zdecydowanie tak, np. ShowDialog(), RunTest() itp. Chociaż metody operujące na danym obiekcie bez tworzenia nowych bytów powinny raczej mieć nazwy w postaci samego czasownika akcji, tak aby wywołanie metody nie dublowało informacji o obiekcie, np. blog.Publish() a nie blog.PublishBlog().
  8. Metody zwracające wartość powinny mieć nazwy typu GetObjectState().
    Jak najbardziej. Dodatkowo dla metod zwracających wartość logiczną najlepiej używać Is zamiast Get, np. IsPublished(). Możliwe i dopuszczalne są również inne przedrostki typu Has, Can itp. jeżeli tylko gwarantują lepszą czytelność kodu, np. HasPermission(), CanPublish(). Analogicznie metody ustawiające właściwości obiektów powinny się zaczynać od czasownika Set, np. SetUserName().
  9. Znaczące, opisowe nazwy zmiennych.
    1. Bez nazw jednoliterowych.
      Konieczność stosowania sensowych nazw zmiennych jest oczywista, jednak unikałbym generalizowania. Niektóre zmienne jednoliterowe niczemu nie szkodzą, np. nazywanie licznika lokalnych zmiennych przez i — to wręcz tradycja uświęcona latami doświadczeń kilku pokoleń programistów.
    2. Bez notacji węgierskiej dla składowych publicznych i chronionych.
      Popieram w całej rozciągłości. IMHO notacja węgierska to jedna z największych pomyłek w historii programowania. Ale jeśli ktoś już koniecznie musi to stosować to przynajmniej niech ta paranoja nie wyjdzie poza zmienne lokalne.
    3. Bez skrótów.
      Nie upierałbym się. Niejednokrotnie skróty (zwłaszcza oczywiste, typu Num zamiast Number) zgrabnie ułatwiają korzystanie z metod o długich nazwach.
  10. Używanie raczej typów predefiniowanych (aka prymitywnych) niż aliasów w namespace System.
    Czyli string zamiast String, object zamiast Object itp. Popieram.
  11. Znaczące nazwy przestrzeni nazw (namespace).
    Zdecydowanie popieram. Można nawet wzorować się tutaj na koncepcji nazewnictwa pakietów w Javie.
  12. Utrzymanie stałej konwencji wcięć bloków kodu.
    Jak najbardziej, chociaż nie rozumiem dlaczego autor proponuje akurat 3 spacje zamiast tabulatora. Dobre IDE czy też edytor kodu powinien ułatwiać (jeśli nie wręcz wymuszać) stosowanie jednolitych wcięć. Visual Studio .NET robi to bardzo ładnie przez wstawienie tabulatorów i spozycjonowanie nawiasów w całym bloku w momencie wpisania }.[1]
  13. Komentarze na takim samym poziomie wcięcia jak kod komentowany.
    Dla komentarzy blokowych jak najbardziej tak, choćby z czysto estetycznych powodów. Natomiast komentarze na końcu linii wyrównywałbym również w ramach bloku kodu lub metody, zwłaszcza jeśli komentarze z różnych linii są ze sobą powiązane — dzięki temu powstaje jakby margines, na którym równolegle do kodu jest zapisany tok myślenia: "robimy to, teraz to, jeżeli tak to tędy, jeżeli nie to tamtędy".
  14. Wszystkie komentarze poprawne ortograficznie.
    Zdecydowanie tak. To jest w końcu jakaś dokumentacja i wyjaśnienie efektów pracy profesjonalisty, a nie blog nastolatki. Jestem również za pisaniem w pełni polskich (nie "polskawych") komentarzy, nawet jeśli byłyby to jedyne polskie słowa i jedyne wystąpienia polskich znaków diakrytycznych w całym kodzie.
  15. Wszystkie składowe klasy zadeklarowane na jej początku, z jedną linią odstępu przed metodami lub właściwościami klasy.
    Jak najbardziej tak, z powodów nie tylko estetycznych. Dodatkowo, jeśli można wyróżnić jakieś grupy pól obsługujące różne aspekty zachowania danej klasy, to można między tymi grupami również dać linię przerwy.
  16. Deklaracja zmiennej lokalnej najbliżej miejsca jej faktycznego użycia.
    Oczywiście. Skoro nie piszesz w archaicznym C to nie ma najmniejszego powodu (ani sensu), żeby zmienne lokalne były deklarowane na początku metody jeśli są wykorzystywane dużo "niżej".
  17. Nazwa pliku powinna odzwierciedlać klasę jaką zawiera.
    Albo przeznaczenie klas jeśli jeden plik zawiera ich kilka. Nazwa pliku źródłowego jest tak samo istotna jak nazwa klasy.
  18. Przy deklaracjach klas częściowych, każdy plik zawierający część deklaracji klasy powinien się nazywać tak samo z przyrostkiem P i numerem kolejnym części.
    Nazwy plików i tak powinny odzwierciedlać zawartość, a w tym przypadku kilka plików ma merytorycznie jedną zawartość (partial class), więc i jedną nazwę. Numer kolejny ma sens; P to szczegół.
  19. Nawias otwierający blok { zawsze w nowej linii.
    Zdecydowanie popieram. Notacja, w której { jest na końcu linii, a nie w nowej, mnie osobiscie irytuje. Oczywiście są to dwie przeciwne szkoły zwalczające się od lat, i żadna nie ma zdecydowanej przewagi. To są bardzo indywidualne ustalenia, ale ważne żeby przestrzegać jednej konwencji w kodzie całego projektu.

Oczywiście nie musisz się z powyższym zgadzać, żeby Twój kod był "ładny" i "porządny" — wszystko zależy od ustaleń między uczestnikami projektu. Tylko niestety bardzo często uczestnicy projektu (zwłaszcza zespół wykonawczy) zmieniają się w czasie jego trwania. Może więc kodowanie w stylu (u)znanym w świecie ma więcej sensu niż się z pozoru wydaje...

[1] W ramach anegdoty: mieliśmy kiedyś w zespole takiego kolegę, który w ogóle nie stosował wcięć bloków kodu. Wcale. Nic a nic. W efekcie produkował kod (w Javie zresztą), w którym znajdowały się takie fragmenty:

}
}
}
}
}
}

Prostota tego pomysłu jest doprawdy wzruszająca. Jednak nikt, kto nie był do tego zmuszony, nie usiłował dociekać czego który nawias dotyczy i z własnej woli nie podejmował się modyfikacji takiego kodu. Projekt szczęśliwie dogorywał poza naszym zespołem a wspomniany kolega już nie pracuje w tej firmie. Tak bywa...

Komentarze

#1 | 2004.05.23 22:24 | Shot

Ad 12 – rozumiem, że sam używasz tabulatora? Ja się za cholerę nie mogę zdecydować, czy pozostać przy trzech spacjach, czy ustawiać środowisko na trzyspacjowego taba, który po pierwsze wydaje się „koszerniejszy”, a po drugie wiem, jak idiotyczne wydaje mi się wymaganie czterech spacji w PEAR Coding Standards…
Ad 19 – wolnostojący { też kiedyś traktowałem jako bardziej elegancki z logicznego punktu widzenia, ale jednak estetyka (wiem, wiem, DGCC…) zostawiania go na końcu linii i oszczędność miejsca z czasem zwyciężyły. :o)

#2 | 2004.05.23 23:22 | MiMaS

Ad 12 – rozumiem, że sam używasz tabulatora?

A właśnie, że nie zawsze... W C# tak, ale np. w PHP preferuję dwie spacje. Nawet nie wiem dlaczego — po prostu taki nawyk. Natomiast nieparzysta liczba spacji (np. 3) wydaje mi się tak dziwna i nienaturalna, że nawet jeśli by mi się to kiedyś przydarzyło, to chyba nie przyznałbym się do takiego kodu. ;-)

Ad 19 – wolnostojący { też kiedyś traktowałem jako bardziej elegancki z logicznego punktu widzenia, ale jednak estetyka...

Właśnie — estetyka nie dopuszcza { w tej samej linii co nagłówek metody lub warunek... ;-) Chociaż przyznam się, że wbrew wszelkiej logice zdarza mi się napisać coś takiego:

if ( warunek )
{
...
} else {
...
}

#3 | 2004.05.24 00:05 | Shot

Natomiast nieparzysta liczba spacji (np. 3) wydaje mi się tak dziwna i nienaturalna, że nawet jeśli by mi się to kiedyś przydarzyło, to chyba nie przyznałbym się do takiego kodu. ;-)

Trzy spacje pozwalają na zakomentowanie linijki przez // bez rozwalania wcięć, i głównie za to je lubię. A cztery to za dużo. :o)

A nie, wróć. Ja jednak używam trzyspacjowego taba, tylko o tym zapomniałem. To właśnie trzyspacjowy tab pozwala na takie zakomentowanie, o.

A co do if-ów: co sądzisz o jednolinijkowych, tzn. takich w ogóle bez klamr?

#4 | 2004.05.24 13:54 | MiMaS

Jeśli kod wykonywany warunkowo składa się z jednego wyrażenia (zwłaszcza krótkiego) to myślę, że spokojnie można go zapisać w tej samej linii za warunkiem. To jest zresztą zgodne z Braces {} Policy wg Todda Hoffa. W praktyce jednak musi to być bardzo krótkie wyrażenie i bardzo krótki warunek żeby wyglądało to znośnie. Dlatego często zapisuję to w następnej linii, jednak również bez {}. Dziwnie? Może... ;-)

#5 | 2004.05.24 15:15 | Shot

No nie no, if z wyrażeniem w osobnej linii, ale bez klamr to jak dla mnie abominacja zupełna, na dodatek niezgodna z tąże „Braces {} Policy”, iwogóle. ;o)

A do jednolinijkowych się jakoś przyzwyczaić nie mogę, ale też jakoś zbytnio nie potępiam, chyba.

#6 | 2004.05.30 09:56 | Shot

Jeszcze co do dyskusji spacje/tabulatory – właśnie przewija się toto przez debian-user, początek tutaj.

#7 | 2004.06.01 19:16 | MiMaS

C and C++ Style Guides

This page offers access to many style guides for code written in C and C++, as well as some discussion about the value and utility of such style guides.

 

Uwaga: Ze względu na bardzo intensywną działalność spambotów komentowanie zostało wyłączone po 60 dniach od opublikowania wpisu. Jeżeli faktycznie chcesz jeszcze skomentować skorzystaj ze strony kontaktowej.