[C++] Jak to jest z tą tzw. "dobrą praktyką" programistyczną

Otóż sprawa rozchodzi się o pojęcie “dobrej praktyki”.

Kilka razy już spotkałem się z opinią, że stosowane prze zemnie szeroko rozwiązanie

while(true)

{

polecenia

if(warunek) break;

}

Na przykład do różnego rodzaju menu, zamiast preferowanego

do

Polecenia

while(warunek);

Nazywane było “złą praktyką”. Generalnie (nawet raz kazano mi) tak nie robić.

Natomiast nikt nigdy mi nie był w stanie wyjaśnić czemu niby moje rozwiązanie jest złe?

  1. Teza jaką słyszałem, to że nie ładnie tworzyć pętle nieskończone.

No ale zaraz, przecież każdy program z API działa w takiej pętli! Nie wchodząc już w ten drobny szczegół, że na poziomie ASEMBLER-a i tak wszystkie moje pętle są tak właśnie rozwiązane.

Tak więc czemu ja, jako programista miałbym tego nie stosować?

  1. Teza jaką słyszałem, to że nie ładnie jest wychodzić z pętli za pomocą break-a;

No zaraz, przecież w instrukcji case nawet należy stosować break-i, tak więc najwyraźniej nie jest to takie zło jak by się mogło wydawać.

Poza tym, skoro język zezwala na takie zastosowanie to czemu ich nie stosować?

Tak więc chciałbym się dowiedzieć czy twierdzenie o “złej praktyce” jest oparte na jakiś sensownych argumentach, czy jest to porostu coś niezgodnego z akademickim schematem profesura?

  1. Pętle nieskończone są ok, czasami nawet niezbędne.

  2. Używaj sobie break’a gdzie i kiedy chcesz :wink:

Generalnie chodzi tylko o to żeby kod był przejrzysty, czytelny i spawnie działał, takie:

do

{

}

while(cond);

jest bardziej jasne od:

while(1)

{

     if(cond)

          break;

}

Cóż… u mnie na studiach mówili, że nie należy stosować break-a, continue i podobnych rozwiązań w językach wysokopoziomowych. Uzasadniali to z kilku powodów:

Np.:

Tutaj masz dwa warunki, które należy sprawdzić.

while(true)

{

polecenia

if(warunek) break;

}

Tutaj tylko jeden.

do

Polecenia

while(warunek);

Pojawia się pewna oszczędność (zwłaszcza jak będzie bardzo dużo porównań). Druga rzecz - jak analizujesz czyiś kod, łatwiej zrozumieć sposób działania pętli, niż jak będziesz miał poustawiane breaki.

Jest to nazwane “dobrą praktyką”, bo jest wiele rozwiązań i wiele możliwości rozwiązywania problemów, dlatego pewne rozwiązania są bardziej preferowane niż inne w celu ułatwienia komunikacji względem koderami.

Generalnie chodzi o czytelność. Jeśli potrzebujesz coś na szybko zakodować to nie ma problemu, jednak przy większych projektach, nad którymi pracują zespoły programistów, dobrze by było gdyby jeden programista rozumiał, co napisał inny. Zastępowanie do-while przez while(true) z pojedynczym if()break;iem na końcu jest niezbyt sensowne, choćby z tego powodu, że zajmuje więcej znaków niż do-while a czytelności nie poprawia; ma to sens gdy if()break;ów jest więcej w wielu miejscach pętli a żaden z warunków nie jest jakoś szczególnie ważniejszy od innych, wtedy rzeczywiście może poprawiać czytelność. http://pl.wikipedia.org/wiki/International_Obfuscated_C_Code_Contest :wink:

@LonngerM

Ale kompilator i tak to zoptymalizuje więc patrząc w ten sposób nie robi to różnicy…

Dla mnie osobiście pętla nieskończona jest bardziej czytelna niż jakieś tam do… while.

I jest znacznie prostsza w kontroli i ewentualnej rozbudowie.

W tego typu konstrukcji mogę sobie od razu oflagować każdą przyczynę przerwania pętli.

Argument, że pętla nieskończona jest mniej czytelna do mnie nie przemawia.

chyba łatwiej ogarnąć blok {} niż jakieś tam skromne “do” i kupę poleceń.

Oczywiście nigdzie nie twierdze, że to rozwiązanie jest za każdym razem lepsze.

Być może… powtarzam co mi mówili:P Ale to przy okazji złożoności algorytmów.

I masz takie prawo:) Niestety albo stety nie dla każdego jest to bardziej czytelne. Nie chce przekonywać czy opisywać wyższości jednej metody nad drugą. Prawda jest taka, że piszemy jak wymaga pracodawca… Ale trzeba pamiętać, że zależnie od sytuacji różnie używa się kodu…

Bo Ty, jako programista, masz pisać czytelny i sensowny kod. (A to, jak coś jest realizowane na poziomie asemblera to faktycznie nieistotny szczegół w tym temacie.)

Dlaczego chcesz kończyć nieskończoną pętlę? Przecież to nawet nie brzmi sensownie!

Ale co to ma do rzeczy? Switch to przecież nie jest pętla.

Stosować z umiarem, ale najlepiej wcale, bo są brzydkie i zmniejszają czytelność kodu.

Break czy continue to tylko takie lepsze goto, oznaczają skok w inne miejsce i zaburzają ciągłość w przepływie programu. Zmniejszają czytelność i stanowią potencjalne źródło niedopatrzeń i błędów.

Pracodawca?

Ja myślę, że trzeba pisać tak, aby nie dostać łomotu od kolegów z zespołu. :wink:

No tak, na początku piszesz o nieistotnych szczegółach, a potem wyjeżdżasz z argumentami estetycznymi.

Generalnie potwierdzasz moją tezę, że poza czyimś widzimisię nie ma absolutnie żadnego argumentu aby tego nie stosować.

Czytelność kodu jest też pojęciem subiektywnym.

Stara zasada mówi, że kiedy piąta pod rząd osoba mówi ci, że jesteś pijany, to choćbyś był trzeźwy jak świnia, wypadałoby pójść się położyć.

Tak i w tym wypadku - jeżeli wszyscy, z którymi pracujesz, uważają tę konstrukcję za nieczytelną, to wypada dostosować się do zespołu. Kod, w którym przy 8/10 przypadków występuje konstrukcja kanoniczna i 2/10 twoja, naprawdę staje się mniej czytelny i mniej spójny niż taki, w którym stosuje się wyłącznie jeden zapis. Taka samo jakby jeden programista nazywał zmienne moja_zmienna, a cała reszta mojaZmienna. Chodzi nie o działanie programu, tylko trudność pracowania z kimś takim.

No chyba że piszesz wyłącznie dla siebie - wtedy nikt nie powinien ci zabraniać nawet najidiotyczniejszych konstrukcji.

Jeśli chodzi o czytelność kodu, to nie jest ona nieistotnym szczegółem. To jest pierwsza cegiełka budująca jakość projektu. Nieczytelny kod utrudnia wprowadzanie zmian, spowalnia zatem rozwijanie aplikacji i skłania do tworzenia kolejnych warstw złego kodu, aż do momentu, w którym projektu nie da się już rozwijać.

Argument nie jest estetyczny… Napisałem “brzydki”, bo gdybym użył właściwego w tym miejscu przymiotnika, złamałbym regulamin.

Nie, nie jest, przynajmniej nie całkowicie. Są ewidentnie nieczytelne konstrukcje i formy zapisu.

Polecam lekturę: http://helion.pl/ksiazki/czysty-kod-pod … czykod.htm

Nie ma czegoś takiego jak pisanie wyłącznie dla siebie

Wydaje mi się, że odchodzimy trochę od sedna sprawy.

“Czytelność” jest jak najbardziej pojęciem względnym i nie ma tu co naciągać definicji do jakiś idei.

Z punktu widzenia fizyki i mechaniki oka ludzkiego bardziej przyjazne i czytelne powinny być białe literki na ciemnym tle, a i tak większość woli czarne na białym bo tak mają na kartce i do tego są przyzwyczajeni.

Dla daltonisty sygnalizacja świetlna na drogach jest nieczytelna a dla niewidomych większość obecnych stron jest nieczytelna bo pełno w nich bagna, które uniemożliwia poprawną interpretacje przez syntezatory.

Więc pytanie powinno brzmieć dla kogo to ma być czytelne?

A no dla programistów chyba.

I tu pojawia się teraz kolejne pytanie, najważniejsze.

Jeśli ktoś będzie miał problem, czytając mój kod, z jego ogarnięciem gdyż przytłoczy go “nieskończona pętla”, to jaki jest sens jego zagłębiania się w mój kod, którego i tak nie zrozumie?

Może inaczej.

Jeśli ktoś patrzy na mój kod i widzi nieskończoną pętle, to znaczy, że jest to dla niego czytelne.

Jeśli patrzy i nie widzi, to znaczy, że i tak nie ma czego szukać.

Bo mówimy tu o pewnej strukturze istniejącej w języku. Nie jest to mój autorski wymysł, tylko pana Dennisa Ritchiego.

Konstrukcje typu

for(;;)

są jak najbardziej dopuszczalne i poprawne z punktu widzenia semantyki języka.

Śmiem nawet twierdzić, że jeśli ktoś nie rozumie takiego zapisu znaczy, że de-facto nie zna języka.

Oczywiście czymś innym jest celowe utrudnianie odczytania kodu, np. nie robiąc wcięć, a czym innym jest stosowanie “nieszablonowego rozwiązania”.

A jeszcze tak humorystycznie może zauważę, że jeśli piszesz kod, którego nikt nie jest w stanie ogarnąć, oznacza to że jesteś niezastąpiony w pracy ;p

Hmm…

Jeżeli poprosisz bardziej doświadczoną osobę o pomoc w znalezieniu błędu w Twoim kodzie źródłowym miej świadomość tego, że czytelność będzie dla niego priorytetem i mogą zrazić go Twoje konstrukcje językowe (te “dopuszczalne”). Kod źródłowy powinien być napisany w taki sposób, aby można dowieść jego poprawności na kartce papieru w mgnieniu oka.

Z drugiej strony, za parę miesięcy sam możesz mieć problem z odnalezieniem sensu w swojej twórczości, lub jej analiza będzie nad wyraz czasochłonna. Jak wiele czasu możesz poświęcić na analizę takich “kwiatków” jak np.:

int p_a = 1;

int b_2c = 2;

int asd = 3;


while (true)

{

    p_a = (b_2a >= p_a ? asd - p_a : (asd > b_2c ? 2 * p_a : b_2c);

    if (p_a > 10)

        break;

}

Im mniej rzucania mięsem podczas czytania, tym lepiej - nieprawdaż? A czy powyższy kod ma jakikolwiek sens? Może i ma, może nie… Na pewno nie jest utworzony zgodnie z przyjętą powszechnie “dobrą praktyką”…

Mylisz znajomość składni z czytelnością. Zdanie “koń ma cztery nogi” jest bardzo zrozumiałe, ale jeśli je napiszesz pomarańczowymi literami na żółtym tle, to będzie mniej czytelne niż napisane czarno na białym.

Kod programu, z wyjątkiem miejsc wymagających jakichś super optymalizacji, powinno się czytać jak prozę. Bez tracenia czasu na zastanawianie się, czy zmienna “x” oznacza np. pierwsze wystąpienie znaku będącego cyfrą w łańcuchu, tracenia kontekstu przez cofanie się 30 linijek w górę, bo tam jest jakiś label od goto, albo zastanawianie się, czy funkcja “myFunc1” służy do parsowania napisów, mnożenia macierzy czy zapisu do bazy danych.