O dewaluacji węgierskiej notacji, a także o jej nadinterpretacji
Notacja węgierska to ZŁOOOOOO! Słychać to ze wszystkich stron i trudno się z tym nie zgodzić. Przedrostki nazw identyfikatorów określające typy takiego identyfikatora nigdy nie były jakimś nowatorskim rozwiązaniem – prędzej protezą. Czy oznacza to jednak, że całość tego pomysłu należy odbierać negatywnie? W mojej opinii nie. Notacja wprowadza trzy reguły:
- używanie w identyfikatorach przedrostków,
- które są skrótami
- komponowanymi wg ścisłej specyfikacji od nazw typów tychże identyfikatorów.
Gdyby odrzucić ostatnią regułę, jest to – jakby nie patrzeć – kwalifikowanie identyfikatorów, czyli coś z czym na co dzień mamy do czynienia używając metod i właściwości obiektów (są one kwalifikowane nazwą tegoż obiektu). Częstokroć nazwy owych obiektów nie są szczególnie rozbudowane, z racji ich istnienia w dość krótkich obszarze kodu – bywają skrótami. Zatem w czym gorsze jest użycie przedrostka w nazwie identyfikatora do przedrostka będącego nazwą klasy (i separatorem, ale przecież przedrostek też może być separowany, np. podkreśleniem „_”)?
Wg zasad panujących w C# nazwy parametrów powinny być podawane małymi literami – w odróżnieniu od nazw właściwości. W ten sposób nie jest potrzebne dodatkowe kwalifikowanie albo parametru (przedrostkiem) albo właściwości (przez this). Ale co będzie jeśli postanowię mieć pole, które jest hermetyzowane przez właściwość? Wówczas te pole powinno być napisane z małych liter, a właściwość z wielkiej. No to teraz dołóżmy do tego metodę, która będzie takie pole określała (obliczała, itd.) – dostając jego wstępną wartość w parametrze. Oj! Ale parametr też należy napisać małymi literami. W takim razie wyjątkowo trzeba użyć this. Rzecz w tym, że owe this może zacząć stwarzać kłopoty przy czytaniu kodu, może być problem czy rzeczywiście powinno ono być w tym miejscu? A może jednak chodziło o wartość otrzymaną z parametru – przy skomplikowanych algorytmach, można się nieźle zaplątać. Użycie przedrostka a (od argument) przed nazwą parametru pozwoli od początku panować nad obecnością parametru w kodzie metody, co więcej – będzie on mógł mieć nazwę zgodną z nazwą właściwości (np. NIP, PESEL, SQLConnection). A skoro zrobiłem tak z parametrem, to mogę tak samo zrobić z polem – dodać mu przedrostek f (od field), nie zmieniając wielkości liter. W ten sposób może to wyglądać następująco:
- NIP – właściwość
- fNIP – pole
- aNIP – parametr
Daje to niezbyt skomplikowana regułę, która jest wolna od wyjątków (typu użycie this). Co więcej, kwalifikowanie następuje jedynie w obrębie kodu klasy (zarówno pola, jak i argumenty nie wychodzą poza nią) zatem nie będzie podwójnego kwalifikowania (nazwa obiektu plus prefiks identyfikatora). Zgadza się, że zawsze trzeba użyć o jeden znak więcej, ale nie jest to jednak czteroliterowy this (i kropka), a jednocześnie, po wpisaniu danego przedrostka, InteliSense podpowie albo pola klasy, albo argumenty metody, co jest wygodne.
Użycie przedrostków daje także korzyść wykraczającą poza sam język C# – jeśli ktoś pisze w innych językach, nie musi – w ich przypadku – przestawiać się na inne reguły, jeśli nie rozróżniają one np. wielkości liter w identyfikatorach (i nie ma tu zmiłuj – parametr musi mieć wtedy w nazwie coś extra). W ten sposób programista dysponuje zunifikowanym zestawem reguł, które dają się stosować w wielu językach i pozwalają żyć w zgodzie z drugą naturą człowieka – przyzwyczajeniem ;).
Należałoby w tym momencie zadać sobie pytanie – czy to jest nadal notacja węgierska, czy może jednak jedynie jej nadintepretacja, gdyż ta nie mówiła o dodawaniu przedrostków jako takich, ale przedrostków określających typy? Co bardziej obeznani w programistycznym świecie zapewne zaprotestują twierdząc, że w pewnym momencie dodano do niej także przedrostki określający zakres funkcjonowania identyfikatora (lokalny funkcji, globalny, wewnętrzny dla modułu), więc konwencja, którą tu rozważam jest kompatybilna z ową rozszerzoną postacią notacji. Pozwolę się jednak nie zgodzić. Istotna jest bowiem geneza wprowadzenia przedrostków. Nie służą one do wyróżniania zakresu obowiązywania identyfikatora, lecz regulują sposób rozdzielenia identyfikatorów o tej samej nazwie, w celu zachowania ich tożsamości (tego co de facto reprezentują) bez uciekania się do wyjątków od reguły i z zachowaniem takiej samej (w przybliżeniu do przedrostka) nazwy.
W kontekście tych rozważań warto zwrócić uwagę na pewien paradoks. Obowiązująca reguła dotycząca parametrów wymaga ode mnie traktowania jako różnych nazwy parametru i nazwy właściwości, a jednocześnie traktowania ich jako posiadających to samo znaczenie – a przecież PAN (Polska Akademia Nauk) i pan (zwrot grzecznościowy) nie znaczy tego samego. Będę zatem doświadczał swoistego dysonansu, szukając różnicy w wielkości liter, by w tym samym momencie ją zignorować. Wolę już ignorować jednoliterowy, mały przedrostek, bo reszta jest taka sama, co daje prosty układ: ta sama nazwa – to samo znaczenie.
Skoro już mowa o paradoksach, to warto przytoczyć i drugi. Z notacji węgierskiej ostała się w C# jedynie konwencja nazywania interfejsów. Jedynie? Otóż nie – proszę sobie zerknąć na typy generyczne (np. Dictionary) występują tam częstokroć pięknie przedrostkowane (przedrostkiem T od Typ) nazwy oczekiwanych typów (dla tego przykładu: TKey i TValue). Jakby nie patrzeć – notacja węgierska wiecznie żywa – niczym Lenin ;).
Zbliżając się do końca moich rozważań, poruszę jeszcze kwestię nazewnictwa kontrolek. Jeśli mamy etykietę i pole edycyjne która ona opisuje, to zazwyczaj staramy się nazwać je w podobny sposób. Jeśli pole to data urodzin wówczas tak powinna nazywać się zarówno etykieta jak i pole. Oczywiście to niemożliwie, aby dwa identyfikatory były identyczne (wówczas nie mogłyby jednoznacznie identyfikować danego elementu) więc rodzi się potrzeba, aby coś tam dołożyć. Albo jest to przyrostek (Etykieta, Pole), albo przedrostek. Inaczej się nie da. Ot kolejny kamyczek do ogródka notacji (lub jak wolę – przedrostkowania).
Podsumowując: rozumiem zakusy pozbycia się węgierskiej notacji, jej pierwotną formę jestem w stanie znieść jedynie w strukturach WinAPI, zaś wszędzie indziej nie. Nie stosuję jej, niemniej system skrótowych przedrostków owszem, bo uważam, że jest nie mniej dobry, niż przyjęte w C# reguły, co mam nadzieje udało mi się w tych rozważaniach wykazać. Nie jest moim zamiarem nawracanie kogokolwiek na przedrostkowanie, chcę jedynie wzbudzić trzeźwość osądu, czym tak naprawdę jest notacja, a czym nie jest i czy to co jest obecnie, nie jest przypadkiem realizacją przysłowia: przyganiał kocioł garnkowi, a sam smoli.
Post Scriptum: nie mam oporów przed stosowaniem konwencji przyjętej w C#, choć nieodmiennie odczuwam dysonans widząc różnice w nazwie, tego co ma takie samo znaczenie. Jak pisałem – łatwiej pominąć mi jeden znak przedrostka, niż przeskakiwać z sqlConection na SQLConection, bardzoSkrupulatnieOpisanyIdentyfikator na BardzoSkrupulatnieOpisanyIdentyfikator, itd.