Nazwy metod, atrybutów i zmiennych na poważnie

Patryk Woziński
5 min readDec 4, 2018

--

Czasami spotykamy się z problemami nazewnictwa poszczególnych elementów naszej aplikacji. Mogą to być gettery, settery, klasy z wieloma rzeczownikami w nazwie — to nie najlepsze praktyki, które moglibyśmy wykorzystać do pisania naszego kodu. Powinniśmy traktować obiekty jak istoty żywe. Na przykład obiekt Doctor. Do pobrania jego imienia nie powinniśmy wykorzystywać czegoś w stylu readName() — to wymuszenie na obiekcie sposobu zachowania, a to on powinien o tym zdecydować. Zapytajmy obiekt o imię za pomocą name() — to czytelniejsza forma wyciągania wartości. Co w momencie, gdy zechcemy zmienić imię naszego Doctora? Powinniśmy wykonać na nim metodę o nazwie idealnie odpowiadającej zachowaniu, które zaistnieje — zmiana nazwy czyli rename($name). Nie możemy setować imienia żywych istot — wykorzystajmy manipulacje, których nazwy określą nam to, co się z nimi stanie. Sławomir Sobótka (Bottega IT) dał genialny przykład, w którym wyśmiał podejście get/set w sposób, który zaprezentowałem poniżej. Wkładamy kiełbasę do brzucha człowieka zamiast go nakarmić. 😂 — Czyli proceduralne podejście do struktur danych zamiast zachowań obiektów.

Sławomir Sobótka (Bottega IT) — “Nie koduj, pisz prozę — lingwistyczne techniki wychodzące poza Clean Code”

Dlaczego poprawne nazewnictwo metod jest tak istotne? Dzięki niemu możemy przeczytać nasz kod klas, czy poszczególnych metod jak książkę. Zwiększa to naszą produktywność, a przez to firma więcej zarabia. Pamiętajmy, że czytamy kod duuużo częściej niż go piszemy. Pielęgnujmy nawet takie drobnostki jak konwencje nazewnictw i myślmy obiektowo zamiast proceduralnie jak w Pascalu tworząc piramidy getterów i setterów.

Nazewnictwo metod pytających

Metody pytające to tak zwane queriesy, buildery czy readery danych. Pozwalają one nam odczytywać, znajdywać, wyciągać dane z różnych miejsc. Nie powinniśmy wykorzystywać w nich prefixów w stylu readX(), findX(), getX() czy innych podobnych. To wymuszanie na obiekcie konkretnych zachowań sposobu, w który dobierzemy się do danych. Najlepszą metodą nazewnictwa tego typu metod jest wykorzystywanie rzeczowników. Określają one w bardzo przejrzysty sposób to, co się znajduje w tego typu metodach. Jeżeli nazwa metody nie zawiera czasowników, to nic nam nie śmierdzi w kodzie i nie kusi do złamania podstawowych zasad CQRS. Za przykład możemy wziąć ponownie klasę lekarza — Doctor, który ma różne typy relacji do adresów w których obsługuje pacjentów. Metoda taka jak address() idąc wyżej opisaną drogą umożliwi nam wyciągnięcie pojedynczego adresu (gdy relacja jest 1:1), a zaś w momencie, gdy użyjemy addresses() uzyskamy dostęp do całej kolekcji adresów lekarza. Logiczne?

Kolejną często spotykaną, złą praktyką jest nazywanie metod z wykorzystaniem suffixów, w których to deklarujemy formę zwracanego typu. Przykładowo addressesArray() czy addressesJson() — ponownie zmuszamy obiekt do zwrócenia konkretnych danych, fu! Wystarczy nam przecież do tego statyczna deklaracja zwracanego typu i… tyle.

Nazewnictwo metod pytających o wartości logiczne

Logiczne wartości mogłyby być częścią wyżej opisanego rozdziału dotyczącego queriesów, aczkolwiek stwierdziłem, że czytelniej będzie — gdy przeniosę to do następnego akapitu.

W sytuacji, gdy pytamy obiekt o coś, co jest reprezentowane przez wartość logiczną, powinniśmy to oznajmić czytelnikowi kodu. Korzystając z naszego przykładu klasy Doctor — możemy zapytać go o dostępność przez isAvailable() — zaś jeżeli chcemy sprawdzić czy jest on aktywnym profilem, to wystarczy metoda isActive() — nic trudnego, a zachowanie spójnego nazewnictwa tak bardzo ułatwia pracę. Nie musimy upewniać się, czy metoda na pewno zwraca boolean — sama konwencja mówi nam o tym przez pytanie logiczne. Bardzo fajną możliwość posiada język Ruby, w którym możemy użyć metod typu active? — a to wszystko dzięki wsparciu znaków zapytania i wykrzykników.

Nazewnictwo metod modyfikujących

Metody modyfikujące to nic innego jak commandy, manipulatory, które stosowane są wszędzie — nie tylko w momencie, gdy świadomie wykorzystujemy podejście Command Query Responsibility Segregation. Często są to nazwy metod serwisów aplikacyjnych, metody w modelach — znajdziemy je wszędzie! Wracając szybko do przykładu z lekarzem — możemy na nim przeprowadzić aktywację konta za pomocą activate() lub przeprowadzić jego banicję przez zablokowanie block(). Czyż te metody nie brzmią lepiej od setActive(true) czy setBlock(true) i tym podobnych? Dodatkowo tego typu przykład, to metody biznesowe — nie przekazujmy parametrów tylko zbudujmy deactivate(), która z powodzeniem zastąpi nam setowanie atrybutu active na wartość false. Największym piekłem są miejsca, w których mamy setStatus(1) dla aktywnego konta, setStatus(2) dla nieaktywnego, a jeszcze więcej podobnych dla każdego innego przypadku stanu w jakim znajduje się obiekt. Tego typu metody NIE określają zachowania, które w nich przebiegają.

Oczywiście obowiązkową praktyką jest unikanie zwracania czegokolwiek z metod typu mutator. Jedyną dopuszczalną możliwością jest return $this, gdy już naprawdę chcemy zbudować sobie łańcuszek z użyć naszych metod.

Zachowania manipulujące powinny opisywać za pomocą czasowników to, co obiekt potrafić zrobić. Oczywiście warto wystrzegać się czegoś takiego jak sendGuzzleRequest() — to już z góry jest przegrana nazwa, gdyż sugeruje nam implementację i narzuca rozwiązanie zachowania. Metody zaczynają uwalniać swój smród, gdy ich nazwy to połączenia wielu słów w stylu changeEmailAndSendToPatients() — na pierwszy rzut oka widzimy, że zasada Single Responsibility jest złamana. Zmiana i wysyłka maila do pacjentów — czy to nie czasami dwie całkowicie różne odpowiedzialności? :)

Nazewnictwo zmiennych lokalnych i atrybutów klas

Podobnie jak metody pytające — tutaj także powinniśmy stosować rzeczowniki. Nie chcemy tutaj ani namiastki czasowników, bo zaburzą nasze właściwości. Zmienne takie jak $htmlContent są problematyczne, gdyż pokazują nasze detale implementacyjne. Jeżeli musimy dodawać prefix html oznacza to najczęściej, że nie mamy domkniętego kontekstu i klasa/metoda posiada całkowicie różne odpowiedzialności. Budowa HTML-a oraz budowa XML-a to dwa osobne zachowania. Jeżeli mamy możliwość odpuszczenia prefixu — zróbmy to, gdyż może on sugerować inne możliwe zmienne w naszej metodzie zawierające jakąś wartość.

Często zmienne, które prefixujemy jakimś kontekstem zdradzają nam moment, w którym powinniśmy zastanowić się nad enkapsulacją do osobnej klasy. Przykładem mogą być zmienne addressStreet, addressCity, addressPostcode — może warto byłoby wynieść to wszystko jako obiekt Address? Dzięki temu wyniesiemy informacje adresowe w jedno miejsce, a w naszej metodzie będziemy mieli jedynie zmienną $address, która to przechowuje obiekt adresu ze wszelkimi detalami możliwymi do wyciągnięcia na przykład poprzez $address->street(). Dużo łatwiej będzie nam też operować na jednej zmiennej zamiast grupy zmiennych z odpowiednim prefixem. Dodatkowo w momencie, gdy wyjdzie nam potrzeba przekazania ich dalej — wystarczy jeden parametr zamiast X. 👊

Co możemy osiągnąć aplikując powyższe zasady?

Pierwszą korzyścią ze stosowania poprawnego nazewnictwa obiektowego jest fakt, że kod taki jest łatwy do przeczytaniu. Czasami spotykamy różne konwencje nazewnictwa: find(), get(), take(), read(), print(), które służą do wyciągania wartości — szczęście, gdy wykorzystywane są jednolicie i spójnie. Używanie powyższych konwencji zamiennie powoduje chaos w kodzie i jest to nieczytelne w momencie, gdy nowy czytelnik kodu zabiera się do pracy. Obiekty typu Query zwracają różne dane, Command umożliwia nam modyfikację — nie mogą one nigdy zajmować się obiema funkcjonalnościami jednocześnie. Zwracamy, albo modyfikujemy — tak jak nauczył nas Greg Young. Jeżeli zżyjemy się z tą zasadą, to staniemy się lepszymi developerami i wprowadzimy zasadę Single Responsibility z SOLIDa do naszych aplikacji. Pamiętajmy, że prawidłowe obiekty, to takie, które są apatyczne i introwertyczne. :) Ułatwmy im to!

--

--

Patryk Woziński

Product Engineer with many years of experience in creating and designing web applications. #DDD freak