Recenzje kodu: Najczęstsze źródła ekstremalnych naruszeń i jak unikać argumentów na ich temat

Noże są rysowane. Ostrza są naostrzone na wypadek konfliktu. Toczy się spór między deweloperami. Namiętności programistów są rozpalone nie z powodu błędnego oprogramowania, ale z powodu rażąco zwięzłego lub pełnego kodu. Te linie są oznaką włamania. Każdy programista, który się nie zgadza, jest amatorem. Tylko neofita wytworzyłby metody i bloki, które tak wyraźnie naruszają dobry smak. Jednak różne preferencje, a nie prawa natury, są źródłem tego konfliktu i witriolu. Nienawiść między twórcami jest w tym przypadku wynikiem różnych skłonności do zwięzłości w handlu dla różnych celów. Cele te i ich tendencja są różne dla każdego programisty, co prowadzi do ciągłego konfliktu w niektórych obszarach. Jednym z takich miejsc jest nieporęczny lub zwięzły kod. Aby zminimalizować walkę, zespół może użyć recenzji kodu, aby wyróżnić najbardziej skandaliczne segmenty, a grupa może spierać się o te części zamiast kłócić się o każdą linię i blok podstawy kodu.

Niektóre konstrukcje lub techniki kodu mogą powodować ekstremalne naruszenia i prowadzić do argumentów na temat korygowania tych wykroczeń. Naprawienie tych nadużyć prowadzi do intensywnych kłótni. Nieporozumienia te można rozwiązać lub przynajmniej usunąć z nich opisy wymienionych poniżej cech i technik językowych.

Instrukcja warunkowa a instrukcja If

Elementy językowe, operator warunkowy i instrukcja if prowadzą do argumentów, z różnymi obozami argumentującymi, że każdy z nich jest lepszą techniką dla niektórych operacji. Działania te można realizować na wiele sposobów, a każda technika ma zalety i wady.

Instrukcja if: Instrukcja if może przyczynić się do okropnie obszernego kodu, gdy gęstość warunków jest wysoka. Wysoka gęstość powoduje, że blok lub metoda wydają się opuchnięte. Jednak kod napisany za pomocą instrukcji if jest również wysoce debugowalny, ponieważ programista może przechodzić przez każdą linię.

if (label1IsRequired) {
 label1.Color = „czerwony”;
} else {
 label1.Color = „czarny”;
}
if (label2IsRequired) {
 label2.Color = „czerwony”;
} else {
 label2.Color = „czarny”;
}
if (label3IsRequired) {
 label3.Color = „czerwony”;
} else {
 label3.Color = „czarny”;
}

Operator warunkowy: Operator warunkowy może prowadzić do niektórych rażąco zwięzłych linii, gdy jest stosowany jako zamiennik kilku instrukcji if. Wbudowane operatory warunkowe sprawiają, że kod, gdy jest on ekstremalnie, bardzo trudny do odczytania, przetestowania lub debugowania. Każdy blok lub metoda obciążająca operatorów warunkowych jest również bardzo zwarta, co zmniejsza ilość kodu, który programista musi skanować.

healthIndicatorColor = (health == „Good”)? „Zielony”: (zdrowie == „godziwy”)? „Żółty”: (zdrowie == „zły”)? „Czerwony”: (zdrowie == „life_support”)? „Pomarańczowy”: „fioletowy”;

Potencjalne rozwiązanie: Operatory warunkowe są korzystne, gdy zastępują wysoką gęstość wartości ustawionych na podstawie warunków zaimplementowanych w instrukcjach if. Operatory warunkowe są destrukcyjne, gdy zastępują nawet kilka decyzji osadzonych w sobie. Imperatywy, które pasują czytelnie w jednym wierszu, są głównym celem operatorów warunkowych, podczas gdy warunki wymagające kilku wierszy są domeną instrukcji if. Wszelkie rażące użycie instrukcji if lub operatorów warunkowych powinno zostać poprawione, aby wdrożyć odpowiednie użycie jednej z tych konstrukcji. (Uwaga: modyfikacja może wymagać znacznego refaktoryzacji).

if (zdrowie == „Dobry”) {
 healthIndicatorColor = „zielony”;
} else if (health == „Fair”) {
 healthIndicatorColor = „żółty”;
} else if (health == „poor”) {
 healthIndicatorColor = „czerwony”;
} else if (health == „life_support”) {
 healthIndicatorColor = „pomarańczowy”;
} else {
 healthIndicatorColor = „fioletowy”;
}
label1.Color = (label1IsRequired)? "czerwony czarny";
label2.Color = (label2IsRequired)? "czerwony czarny";
label3.Color = (label3IsRequired)? "czerwony czarny";

Wiele wyciągów zwrotnych a jedno zwrotne

Dwa szczególne style prowadzące do argumentów to wielokrotne i pojedyncze zwroty. Pojawiają się spory dotyczące tego, czy metody powinny mieć jedną instrukcję return, czy też wiele instrukcji return jest akceptowanych. Każde podejście ma pozytywne i negatywne strony.

Instrukcje wielokrotnego zwrotu: Instrukcje wielokrotnego zwrotu mogą przyczynić się do powstania kodu, który jest trudny do zrozumienia, śledzenia i testowania. Jednak metody z wieloma zwrotami mogą być krótsze niż funkcje z jednym zwrotem.

SomeDataType someMethod (param1, param2, param3) {
 SomeDataType retVal;
 if (param1 == null) {
 retVal = null
 }
 if (retVal == null) {
 return retVal;
 }
 
 if (param2! = null) {
 retVal = param2;
 }
 if (retVal.Equals (param2)) {
 return retVal;
 }
 
 retVal = param3;
 return retVal;
}

Jedna instrukcja zwrotu: Pojedyncza instrukcja zwrotu może prowadzić do długich metod. Jednak procedury te mają wyjście jednopunktowe, co upraszcza testowanie i debugowanie.

SomeDataType someMethod () {
 SomeDataType retVal;
 // setki lub tysiące linii kodu
 return retVal;
}

Potencjalne rozwiązanie: wiele zwrotów powoduje, że kod jest trudny do zrozumienia, śledzenia i testowania, jeśli są używane niespójnie. Pojedyncze instrukcje return prowadzą do długich metod, gdy są poprzedzone długim odcinkiem kodu. Te wyciągnięte zakresy można skrócić, a przynajmniej uczynić czytelnymi, używając kilku instrukcji return zamiast jednego. Pojedyncze zwroty są całkowicie akceptowalne, jeśli podążają krótkimi fragmentami kodu. Każde rażące niewłaściwe użycie pojedynczej instrukcji zwrotu lub wielu zwrotów powinno zostać naprawione, aby zastosować zaakceptowany przypadek użycia jednego z tych stylów. (Uwaga: Korekta może wymagać znacznego refaktoryzacji).

SomeDataType someMethod (param1, param2, param3) {
 if (param1 == null || param3 == null) {
 zwraca null;
 }
 
 SomeDataType retVal = null;
 if (param2! = null {
 retVal = param2;
 } else if (param1! = null) {
 retVal = param1;
 } ele if (param3! = null) {
 retVal = param3;
 }
 return retVal;
}
SomeDataType someMethod (param1, param2) {
 SomeDataType retVal = null;
 dla (int i = 0; i  if (param1 [i] .equals (param2)) {
 retVal = param1 [i];
 przerwa;
 }
 }
 return retVal;
} Przerwij i kontynuuj korzystanie z pętli

Konstrukcje przerwań i kontynuacji są przedmiotem intensywnych debat. Po jednej stronie argumentu programiści twierdzą, że przerwanie i kontynuacja może uprościć kontrolę. Inni programiści twierdzą, że te funkcje komplikują logikę programu. Przerwij i kontynuuj można zdecydowanie wykorzystać do uproszczenia lub skomplikowania kodu. Te linie można dostrzec.

Przerwij i kontynuuj użytkowanie: elementy mogą uprościć kod, ale mogą też niepotrzebnie je skomplikować.

SomeDataType someMethod (param1, param2) {
 SomeDataType retVal = null;
 dla (int i = 0; i  if (param1 [i] == null) {
 dalej;
 }
 if (param1 [i] .equals (param2)) {
 retVal = param1 [i];
 przerwa;
 }
 }
 return retVal;
}
SomeDataType someMethod (data, param1) {
 SomeDataType retVal = null;
 Zrób coś:
 for (int i = 0; i  if (i> = data.length) {
 przerwa;
 }
 if (data [i] .equals (param1)) {
 retVal = dane [i];
 } else {
 dalej;
 }
 }
if (retVal == null) {
 data - refreshData ();
 goto dosomething;
 }
return retVal;
}

Rozwiązanie problemu: większość programistów twierdzi, że kod powinien używać prostych mechanizmów kontroli przepływu. Jakie konkretne mechanizmy są proste, jest źródłem debaty. Ten argument staje się znacznie mniej ostry, gdy każdy instrument jest używany w powszechnie akceptowany sposób. Akceptowane podejścia istnieją dla przerwania i kontynuowania. Trzymaj się tych konwencji, aby uniknąć nieporozumień i uprościć kontrolę. Wszelkie środki kontroli, które rażąco naruszają te standardy, powinny zostać poprawione bez debaty.

SomeDataType someMethod (data, param1) {
 SomeDataType retVal = null;
 dla (int i = 0; i  if (data [i] == null) {
 dalej; // pomiń resztę pętli
 }
 if (data [i] .equals (param2)) {
 retVal = dane [i];
 przerwa; // nie zapętla więcej b / c Skończyłem
 }
 }
 return retVal;
}

Wyjątki obronne

Wyjątki są sposobem na wskazanie problemu lub odstąpienie od przyszłego problemu. Jakie bóle głowy powinny być wskazywane lub powstrzymywane przez to, które części kodu są przedmiotem zaciętej debaty. Z jednej strony sporu koderzy twierdzą, że wszechobecne wyjątki obronne zapobiegają błędom i ułatwiają ich lokalizację. Jednak ta lista pralni może sprawić, że kod będzie nadęty i trudny do zrozumienia, jak twierdzili niektórzy programiści. Deweloperzy po obu stronach debaty mają rację. Wyjątki obronne mają zarówno zalety, jak i szkody.

Korzyści i szkody związane z wyjątkami obronnymi: Ochrona przed błędami i innymi problemami może być chroniona, przy minimalnych wadach, z wykorzystaniem wyjątków obronnych. Te wady stają się większe, gdy technika jest bez rozróżnienia.

void someMethod (param1, param2) {
 if (param1 == null || param2 == null) {
 wyrzuć nowy ArgumentNullException („Brakuje jednego lub więcej parametrów”);
 }
 // wykonaj metody
}
void someMethod (param1, param2) {
 // dziesiątki linii kontroli obronnych… ..
 // wykonaj resztę metody
}

Potencjalne rozwiązanie: braki w wyjątkach obronnych są najmniejsze, gdy są stosowane w przyjętych zastosowaniach. Każde wdrożenie techniki, która odbiega od tych konwencji, powinno zostać poprawione, chyba że podano ważny powód.

public void someMethod (param1, param2) {
 // sprawdź każdy parametr w metodzie publicznej
 if (param1 == null || param2 == null) {
 wyrzuć nowy ArgumentNullException („Brakuje jednego lub więcej parametrów”);
 }
 
 // odeprzyj problemy spowodowane przez nieprawidłowe dane
 if (! isValid (param1) ||! isValid (param2)) {
 wyrzuć nowy InvalidParameterException („Jeden lub więcej parametrów jest niepoprawny”);
 }
 
 // zrób coś z parametrami
}

Zakończyć

Te konstrukcje i techniki kodu są stosowane zarówno przez dobrych, jak i złych programistów. Programiści to ludzie. Ludzie mają tendencje. Skłonności te przejawiają się w kodzie. Czasami impulsy programisty skłaniają go do pisania kodu, który inni koderzy słusznie krytykują. Pręgowany deweloper niekoniecznie jest złym programistą. Krytykujący go koder niekoniecznie jest dobrym programistą. Obie osoby zostały w pewnym momencie oszukane przez swoje preferencje. Pragnienia te nie powinny prowadzić do przekształcenia się w niekończący się strumień zniewag rzucanych na siebie. Programiści powinni raczej sprawdzać nawzajem swój kod, ograniczać swoje bitwy do najgorszych sekcji i zgadzać się rozstrzygać pewne argumenty zgodnie z zasadami określonymi powyżej.