Porzekadło głosi, że w informatyce są tylko dwa trudne problemy: nazewnictwo, mechanizmy cache‘owania i pomyłki o jedynkę. Jeśli chodzi o jeden dwa ostatnie, to może przyjrzymy się im przy innej okazji… Dzisiaj chciałbym za to zająć się pierwszym z nich: dobieraniem odpowiednich nazw dla konstrukcji programistycznych, takich jak funkcje czy klasy.
Nie każda nazwa jest właściwa. O tej trywialnej prawdzie każdy pewnie przekonał się już dawno, zwłaszcza jeśli przechodził przez fazę zmiennych a
, b
, c
lub funkcji fun1
i fun2
. Zdaje się zresztą, że kiedyś była to powszechna przypadłość, co widać zwłaszcza w przypadku starych API *niksowych. Najwyraźniej jednak poszliśmy kolektywnie po rozum do głowy i dziś już nikt nie nazwie funkcji wait3
czy wait4
.
Nietrudno jest oczywiście wskazać podstawowy problem tego rodzaju nazw. Jakkolwiek jest on ściśle związany z długością, nie uzasadnia to automatycznie stwierdzenia, że wszystkie krótkie nazwy są złe. Żeby nie odchodzić daleko, wystarczy tylko spojrzeć na POSIX-owe, uniwersalne funkcje read
i write
. Ich nazwom nie brakuje dokładnie niczego; przeciwnie, próba dodania czegoś więcej wprowadzałaby tylko zamieszanie. readFromFileDescriptor
może i wskazywałaby wyraźnie na źródło danych, ale czy zupełnie poprawne użycie takiej funkcji na uchwycie sieciowego gniazda (socket) nie byłoby mylące? O jakim pliku
wtedy mówimy?
Naturalnie, w *niksach “wszystko jest plikiem” i należy o tym wiedzieć. Lecz skoro tak jest, to co zyskujemy przez dłuższą nazwę funkcji read
? Niby z czego innego niż z pliku mielibyśmy czytać?…
Dywagując na ten temat muszę koniecznie zaznaczyć, że nazwy zaśmiecone oczywistymi informacjami nie są wcale hipotetycznym problemem. Moim ulubionym przykładem – ze względu na swoją groteskową wręcz ekstremalność – jest poniższe wywołanie:
Ja wcale nie żartuję – tak w Objective-C (OSX/iOS) wygląda zastępowanie jednego ciągu w tekście innym. Zawsze chętnie wysłucham argumentów przekonujących o ekspresywności i opisowości podobnych nazw, ale nigdy nie uwierzę, że koderzy je rzeczywiście czytają. Zgaduję, że ich percepcja u statystycznego programisty polega na dostrzeżeniu “Repl
” lewym okiem i “ccurr
” prawym – albo coś w tym rodzaju. Jeśli mam rację, to nazwa ta jest znacząca w zaledwie 22 procentach; trudno to uznać za dobry stosunek sygnału do szumu.
Czy można było zrobić to lepiej? Zapewne – weźmy chociażby Javę:
Wygląda to całkiem dobrze. Okazuje się, że można użyć trzy razy krótszej nazwy i osiągnąć niemalże ten sam efekt, posługując się po prostu samą składnią języka (kolejnością parametrów i wartością zwracaną) zamiast długich tekstowych opisów.
Rozwlekłe nazwy – nawet jeśli camelCase czyni je znośnymi – nie zawsze są więc dobrą odpowiedzią. Czasami mogą one być równie nadmiarowe co niepotrzebne komentarze.
Karolu, właśnie Twój ostatni przykład dość dobitnie pokazuje że krótkie nazwy mogą być złe. W tym przypadku zadziała ok, ale
jeśli spojrzysz na API javowe, to String.replaceAll działa zupełnie inaczej niż można by się było spodziewać z nazwy. Bo zastępuje ona WYRAŻENIE REGULARNE a nie string (w przeciwieństwie do podobnej metody String.replace która zastępuje stringi właśnie)…
Oczywiście gorzej to świadczy o niespójności API Javy a nie “złych krótkich nazwach” ale czyż nie lepiej do czytania kodu wyglądało by:
Ja na codzień mam do czynienia z takimi programami jak SPSMakro80 albo UP2. Główne programy nazywamy Folge1.
Jeśli chcesz ustawić sobie jakąś zmienną proszę bardzo, np F123.
Xion, bardzo dobry przykład. :) Jednak używanie takich nazw jest konsekwencją przyjętej konwencji i skutków ubocznych niektórych właściwości języka. Niestety Obj-C ma jedną zasadniczą wadę, a jest nią “słabsza” silna typizacja (porównując np. do Java’y) i bardzo ciężkie do wychwycenia błędy podczas dynamicznej analizy w przypadku nawet drobnej pomyłki. W skrócie łatwiej usunąć błąd czytając kod źródłowy niż gapiąc się w treść rzuconego wyjątku po wywołaniu selektora na obiekcie, który nie potrafi go obsłużyć. Dodatkowo w Obj-C nie ma przeciążania… więc mamy całą rodzinkę różnych metod do obsługi różnych obiektów i jakoś te metody trzeba nazwać. Replace1All … ReplaceNAll nie wydają się tutaj najlepszym wyborem. :)
Nie mam żadnych statystyk na to ale wydaje mi się, że dłuższy wyraz ciężej niechcący pominąć, i przy okazji zmusza to trochę programistów do dzielenia dłuższych wyrażeń na pojedyncze linię – po prostu nie da się tego w pewnym momencie czytać. ;D
Wyżywając się na Obj-C, jeszcze fajniejszym a dość często używanym przykładem jest UIAlertView. Ale generalnie jakoś właśnie chyba przez to lubię ten język. Mimo wszystko jest na swój sposób ekspresywny.
W przypadku przykładu Obj-C wszystko ładnie widać. W przykładzie Javy co zastępuje co ? Kot psa czy pies kota ?
Heh, no tak, Obj-C “slynie” z tego typu nazw. Jak dluzej sie siedzi w tym jezyku, ma to wiecej sensu. Po pierwsze – tego typu nazwy metod sa tak naprawde nazwami metod z nazwanymi parametrami – przy czym kazdy parametr musi byc obligatoryjnie nazwany ( w sumie to nie do konca, ale pominmy to ;) ). Rozwleka to oczywiscie strasznie kod, ale piszac w stylu jaki podales, czyli przy dlugich metodach, kazdy parametr w osobnej linijce – staja sie to IMO nawet bardziej czytelne niz w stylu klasycznego C. Rzadziej tez trzeba zagladac do dokumentacji.
Po czasie czlowiek sie “wprowadza” w taki styl i sie okazuje ze samemu zaczyna tworzyc takie rzeczy ;0
Wymyślając nazwy metod sam mam tendencję do tworzenia długich i opisowych ;)