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.















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ć...