Dziś jest poniedziałek, 13 października 2008 roku (z kalendarza...)

Styl kodowania

Na forum Invenzzia toczy się aktualnie dyskusja dotycząca stylu kodowania w Open Power Libs, w związku z tym postanowiłem poświęcić temu zagadnieniu nieco miejsca na własnym blogu. Skoncentruję się głównie na perypetiach związanych z moim stylem oraz paru przemyśleniach na tę kwestię.

Języki pokroju C++ czy PHP dają programiście dużo swobody w kreowaniu układu graficznego kodu. Z jednej strony jest to zaleta, gdyż nie mamy tutaj sytuacji, że autorzy języka wymyślili coś niestrawnego, ale konsekwencją są tysiące stylów będących w użyciu.

Podstawowe założenia

Mam kilka prostych zasad dotyczących stylu:

  • Musi być dopasowany do struktury i mechanizmów danego języka.
  • Powinien być dopasowany do techniki, jaką rozwiązujemy problemy programistyczne.

Zasada funkcjonowania drugiego punktu jest następująca: nie jestem purystą, uważam że nie zawsze "strukturalnie" znaczy "czytelnie". Część problemów można znacznie logiczniej zapisać (szybciej również), godząc się na użycie 'break' i podobnych. W związku z tym mój styl jest ułożony tak, aby nie przeszkadzał w ich używaniu. Co więcej, zachodzi tu jeszcze inne zjawisko - jeśli styl z natury jest rozwlekły, programista podświadomie dąży do zbicia go w inny sposób, najczęściej przez racjonalniejsze wykorzystanie struktur kontrolnych.

Klamerki i wcięcia

Na początku mojej przygody z językami a'la C++ przyjąłem wcięcia robione dwoma spacjami i nawiasy klamrowe otwierane w tej samej linii, co instrukcja. Później "odkryłem" tabulacje i doszedłem do wniosku, że robienie wcięć spacjami to jedno wielkie nieporozumienie. Wystarczą inne ustawienia u innej osoby i już wszystko się rozwala przy próbie modyfikacji. Kursor jeździ po linijce wolniej, można wpisać tekst w połowę wcięcia, a sytuacje, gdy trzeba coś dokładnie wypozycjonować na konkretną kolumnę, można było policzyć na palcach (teraz w ogóle ich nie doświadczam).

Klamerki jednak się ostały i nawet lubiłem ten styl. Niestety - mnóstwo programistów PHP stawiało je w nowej linijce i gdy rozpoczęła się przygoda z Open Power Template, trzeba było się dostosować. O dziwo, nowy styl spodobał mi się nawet bardziej. Wprawdzie linijek produkuje się więcej, ale zgodnie z tym, co napisałem wyżej, zacząłem racjonalniej nimi gospodarować. Niestety, ktoś tu najwyraźniej ma problem z orientacją. Zaczął ukazywać się Zend Framework, a tam klops - klamry w tej samej linijce, co instrukcja. Wyszło parę kolejnych bibliotek, to samo. Tym razem nie ma głupich, co trzy lata nie będę z tego powodu zmieniać stylu. Jak będę musiał pracować przy projekcie z takim wariantem, prędzej sobie napiszę skrypt do konwersji na mój format przy ściąganiu kodu z SVN i zamianie z powrotem na "oficjalny" przy eksporcie.

Nazewnictwo

Dzięki OPT, jest to obecnie camelStyle. Obecnie w związku z zaczęciem korzystania z autoloaderów, do nazw klas przerzuciłem się na Nazwa_Czegostam, gdyż się to łatwiej przerabia na ścieżkę katalogową. Czekam z niecierpliwością na przestrzenie nazw w PHP, które spowodują pewnie kolejne usprawnienie.

Wyrażenia

Generalnie wszystkie składowe wyrażeń separuję jedną spacją. Kompresję stosuję tylko w wyjątkowo długich potworkach, gdzie istnieje potrzeba, by jak najwięcej naraz widzieć. Jest tu tylko parę wyjątków:

  • W PHP: kropka - właśnie przeważnie takie wyrażenia się by szybko robiły przydługawe.
  • Operator ?: który zawsze jest separowany spacjami dla podkreślenia całości, a na dokładkę otaczany nawiasem.
  • Negacja - stawiana bezpośrednio przy tym, co neguję.

Dodatkowo przy otwieraniu i zamykaniu nawiasów wewnętrzne wyrażenie zsuwam do takowych: a + (b * c)

Struktury kontrolne

for:

// typowa pętla
for(inicjacja; zakończenie; iteracja)
{
    treść
}
 
// pętla z pustym ciałem
for(inicjacja; zakończenie; iteracja){ }
 
// pokaz pomijania warunków
for(; ; )
{
 
}

if:

if(warunek)
{
 
}
elseif(drugi_warunek)
{
 
}
else
{
 
}

Jeżeli coś się musi wykonać po spełnieniu pewnego koniecznego warunku, gdyż w przeciwnym razie mamy błąd, dokonuję kompresji w następujący sposób:

if(!warunek)
{
    // tu wysyłamy gościa na bambus
    echo 'Bunt!';
}
 
// Żeby dojść do tego miejsca, trzeba spełnić warunek
echo 'coś sobie robię...';

Unika się w ten sposób dodatkowej klauzuli, dodatkowych klamer i dodatkowego wcięcia. Wystarczy spróbować sprawdzić tak 10 warunków i porównać wyniki.

while w zasadzie identycznie, jak for, tyle że bez średników w środku. W PHP foreach wygląda następująco:

foreach($tablica as $indeks => $wartosc)
{
    // robmy coś
}

switch:

switch(wyrazenie)
{
    case wartosc1:
    case wartosc2:
        // robimy cos
        break;
    case wartosc3:
        // robimy cos
    case wartosc4:
        // jeszcze cos robimy
        break;
    case wartosc5:
        if(costam)
        {
             // cos robmy
             break;
        }
    case wartosc6:
        // cos robmy
        break;
    default:
        // domyslne
}

Tutaj styl kodowania dostosowany do możliwych wariantów przepływu sterowania, jaki instrukcja oferuje. Z powodu tego, że jak najbardziej akceptuję możliwość przeskoku do następnego przypadku, nie oddzielam takowych spacją, ani specjalnie nie wyróżniam break, gdyż to powodowałoby z kolei problem z fragmentem wartosc5 i wartosc6. Jak wtedy go wyróżnić, skoro jest warunkowy?

do... while:

 
do
{
    // costam
}
while(warunek);

Bardzo rzadko używana pętla - u mnie w zasadzie dopiero przy pracach nad OPTv2 się częściej zaczęła pojawiać i się okazało, że w zasadzie nie mam żadnej konwencji na jej formatowanie. Ostatecznie zostało coś takiego, jak powyżej.

Funkcje:

 
int nazwa(int parametr1, int parametr2)
{
 
} // end nazwa();
 
function nazwa($parametr1, $parametr2, $parametr3 = opcjonalny)
{
 
} // end nazwa();
 
public function nazwa($parametry)
{
 
} // end nazwa();

Komentarze z nazwą funkcji przy końcowych klamerkach to mój znak rozpoznawczy :). Naprawdę, nie mam pojęcia, skąd to wytrzasnąłem, ale patrząc z perspektywy czasu stwierdzam, że pomysł jest genialny. Nieważne, z której strony przewijam kod, zawsze widzę nie tylko, co się gdzieś zaczyna, ale co się też w danym miejscu kończy. Komentarz taki dodaję również do klas i interfejsów, lecz tam zapisuję nazwę wyłącznie ze średnikiem, już bez nawiasów. W C++ podaję samą nazwę metody, z pominięciem klasy.

Inne konwencje

W PHP mam przyjętą praktykę dotyczącą funkcji mających coś odnaleźć i zwrócić. W przypadku niepowodzenia zwracaną wartością jest zawsze NULL, a nie żadne false. Rozwiązuje to hipotetyczny problem, co robić, gdy false będzie znalezioną wartością, a nie powiadomieniem o niepowodzeniu? Moim zdaniem taki zapis wygląda logiczniej.

Niekonwencjonalne rozwiązania kodowe

Jak uniknąć dziesiątków instrukcji warunkowych decydujących, czy włączyć jakąś funkcję w zależności od stanu pewnej zmiennej?

 // Statemaker
$zmiennaStanu1 and funkcjaDoWykonania1();
$zmiennaStanu2 and funkcjaDoWykonania2();
$zmiennaStanu3 and funkcjaDoWykonania3();

Masowe tworzenie zmiennych:

$zmienna1 = $zmienna2 = $zmienna3 = $zmienna4 = 0;

Operacja przetwarzania i interpretowania - tutaj właśnie znakomitym uproszczeniem są elementy niestrukturalne, ponieważ świetnie oddają istotę sprawy. Aby symbol mógł być zamieniony, musi spełnić pewne warunki. Zamiast kombinować niepotrzebnie, wystarczy sprawdzać je liniowo. Jeśli wszystkie weszły, znaczy to, że wszystko jest w porządku i możemy działać:

foreach($dane as $symbol)
{
    if(!warunek1)
    {
        continue;
    }
    if(!warunek2)
    {
        continue;
    }
    if(!warunek3)
    {
        continue;
    }
    // wszystko jest OK
    przetworz($symbol);
}

Zasada jest prosta - jeżeli dany symbol nie wszedł, to jest oczywiste, że nie ma sensu iść dalej i przerywamy cały proces sprawdzania. Gdyby to sobie rozrysować, wyglądałoby to jak prosta linia idąca w dół, od której z jednej strony odchodzą strzałki wracające z powrotem ilustrujące, co się dzieje z tymi, co odpadają. Wariacje na temat tego schematu są podstawą kodu Open Power Template'a.

Zakończenie

Wypracowanie stylu programowania zajmuje trochę czasu, ale z pewnością warto inwestować w tym kierunku. Zachęcam do dzielenia się własnymi pomysłami zarówno w komentarzach, jak i na innych blogach.

Powrót

Komentarze

Napisał Hashedone w sobotę, 14 czerwca 2008 o 17:26

Wiele mi się swoją drogą przejęło z OPT:P I te komentarze na końcu funkcji to już nie tylko Twój znak rozpoznawczy bo też je stosuje. Tylko nie lubię klamerek w nowej linii. I ciekawi mnie jeszcze co robisz z instrukcjami warunkowymi po których jest jedno wyrażenie. Też używasz klamerek, piszesz w jednej linii, czy w następnej po tabulacji? Poza tym przydatny artykuł, dał mi do myślenia że pewne rzeczy trzeba usystematyzować...

Napisał Zyx w sobotę, 14 czerwca 2008 o 22:08

Tak, klamerki są zawsze:

if(warunek)
{
    echo 'foo';
}


W ten sposób w przypadku debugowania mogę szybko i prosto wstawić tam jakieś dodatkowe echo, komentarz elegancko zamieścić itd.

Napisał KODON w sobotę, 14 czerwca 2008 o 23:04

Zawsze stawiam klamerki w osobnej linijce - kod zdaje się być czytelniejszy. Jeśli nie ma potrzeby, po prostu ich nie używam. :) Największe kłopoty z formatowaniem sprawiają długie zapytania SQL ze wstawkami PHP.

Niekonwencjonalne rozwiązania - ciekawe pomysły, które mogą zmniejszyć objętość i zawiłość kodu. Niestety kod może być niezrozumiały dla początkujących (choć oni raczej nie wnikają w zaawansowane struktury kodu ani ich nie edytują).

Odnośnie wcięć - w pewnym artykule w wortalu PHP autor radzi, aby zamiast TAB używać spacji. Błędne podejście. Dobry edytor pozwala ustalić szerokość wcięcia - można ją ustalić zgodnie ze swoim przyzwyczajeniem.

Pozostała jeszcze kwestia nazewnictwa zmiennych i funkcji. Ostatnio zacząłem przerabiać nieregularny styl na następujący: $varName, CONSTANT, $lang['keyName'], Function, method...

Napisał CyberBoB w niedzielę, 15 czerwca 2008 o 09:02

Komentowanie końca funkcji wprowadził prawdopodobnie jako pierwszy Niklaus Wirth, więc to raczej jego znak rozpoznawczy..Kiedyś również te komentarze stosowałem, jednakże są one bardzo niepraktyczne, przy zmianie nazwy metody, trzeba zmieniać komentarz. A co do pokazywania do jakiej metody należy ostatni wąsik to wystarczy dobry edytor np. PDT. Nie zgodzę się również z stosowaniem tabulacji, dziwny jest dla mnie argument z problemem pozycjonowania bloków, porządny edytor nie pozwoli na pisanie w połowie wcięcia, a można po parach spacji skakać jak po tabulacjach, z drugiej strony tabulatory w każdym edytorze wyglądają inaczej i szybkie zmiany w konsoli czy jakimś webowym edytorze to jest koszmar.

Napisał Vane w niedzielę, 15 czerwca 2008 o 10:06

Ja od zawsze stosuję zapis (o ile formatowanie na tym blogu czegoś nie rozwali)

 
if(cos) {
  $camelCase;
  $a = (cos) ? true : false;
}


Jeżeli jest tylko jedna linia (chociaż miałem z tego powodu wątpliwości - przenosić do nowej linii czy nie)
 
if(cos) { return true; }


Odkąd używam ZF nazwy klas pozmieniałem zgodnie ze stylem Odwzorowanie_Do_Katalogu. Zacząłem dodawać również komentarze do wszystkiego nawet jeżeli wydaje się oczywiste. Jako wcięcia 2 spacje jako że niektóre edytory miały skłonność do rozwalania kodu z tabulacjami.

A u Ciebie Zyx co mi się nie podoba to jakaś dziwna zadyma z wyjątkami. Przerzucasz je przez funkcję, nie ma podziału w stylu OPT_IO_Exception, OPT_Plugin_Exception.

Napisał Zyx w niedzielę, 15 czerwca 2008 o 10:12

W kolejnym devie wyjątki będą już "po ludzku" zrobione w związku ze zmianami w systemie obsługi błędów i unifikacją wszystkiego.

Napisał Kłeczek Marcin w niedzielę, 15 czerwca 2008 o 15:43

Hm.. cała rozważanie co do wcięć i klamerek nie ma sensu, bo można łatwo przerformatować - dużo ważniejsze jest nazewnictwo zmiennych, klas (właśnie użycie _ dla autoloadera). Dobrą metodą jest oznaczanie rodzaju przechowywanych treści w zmiennych przy użyciu prefiksów fZmiennaFloat, sZmiennaString - lub okręslanie ich zakresów tWysokosc, gWysokosc (np. przy przetwarzaniu obrazkow zmienna lokalna, uzywana przy obliczaniu "czegos" i zmienna wysokosci dla calego obrazka)... ale to juz jest na osobny artykul temat... Zyx - czekamy!

Napisał Termit w niedzielę, 15 czerwca 2008 o 17:48

OK, dawno nie pisałem w PHP, ale sprawdziłem w manualu (bo coś mi tak się nie podobało) i wydaje mi się, że źle napisałeś w artykule pętle foreach ;).

Napisał MySZ w poniedziałek, 16 czerwca 2008 o 04:06

foreach($tablica => $indeks as $wartosc)

A co to takiego? W PHP nie znam takiej konstrukcji ;) Tak, wiem, czepiam się ;)
Generalnie mam całkowicie inny styl, częściowo wymuszony przez przyjęte konwencje w pracy, częściowo wybrany podczas iluś tam lat własnych doświadczeń. No dobra, nie całkowicie, też _zawsze_ używam klamerek przy warunkach ;)
Z Twojej wersji najbardziej nie podobają mi się klamerki w osobnych liniach - przez to później są problemy ze zmieszczeniem funkcji na jednym ekranie (tak, wiem, kwestia ekranu ;) ), reszta byłaby strawna gdybym musiał coś w tym stylu pisać ;)
Oczywiście potraktuj to jako luźne uwagi czytacza, w żadnym wypadku jako krytykę :)

Napisał m_gol w poniedziałek, 16 czerwca 2008 o 13:08

Chyba wywaliło mój komentarz... Chciałem napisać, że u nas na Programowaniu Współbieżnym obowiązującym stylem kodowania jest ten spod adresu:
http://www.mimuw.edu.pl/~mengel/PW/PUBLIC-PW/06_zadanie/standard.txt

Napisał Zyx w poniedziałek, 16 czerwca 2008 o 19:42

Foreach poprawiony. Tak właśnie mi się zdawało, że coś jest z nim nie tak, jak to pisałem :).

Dodam, że u mnie właśnie ograniczenia na długość funkcji nie ma. Fakt, siłą rzeczy dążę, by kod był zwięzły i elegancki, ale naprawdę nic się nie dzieje, jak coś zajmuje np. 200 czy 300 linijek. Jeśli taka jest potrzeba, a dany kod na pewno nie musi być zmodularyzowany, to nie widzę powodu, żeby specjalnie wszystko spowalniać i na dokładkę zmuszać się do skakania po nim, kiedy mogę mieć wszystko elegancko po kolei.

m_gol -> nie miałem w adminie żadnego Twojego komentarza do tego wpisu wcześniej.

Napisał Sędziwój w czwartek, 19 czerwca 2008 o 23:35

Dlaczego kod metody dłuższej najlepiej rozbić, no właśnie po to aby było łatwiej czytać. Mówisz o skakaniu, ale to jest potrzebne, jeśli akurat ten fragment musisz sprawdzić. Prosty przykład: http://www.industriallogic.com/xp/refactoring/composeMethod.html (znam lepsze artykuły, ale nie mam czasu szukać)

Co do formatowania, to mnie klamerki otwierające w nowej linijce wkurzają, ale to moja opinia, szef je stosuje, ale mnie do nich nie przekonał. W tej samej linijce są stosowane w Java, więc że trochę w niej pisałem, tam to poznałem. Co zwiększa czytelność kodu, bo nie jest rozwlekły, przy wielu blokach instrukcji (python pokazuje że z wcięciami są nawet nie potrzebne, więc po co dawać im osobne linijki).
Co do dawania nawiasów jeśli jest jedna instrukcja, zawsze, potem za często się pojawiają przez to błędy, a przy nomenklaturze otwierania nawiasu w tej samej linijce co warunek (czy cokolwiek) nie przeszkadza. Do tego nawet jak jedna linijka instrukcji jest, _zawsze_ w nowej linii, przecież inaczej to łamanie konwencji, czyta się kod widzi się if'a czyta kolejną linijkę "a to chyba nie ta bo nie ma wcięcia", tylko zmniejsza czytelność, jak lecimy wzrokiem po kodzie, musimy wtedy się chwilę zastanowić, lub pominiemy co gorsza.
Co do nazewnictwa, funkcje, metody i zmienne zawsze z małej, stałe całe z dużych, klasy zawsze z dużej (z wyjątkami wymuszonymi na mnie), co do _ w nazwie klas, bez problemu można autoladera napisać tak, że będzie jadł i duże literki, a kwestia wydajności, przecież raz się generuje, mapę (ale to nie ten temat).
Nazwy zmiennych mają podany typ który przechowują (str/int/bln/arr itp.) jeśli obiekt, to często nazwę ma jak klasa (bo po prostu mówi co to to jest), jeśli jest więcej to konkretyzuję.
Preferuję tabulację, czemu? Bo każdy może sobie w środowisku ustawić ile to ma ten tab zajmować znaków, a spacje trzeba było zmieniać, co jest niewygodne. Ale z tabulacji na spacje i vice versa jest łatwo przejść.

Dość zmarginalizowana sprawa, a bardzo ważna, to dokumentowanie kodu, czyli co przyjmuje, co zwraca i opis. Jak się pracuje w PDT to z dobrą dokumentacją o wiele szybciej się pracuje. Szczególnie jak jest w miarę obiektowo napisane.

P.S. Zmień sposób obsługi niepoprawności w dodawaniu komentarza, bo obecny jest wku*cy, powinno powrócić do formularza z treścią i wiadomość co jest nie tak.

Napisał Zyx w piątek, 20 czerwca 2008 o 14:57

Ad. PS -> pewnie, że powinno, ale Zyxist.com powstał dużo wcześniej, niż Open Power Forms. Będzie (kiedyś) nowa wersja, będą lepsze formularze, a póki co zapraszam do korzystania z przycisku "Wstecz". Z moich doświadczeń wynika, że pomaga.

Napisał luinnar w wtorek, 24 czerwca 2008 o 00:01

Używam takiego samego stylu kodowania co Ty, tylko do zmiennych stosuję notację węgierską.

Co do sporu spacja vs tabulator u mnie wygrał tabulator z takich samych powodów jak u Ciebie.

Nie popieram pisania nazw klas z podkreślnikami, tłumaczenie że to do autoloada jest dla mnie bezpodstawne, ponieważ i tak generuje się mapę.

Kwestia pozycji klamer w kodzie jest dla mnie oczywista: klamra = nowa linia.

Ogólnie o stylach kodowania można pisać latami, a i tak każdy będzie pisał po swojemu.

Strona 1 z 1 :: 1

Skomentuj

NickInformacja
E-mailTylko do użytku wewnętrznego.
WWWNie zapomnij o http://
LayoutNapisz tu, czy widzisz dzienny czy nocny layout.
WpisFormatowanie wiki
Internauto, pamiętaj! Wolność to nie samowola - dbaj o kulturę wypowiedzi oraz dyskusji w sieci.

Na Zyxist.com panuje swoboda wyrażania opinii oraz krytyki pod dowolnym adresem. Jedyny warunek: musi być ona kulturalna i rzeczowa. Na chamstwo, prostactwo lub jawne obrażanie kogokolwiek nie ma tu miejsca i takie komentarze są bardzo szybko usuwane. Jeśli zamierzasz polemizować z treścią wpisu, wpierw uważnie ją przeczytaj.

© Tomasz "Zyx" Jędrzejewski 2005 - 2008 | Wykonanych zapytań: 2 | Serwer wirtualny zapewnia