O nazwach
Brak motywacji, by dokończyć pisanie o nazwach i rozwiązać „konkurs” dotyczący nazwy został zredukowany do zera „hejtem”, jaki pojawił się ostatnio na kod ze strony pkp intercity.
Koledzy i koleżanki w kodzie. Powiem wam jedno.
JESTEŚCIE HIPOKRTAMI
I niech kurwa nikt nie mówi, że nie, bo każde z nas puściło nie jedną kiepską nazwę na produkcję. Do problemu PKP jeszcze wrócimy. Skupmy się na temacie nazw.
Nazywać po imieniu
Problem nazewnictwa przerabialiśmy już w cyklu ekstremalna obiektowość w praktyce, a dokładnie w części piątej. W tamtym wpisie nie wyczerpaliśmy tematu. Co więcej, tamten wpis dotyka trochę innego problemu, czyli odszukiwania potencjalnych problemów za pomocą kontekstu użycia danej metody.
Nazywamy nie tylko metody, ale też klasy, moduły, zmienne i dlatego warto zastanowić się jak robić to dobrze.
Zasady — ogólnie
Branżunia, wypracowała sobie pewne zasady, co do nazywania różnych rzeczy. Można je ująć w następujący sposób.
- Klasy, moduły, struktury itp. obiekty, które reprezentują w kodzie pewne byty biznesowe, nazywamy korzystając z rzeczowników.
- Funkcje, metody, procedury reprezentują czynności i ich nazwy pochodzą od czasowników.
- Wartości typów wyliczeniowych, stałe reprezentujące pewną cechę itp. są przymiotnikami, ale możemy je grupować np. w
enum
-a, którego nazwa jest rzeczownikiem. - Chcąc doprecyzować znaczenie danego elementu oraz umieścić go w przepływie sterowania możemy dodawać przedrostki w rodzaju
Pre
iPost
, albo przyimki. W przypadku przyimków zazwyczaj stosujemy okoliczniki czasu, bo co do zasady używamy języka angielskiego.
Proste. Klasa rzeczownikiem, metoda czasownikiem. Jak pamiętacie coś z polskiego, z podstawówki, to jeszcze dołóżcie budowę zdania i mamy ładnie rozklepane wywołanie metody.
Listing 1. Przykładowe wywołanie metody
"12-345".matches(plPostCodePattern);
„12-345”
— podmiot.matches
— orzeczenie.plPostCodePattern
— przydawka (not sure)
Istnieje też grupa zasad, które pozwalają na identyfikowanie problemów:
- Jeżeli nazwa zawiera spójniki i, lub, albo, to mamy wiele odpowiedzialności.
- Jeżeli nazwa zawiera generyczne bądź abstrakcyjne rzeczowniki, a kod, który reprezentuje jest konkretny, to mamy najprawdopodobniej do czynienia z nieprecyzyjną nazwą.
- Jeżeli nazwa na wysokim poziomie abstrakcji zawiera szczegółową nazwę implementacji, to abstrakcja nam cieknie.
- Jeżeli mamy powtórzenie w nazwie, to przekombinowaliśmy z architekturą.
Do tego dochodzi kilka „zabronionych” słów w rodzaju Listener
, Handler
czy Provider
, czy niespójności w nazwie i typie zwracanym/przyjmowanym. I
mamy komplet.
Problemy
Przyjmując takie proste zasady, dość łatwo jest stworzyć idealną nazwę, prawda? No nie do końca. Po pierwsze nazwa powinna być opisowa, ale nie długa.
Problem
już omówiliśmy wcześniej.
Nazwę można skracać, byle nie za bardzo, a nawet powiem
więcej czasami stosujemy skróty w rodzaju DTO
, DAO
. To powoduje, że mamy ograniczone pole manewru. Po drugie nazwa powinna ułatwiać nam
lokalizację kodu w architekturze, ale pewne nazwy występują w wielu miejscach w odmiennych znaczeniach. Po trzecie nazwa powinna być dostosowana do
poziomu abstrakcji, na którym pracujemy, a nie zawsze jest to możliwe. Po czwarte, w końcu, nazywający może mieć ograniczony zasób słownictwa, który
ogranicza mu możliwości zarówno w przypadku oczywistych jak i specyficznych nazw.
Dobra nazwa, czyli jaka?
Z dotychczasowej zabawy wiemy już, że nadanie dobrej nazwy może być problematyczne. Co więcej, im mniej wiemy o systemie, tym trudniej jest nam znaleźć odpowiednią nazwę, która będzie pasować do świata. Z pomocą przychodzi nam DDD, które wymusza do pewnego stopnia stosowanie odpowiedniego nazewnictwa. Użycie języka domenowego do opisu operacji i bytów przenosi ciężar szukania nazw z programistów na ekspertów domenowych. Nie jest to, niestety, idealne rozwiązanie, ponieważ eksperci domenowi mają trochę inną perspektywę. Zazwyczaj bardzo dobrze rozumieją poszczególne pojęcia i potrafią ich używać w odpowiednim kontekście. Jeżeli damy programiście dwa identycznie nazywające się byty, to mamy dużą szansę, że wyprodukuje potworka. Tu warto podrzucić przykład, a ten będzie wredny.
W naszej aplikacji mamy konto (ang. account) i rachunek (ang. account). Konto istnieje w kontekście księgi głównej, rachunek w kontekście klienta. Spotykają się one w kontekście księgowego (ang. accountant), który na podstawie operacji na rachunkach tworzy operacje na kontach.
Zastanówcie się nad nazwami. Przykład jest o tyle wredny, że obie te nazwy w kontekście operacji finansowej w języku angielskim są reprezentowane przez to samo słowo. Nie takie samo, ale to samo. No właśnie, kontekst i język.
Język ma znaczenie
Większość z nas używa języka angielskiego w kodzie. Przyjmujemy to jako oczywistość. Wszelkie odstępstwa są traktowane jako herezja. Czy jednak
słusznie? Jeżeli popatrzymy na ten problem z perspektywy użyteczności, to kurczowe trzymanie się języka angielskiego nie ma sensu. Jeżeli środowisko
pracy jest polskojęzyczne, to używanie języka polskiego może okazać się dobrym wyborem. Oczywiście musimy spełnić kilka warunków m.in. jakoś
zagwarantować sobie, że wszyscy znamy język polski. Oznacza to też, że ograniczamy sobie możliwość zatrudniania, ale czasami i tak mamy to
narzucone.
Inna sprawa to język dokumentacji i analizy. Tu często mamy do czynienia z językiem polskim, co prowadzi do podwójnego tłumaczenia nazw i w efekcie
może prowadzić do nieporozumień. Jako ciekawostkę można potraktować projekty realizowane przez firmy z Niemiec, gdzie zarówno dokumentacja, jak i
komunikacja są w języku niemieckim. Kod też jest w języku niemieckim, jeżeli nie wychodzi poza organizację.
Casus PKP, czyli podsumowanie
Wspomniany na początku kod w wykonaniu PKP Intercity jest, z punktu nazewnictwa, znośny. Problemem nie jest język polski, bo taka jest konwencja i naprawdę nie ma potrzeby, by kombinować z angielskim. Mamy tu do czynienia z dwoma innymi problemami.
Mieszanie języków
Do pewnego stopnia wymuszone przez konwencje stosowanie przedrostków is
, valid
itp. Jest to problem nie do rozwiązania, szczególnie w językach
słabo lub niejawnie typowanych, które bazują na konwencji (patrz FORTRAN i implicit none
). W językach silnie typowanych ten problem ma postać
generycznych nazw metod w interfejsach np. handle
, accept
itp. Taki problem, nie problem.
Mieszanie konwencji
Rzeczywistym problemem w tym kodzie jest mieszanie różnych konwencji nazewniczych. Czasami camelCase rowerOferta
, czasami
snake_case is_bilet_rodzinny
. Nazwy pełne kod_znizki
, skróty kat_poc
, obfuskacja v1
, nazwy zawierające liczebniki porządkowe liczba_u_2
.
Takie nietrzymanie konwencji jest jak nietrzymanie moczu. W pewnym momencie narobimy sobie wstydu przed obcymi ludźmi.
Sprawa jakości samego kodu nie jest tu istotna, ale będzie i o tym.
Rozwiązanie konkursu
W komentarzach było kilka propozycji co
oznacza attrValPrnt
. bestils, zaproponował attractvalueprint
, która to nazwa też była naszym pierwszym rozwinięciem gdy mieliśmy problem i
pośrednio przyczyniła się do powstania kilku ostatnich wpisów. Paweł zaproponował pointer
, co jest bliższe prawdzie, ale kolejność liter się nie do
końca zgadza.
Otóż moi mili. attrValPrnt
to nic innego jak Attribute Value **P**a**r**e**nt**
. Jest to wskaźnik na słownik nadrzędny, w którym trzeba szukać
wartości dla danego atrybutu pod takim samym kluczem. Fajne prawda?