Owszem, reguły trzeba stosować, ale nie dajmy się im zwariować.
Nie pamiętam od kiedy,
tak mnie nagle pokochał świat,
wyciągając wciąż z biedy
całą masą bezcennych rad.Lady Pank „Vademecum Skauta”
Informatyka z racji swojego sformalizowania kocha się w regułach. Nie można się praktycznie ruszyć, by w jakąś nie wdepnąć ;). Zaczynam się powoli zastanawiać, czy nie mamy tutaj do czynienia ze zjawiskiem podobnym do eksplozji klas – eksplozją reguł. Bezsprzecznie do tego stanu rzeczy przyczyniają się także blogi. Widywałem na blogach wpisy, w których autor prowadzony na smyczy własnych przyzwyczajeń, starał się sprzedać te przyzwyczajenie pod płaszczykiem reguły. I żeby nie było – z pewnością ja sam, niejednokrotnie, dla własnej wygody, próbowałem takie przyzwyczajenie zaszczepić jako regułę. O – właśnie sobie przypomniałem – toczyłem długotrwałe spory na temat sposobu formatowania złożonych instrukcji if, które miały zastępować instrukcje switch (jak wiadomo, ta nie dla wszystkich typów danych da się zastosować). Optowałem za tym (i próbowałem indoktrynować, że jest to reguła), żeby po kolejnym else nie następowało wcięcie, ale równorzędny kolejny if – aby dzięki temu zabiegowi taka konstrukcja była postrzegana właśnie jako zamiennik switch. Oponenci zaś argumentowali, że skoro następującą po else instrukcję wcinamy, to tak samo powinniśmy uczynić z kolejnym if, bo to też instrukcja.
Byłem więc typowym przykładem programisty próbującego sprzedać własne preferencji jako regułę. I nie ważne kto miał w tej dyspucie rację: oni czy ja.
Spotkałem się też z „regułą”, że instrukcje warunkowe trzeba konstruować tak, aby przerywały wykonywanie kodu, czyli:
private void SomeMethod() { // jakiś kod if (ThatCondition()) return; // jakiś kod if (OtherCondition()) return; // jakiś kod }
Być może istnieją takie przypadki, kiedy takie skonstruowanie metody ma sens. Ale można znaleźć tyle samo innych, kiedy nie ma. Czy jest to zatem reguła – przecież ma mnóstwo wyjątków. I tu dochodzimy do dość ciekawego spostrzeżenia:
W każdej regule kryje się podstęp
jest tym podstępem … reguły kontekst.
Całkiem możliwe, że ojciec rzeczonej reguły wymyślił ja w ściśle określonych warunkach. Może sugerował się (podobnie jak ja) instrukcją switch i chciał by poszczególne warunki, tak jak w jej przypadku, po prostu przerywały dalszy ciąg przetwarzania kodu? A może metoda była tak krótka/długa/skonstruowana, że właśnie takie rozwiązanie sprawiało, że stawała się najlepsza/najpiękniejsza/najwydajniejsza? Zapewne była jakaś przyczyna tej reguły – i to będzie właśnie jej kontekst. Jeśli bowiem trafi się metoda, która ma zrealizować dwa i tylko dwa – będące do siebie w opozycji – scenariusze, to może się okazać, że bardziej czytelnym będzie takie jej sformułowanie (widać, że scenariusze są alternatywne: if … else …):
private void SomeMethod() { if (DayOfWeek(Sa, Su)) ItsWeekendTime(); else ItsBoringTime(); }
niż takie (na pierwszy rzut oka nie widać alternatywy, trzeba ją wywnioskować):
private void SomeMethod() { if (DayOfWeek(Sa, Su)) { ItsWeekendTime(); return; } ItsBoringTime(); }
W tym momencie należy zasygnalizować następującą rzecz. Rozpatrując daną regułę, warto brać pod uwagę także inne reguły, które mogą wchodzić z nią w interferencję. Dla tego przypadku jest to reguła czytelności kodu, czyli: który kod lepiej przedstawia intencję, wskazuje przyjętą logikę? To właśnie ta reguła – o ile przyjmiemy, że ma większy priorytet (a czytelność kodu ma jeden z najwyższych) – powinna być brana pod uwagę. Co ciekawe – także w przypadku pierwszego przykładu (tj. moich zapędów regulatorskich), owa reguła znajduje zastosowanie. Wystarczy określić, które z przyjętych formatowań daje czytelniejszy kod, które lepiej wyraża tego kodu intencję. Oczywiście zdania i w tym przypadku mogą być podzielone (bo de facto dyskusja tak naprawdę dotyczy właśnie czytelności kodu), ale tu jak zawsze w ostateczności na pomoc może przyjść demokracja ;).
Przejdźmy teraz do reguł, które nie są już wątpliwego pochodzenia – znany jest ich konkretny kontekst. Taką regułą jest np. zakaz używania gwiazdki w zapytaniach SQL. Zawsze, gdy ktoś wspomina o tej regule, słyszę kategoryczne, z mocą wypowiadane słowa – tylko lista pól, tylko. Jestem w stanie podać wyjątki od tej reguły, kiedy użycie listy pól jest bez sensu, bo nie niesie za sobą zagrożeń, które ta reguła ma wyeliminować, a uniemożliwia skorzystanie z dobrodziejstw składni. Są to przypadki, kiedy kontekst reguły nie występuje. I żeby nie być gołosłownym podaję taki przykład:
select top 0 * into #Result from template.EndOfDayReport;
Jest to sposób na uzyskanie tabeli tymczasowej, która ma strukturę zdefiniowaną w dedykowanej tabeli szablonowej. Dzięki temu nie ma potrzeby modyfikacji procedury składowanej (lub dowolnej innej konstrukcji SQL), aby zmienić format tabeli. Jeśli cała reszta kodu stosuje się do reguły używania nazw kolumn, to będzie wypełniać tabelę #Result bezproblemowo. A my możemy dołożyć kolejne polecenie SQL, które uzupełni ją o nowe pola, czyli te dodane do struktury tabeli szablonowej.
Inną, szeroko znaną i uznawaną regułą, jest, aby kwestionować tworzenie bytów, których nie można precyzyjnie nazwać. Ma to sens, bo dzięki temu bronimy się przed powstawaniem chociażby boskich klas o rozbuchanej funkcjonalności. Ale czy ta reguła zawsze daje korzyści? Nie! Jest taki proces, który może ona znacząco utrudnić – refaktoryzację. Jeśli bowiem zaczniemy (odsyłam do mojego cyklu na temat refaktoryzacji na tym blogu) refaktoryzować za pomoczą wzorca Obiektu Metody, to okaże się, że trudno nam nazwać reprezentującą go klasę. No i słusznie! Zawiera ona bowiem cały różnorodny kod, którego chcemy tylko i wyłącznie pozbyć się z jego obecnego miejsca występowania. Zatem możemy klasę dosłownie nazwać MethodObjectClass i przestać się przejmować tą regułą. Nie ma ona bowiem w tym kontekście zastosowania.
Podsumowując powyższe rozważania. Przyzwyczajeni (by nie rzec – skrzywieni 😉 ) do binarnego rozpatrywania zagadnień tj.: stosuje regułę – sukces, nie stosuję – porażka, nie pozwólmy się zwariować i pamiętajmy o nadrzędnej regule – regule wszystkich reguł:
stosuj regułę, jeżeli w danym przypadku jej zastosowanie ma uzasadnienie.