#Niezbędnik Juniora. Dobre praktyki dla początkującego programisty

By 29 lipca 2016Niezbędnik Juniora

Są rzeczy, które już od samego początku twojej przygody z programowaniem w Javie powinieneś wiedzieć. Podstawy to na pewno fundamenty programowania obiektowego, często opisywane pod akronimem SOLID. W dzisiejszym wpisie zebraliśmy dobre praktyki, które z naszej strony uważamy za najważniejsze i krótko je opisaliśmy. Zapraszamy do zapoznania się z nimi.

SOLID to akronim od:
S – Single-responsiblity principle
O – Open-closed principle
L – Liskov substitution principle
I – Interface segregation principle
D – Dependency Inversion Principle

Więcej o każdym z tych elementów znajdziesz tutaj. – dzisiaj skupimy się jednak przede wszystkim na praktyce.

Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.

Rick Osborne

0.Nazewnictwo

Po pierwsze, kod piszemy po angielsku (na naszym blogu wrzucamy kod w języku polskim, po to, by było by go łatwiej zrozumieć początkującym) – jednak cały świat programuje po angielsku (* – z małymi wyjątkami, ale nie są to najlepsze praktyki, np. w projektach rządowych często wymagane jest polskie nazewnictwo). Nazwy zmiennych tworzymy od rzeczowników liczby pojedynczej, nazwy metod to po prostu odpowiednie czasowniki :) Pamiętaj, by nazwy twoich zmiennych były specyficzne i jednoznaczne.

Jeśli chodzi o Javę zasady nazewnictwa znajdziecie w tej tabeli:

Element języka Zasady tworzenia nazwy Przykłady
klasy Nazwy klas to rzeczowniki zapisane za pomocą UpperCamelCase – pierwsza litera każdego słowa jest duża. Unikaj skrótów i akronimów, chyba, że są one powszechnie znane jak URL czy pdf czy HTML
class Raster;
class ImageSprite;
metody Nazwy metod to czasowniki zapisane za pomocą lowerCamelCase, albo wyrażenia, które zaczynają się od czasownika zapisanego małą literą, a pierwsza litera każdego kolejnego słowa jest duża.
run();
runFast();
getBackground();
zmienne Zmienne lokalne, zmienne obiektu,  pola klasy,  są zapisywane za lowerCamelCase. Nie powinny one zaczynać się od podkreślnika (_), albo znaku dolara  ($), być może znasz taki zapis z np. z C++, w Javie go nie stosujemy.

Nazwy zmiennych powinny być krótkie, ale tłumaczące się. Powinny zdradzać intencje tzn. programista, który zapoznaje się z projektem po przeczytaniu nazwy powinien wiedzieć do czego ta zmienna służy.

Nazwy zmiennych w postaci jednej litery nie powinny być stosowane, wyjątkowo, można ich używać do tymczasowych zmiennych (np. zakres iterowania w pętli for). Zwyczajowo zmienne typu int nazywamy kolejno i, j, k, m…, a zmienne typu char  c, d,e…

int i;
String surname;
float myWidth;
stałe Nazwy stałych powinny być zapisane dużymi literami, a wyrazy w nice powinny być oddzielone podkreślikiem.  Jeśli w nazwie musisz użyć liczby, to pamiętaj, by nie była ona na początku nazwy.
static final int MAX_PARTICIPANTS = 10;
pakiety Nazwy pakietów piszemy małymi literami, zwyczajowo nazwa pakietu jest nazwą domeny, ale odwracamy kolejność poszczególnych członów.
pl.kobietydokodu.kursjavy

Nasz czytelnik, Piotr (zerknij na komentarz poniżej!) proponuje dodatkowe reguły, z którymi całkowicie się zgadzamy:

W zależności od typu zwracanego, nazwy metod różnią się między sobą:

  • dla metod void – nazwą jest po prostu czasownik (np. ‚run’, ‚init’, ‚executeWorkflow’ itp)
  • dla metod zwracających wartość boolean, używamy prefiksów is*, has*, can* (np.: ‚isValid’, ‚canHandle’, ‚hasChildren’ itp)1
  • dla metod zwracających określony typ obiektu – rzeczownik (typ obiektu) lub getRzeczownik (getTypObiektu) (np. ‚getNode’, ‚getElement’, ‚elements’, ‚node’ itp)

Co bardzo ważne – jeśli nazwa metody jest czasownikiem (z wyłączeniem prefiksów ‚is’, ‚has’, ‚can’, oraz ‚get’), to metoda nie powinna nigdy niczego zwracać (czyli być po prostu operacją).

1) Oficjalna konwencja Javy mówi, że w przypadku metod zwracających boolean (prymityw) należy stosować przedrostek ‚is’, natomiast w przypadku metod zwracających Boolean (obiekt) – przedrostek ‚get’. Wynika to z faktu, że metoda zwracająca obiekt w teorii może także zwrócić wartość null – sugerując się nazwą, nie sposób określić co ta wartość oznacza (to tak, jakby na pytanie ‚czy jesteś człowiekiem’ nic nie odpowiedzieć). Ta konwencja jest częstym przedmiotem sporów i dyskusji, jest jednak częścią oficjalnej dokumentacji języka.

1. Spójność jest ważniejsza od poprawności (ogólnie)

Lepszy spójny kod w projekcie, niż część kodu, która jest zgoda z ogólnie przyjętymi zasadami. Dlaczego? Odsyłam Cię do cytatu z początku tekstu. Twój kod nie jest na tu i teraz, ktoś kiedyś go po Tobie przejmie, ktoś będzie musiał zrozumieć o co w nim chodzi, umieć go zdebugować, utrzymać i rozwijać. Dlatego lepsza spójna całość, niż 1001 pomysłów zaczynanych i porzucanych po drodze. Stąd raz na jakiś czas warto sprawdzić, czy nie należy zrobić refaktoru, i wtedy jak najbardziej jest miejsce na poprawianie. Całości.

2. Trzymanie się ustalonej architektury

Spójna architektura również ułatwia rozumienie aplikacji. Jeśli macie ustalone pewne zasady, trzymajcie się ich.

Załóżmy, że macie klasy typu „Helper” – jeśli w jednej części aplikacji ich metody są statycznymi metodami klasy, a w drugiej są udostępniane w postaci serwisu Springa, to mamy bałagan. Pomyślisz sobie, że przecież obie te rzeczy działają, to po co się czepiam? Zadam więc pytanie, dlaczego w jednym miejscu zastosowałeś to specyficzne podejście i nie możesz w nim zastosować tego drugiego? Jeśli nie potrafisz odpowiedzieć na to pytanie, to oznacza, że to nie architektura, a bałagan.

3. Dostosuj się do reguł w zespole

Bardzo fajnie gdy od początku współpracy, twój zespół ustali pewne standardy pracy. Wspólny formatter (albo chociaż decyzja tabulatory czy spacje- my głosujemy za spacjami!), określony schemat nazywania branchy, commitów, to nie zbytnia formalizacja, ale coś, co ułatwi Wam pracę. W szczególności, że narzędzia takie jak Jenkins, Jira itp mogą z takich reguł zrobić użytek :)

Nic nie stoi na przeszkodzie, byś podzielił się swoimi pomysłami w tym zakresie, być może masz naprawdę fajny patent, który ułatwi Wam pracę. Ważne jednak, by mieć wspólne zasady, na które wszyscy się godzicie, i na którymi każdy z członków zespołu czuwa (a wspierać dzielnie może go np. checkstyle).

4. JavaDocs

Pisanie samotłumaczącego się kodu to jedno, dokumentowanie go to drugie. O ile znaczące nazwy metod, zmiennych, są Ci w stanie wytłumaczyć linijka po linijce co robi dany fragment kodu, o tyle dokumentacja pozwala na zrozumienie intencji znacznie szybciej. Polecamy więc pisanie javadoców do Twoich klas, interfejsów i metod publicznych (o ile nie są one ogólnie rozumianymi konceptami).

Java doców nie należy mylić z komentarzami do kodu, jeśli musisz do każdej linijki dopisać wytłumaczenie, to nie stosujesz się do numeru zero naszego zestawienia.

5. Nie wymyślaj koła na nowo

Jedną z rzeczy, które od razy spodobały mi się podczas nauki programowania to standardy, na których można polegać. Ba, nawet trzeba. Jeśli coś jest dobrym, polecanym, sprawdzonym rozwiązaniem nie ma potrzeby by go nie stosować. Warto być też na bieżąco. Jeśli masz projekt w Javie 8, to do dat używaj api z Javy, a nie zewnętrznych bibliotek (joda date time – jest świetne, ale dlatego, że gdy powstawało,cześć Javy do dat i czasu stwarzała problemy), czy właśnie wspomnianej java.util.date. Zanim stwierdzisz, ze napiszesz to sam, sprawdź 4 razy w internecie, czy to już nie istnieje. Oczywiście, stworzenie własnej biblioteki może być zabawą, ale w projekcie liczy się czas, efekty i pewność, że rozwiązanie będzie stabilne i skuteczne.

6. Nie twórz świadomie długu technicznego

Jeśli pisząc kod myślisz sobie: ‚a to poprawię, jak będzie trochę więcej czasu’, to właśnie zmarnowałeś go przynajmniej dwa razy więcej. Nie istnieje coś takiego jak pisanie kodu ‚na chwilę’ (*poza hot fixami na 30 min przed demo – ale tutaj mówimy o regularnej pracy developera), w szczególności, jeśli dostrzegasz, że Twoje rozwiązanie nie jest eleganckie/zgodne ze standardami/działające/zgodne z Twoim sumieniem. Podstawowy problem, z takim odkładaniem na później jest taki, że w projekcie rzadko kiedy jest czas na przeglądanie ot tak kodu i ewentualny refaktor. Ale może być jeszcze gorzej, możesz nie zdążyć zupełnie tego poprawić, możesz o tym zapomnieć, albo uznać, że działało pół roku, to przecież będzie działać… Pomyśl o osobie, która pewnego pięknego dnia siądzie do Twojego kodu i w najlepszym razie złapie się za głowę, a w najgorszym nie będzie w stanie naprawić tego chwilowego rozwiązania.

Potrzebujesz hot fixa? oznacz go jako //TODO i od razu wrzuć jako task do przyszłego sprintu. Nie produkuj dodatkowego długu technicznego, naprawdę go nie chcesz w swoim projekcie.

7. Dobrze się zastanów zanim dodasz nową i super modną bibliotekę

O wyborze bibliotek pisaliśmy już na blogu, więc tutaj najważniejszy wniosek – zawsze, ale to zawsze, miej uzasadnienie do zastosowania tej konkretnej technologii. Uzasadnieniem nie jest to, że jest modna, ani  że była w poprzednim projekcie, ani, że chcesz sobie ją wypróbować (to możesz zrobić na boku, jako proof of concept). Upraszczając najlepszym uzasadnieniem jest to, że realizuje konkretną potrzebę, której nie można było zaspokoić z dostępnym stackiem. Oczywiście potrzebą może być konkretny problem biznesowy, efektywność pracy Twojego zespołu, wydajność aplikacji itp.

Pamiętaj, że każda nowa technologia/tool/biblioteka to coś, z czym cały zespół będzie musiał się zapoznać, by móc stosować ją w swojej pracy. Ważna jest więc też dokumentacja, tutoriale, opinie innych developerów. Warto zrobić małą analizę i sprawdzić, czy nie jest to zbyt duże ryzyko do Twojego projektu.

8. done, done, done

To określenie pochodzi ze Scruma a poszczególne ‚done’ oznaczają:

  • kod napisany, czyli taki, który działa lokalnie („u mnie działa”)
  • kod przetestowany, za pomocą testów jednostkowych, a także integracyjnych
  • kod zatwierdzony, czyli taki, który został zatwierdzony przez Product Ownera, jako realizujący konkretną potrzebę biznesową wynikającą z zadania. Taki kod jest zmerdżowany do mastera i stanowi cześć naszego produktu.

Można do tego dodać jeszcze jeden „done”, który będzie oznaczał produkcyjną gotowość, a więc dokumentacje dla użytkownika, opracowanie szkoleń z użytkowania itp.

9. Staraj się tworzyć bezstanowe serwisy

Pomyśl, że serwisy to takie pionki w grze wielkich mafiozów. Jedyne co wiedzą, to co mają robić, nie wolno im jednak pamiętać, co robiły, ani zapamiętywać szczegółów dotyczących poszczególnych spraw. Po prostu dostają zlecenie, realizują je i czekają na kolejne :)

Metody serwisów, mogą więc modyfikować obiekt który dostały, mogą tworzyć i zwracać nowy, ale nie powinny w sobie przechowywać żadnych informacji w sobie. W razie przesłuchania, mają przecież nic nie wiedzieć ;)

10. Nie rzucaj nullami, ale …

W Javie 8 mamy cudowne wrappery, czyli klasę Optional, która pozwala nam nie robić ‚ulubionego’ przez wszystkich sprawdzenia: if (object != null), więc prosimy Cię bardzo, stosuj je (a w Javie 7 lub niżej możesz po prostu przekazywać kolekcje – pusta kolekcja nie da nam równie ‚ulubionego’ NullPointerException, lub korzystać z bibliotek takich jak Guava). Jest tylko jedno ale… nie opakowuj za pomocą Optionali kolekcji! Wynika to z tego co napisałam powyżej, kolekcja może być pusta, więc np. Optional<Elements> (Elements to lista elementów w bardzo sprytnej bibliotece do parsowania HTML – jsoup) nigdy, przenigdy nie będzie pusty.

Podsumowanie

Tak właśnie wygląda nasza lista 11 zasad, które warto sobie wpoić od początku. Takich praktyk jest pewnie znacznie więcej… Nie zrażaj się jednak, bo standardy, to coś naprawdę ekstra. Mając je opanowane, nie musimy się martwić o stabilność naszego kodu, czy to jak napisać pewne rzeczy. Możemy skupić się na dostarczeniu wartości dla biznesu.

Od nas to tyle (przynajmniej na razie), ale prosimy naszych bardziej doświadczonych czytelników, o podzielenie się w komentarzach swoimi zasadami, tak byśmy mogli na bieżąco uzupełniać ten wpis!

Rady od Was:

  •  Lepiej pracować nad kodem godzinę codziennie niż dwie godziny co drugi dzień. (Jarek)
    Wdrażanie się do kodu, zostawionego, „rozgrzebanego zadania” zawsze zajmuje trochę czasu. Warto więc nie robić sobie przerwy od programowania, jeśli nie trzeba i chociaż mniej, to pracować/uczyć się codziennie. W szczególności gdy jesteś na początku/uczysz się nowej technologii, każda dłuższa przerwa zmusi Cię do przerobienia materiału na nowo.
  • 18
  •  
  •  
  • 31
  •  
  • Cześć, wielkie dzięki za uwagi! Uzupełnimy zaraz wpis o wspomniane konwencje nazewnictwa.
    W kwestii zwracania nulli/optionali – nie do końca się zgadzamy ;) O ile wspomniany wzorzec jest jak najbardziej poprawną opcją, jego zastosowanie jest bardzo niepraktyczne w aplikacjach, z którymi pracuje się na codzień. Jednocześnie istnieją operacje, które mogą zwrócić ‚nic’, jednocześnie jest to poprawna wartość (vide: nie chcemy tutaj wyjątku). Przykładem niech będzie API w rodzaju ‚getById’ w większej aplikacji – z różnych powodów takie zapytanie może skutkować zwróceniem ‚niczego’, co jednak nie wymaga rzucania wyjątku (z uwagi na specyfikę systemu, dany węzeł może po prostu nie ‚wiedzieć’ o określonym obiekcie). Wzorzec ten nie do końca sprawdza się też w przypadku serwisów i systemów rozproszonych – implementacja pełnego typowania i hierarchii obiektów na poziomie API tylko po to, aby go zaimplementować to trochę przesada po stronie ‚poprawności’ (i często z uwagi na różne zastosowane technologie rzecz niemożliwa).
    Pozdrawiamy!

  • Darek

    Niestety trzymanie się sztywno reguły, iż metoda z czasownikiem nic zwraca, nie jest możliwe, albo raczej nieefektywne.
    Nie będę tu podpierał się popularnymi bibliotekami czy frameworkami, w których takie metody coś zwracają, ale podam przykład z własnego podwórka. Wywołanie metod przez rmi, które jest zawsze kosztowne i dodatkowo wykonanie kodu w ramach transakcji. Żądanie wymaga zwrócenia wyników z poszczególnych kroków tej metody. Owszem można później zaciągnąć potrzebne dane z bazy, ale to niepotrzebna i zbędna akcja, skoro wcześniej mamy do nich dostęp w metodzie z czasownikiem.

  • Darek

    Powyższe praktyki będą przydatne nie tylko juniorom.
    Chętnie poznałbym Wasze reguły komitowania.
    Sam mam problem z utrzymaniem w tej dziedzinie porządku i spójności.

    • To temat, w którym można by wiele napisać ;) I ciekawy pomysł na kolejny artykuł, dzięki! Obawiam się że w komentarzu trochę ciężko będzie ująć wszystko, ale najważniejsze:
      1) Opisy commitów, które mają jakąkolwiek wartość (np. odniesienie do zadania w bug trackerze + opis zmian)
      2) Jeden commit / istotną modyfikację, a nie jeden commit na feature – dużo łatwiej robić review i testować co tak naprawdę poszło nietak
      3) Każdy commit musi się budować, a aplikacja działać (jeśli z jakiegoś powodu to niemożliwe – większy refactor, budowanie nowego feature itp – wtedy wyraźne zaznaczenie tego w opisie commita)
      4) Push na główny branch tylko po review (i z linkiem do review w opisie!)