#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 two­jej przy­gody z pro­gramowaniem w Javie powinieneś wiedzieć. Pod­stawy to na pewno fun­da­men­ty pro­gramowa­nia obiek­towego, częs­to opisy­wane pod akro­n­imem SOLID. W dzisiejszym wpisie zebral­iśmy dobre prak­ty­ki, które z naszej strony uważamy za najważniejsze i krótko je opisal­iśmy. Zaprasza­my do zapoz­na­nia się z nimi.

SOLID to akro­n­im od:
S – Sin­gle-respon­si­b­li­ty prin­ci­ple
O – Open-closed prin­ci­ple
L – Liskov sub­sti­tu­tion prin­ci­ple
I – Inter­face seg­re­ga­tion prin­ci­ple
D – Depen­den­cy Inver­sion Prin­ci­ple

Więcej o każdym z tych ele­men­tów zna­jdziesz tutaj. — dzisi­aj skupimy się jed­nak przede wszys­tkim na prak­tyce.

Always code as if the guy who ends up main­tain­ing your code will be a vio­lent psy­chopath who knows where you live.

Rick Osborne

0.Nazewnictwo

Po pier­wsze, kod pisze­my po ang­iel­sku (na naszym blogu wrzu­camy kod w języku pol­skim, po to, by było by go łatwiej zrozu­mieć początku­ją­cym) — jed­nak cały świat pro­gra­mu­je po ang­iel­sku (* — z mały­mi wyjątka­mi, ale nie są to najlep­sze prak­ty­ki, np. w pro­jek­tach rzą­dowych częs­to wyma­gane jest pol­skie nazewnict­wo). Nazwy zmi­en­nych tworzymy od rzec­zown­ików licz­by poje­dynczej, nazwy metod to po pros­tu odpowied­nie cza­sown­i­ki :) Pamię­taj, by nazwy twoich zmi­en­nych były specy­ficzne i jed­noz­naczne.

Jeśli chodzi o Javę zasady nazewnict­wa zna­jdziecie w tej tabeli:

Ele­ment języ­ka Zasady tworzenia nazwy Przykłady
klasy Nazwy klas to rzec­zown­i­ki zapisane za pomocą Upper­Camel­Case — pier­wsza lit­era każdego słowa jest duża. Unikaj skrótów i akro­n­imów, chy­ba, że są one powszech­nie znane jak URL czy pdf czy HTML
class Raster;
class ImageSprite;
metody Nazwy metod to cza­sown­i­ki zapisane za pomocą low­er­Camel­Case, albo wyraże­nia, które zaczy­na­ją się od cza­sown­i­ka zapisanego małą literą, a pier­wsza lit­era każdego kole­jnego słowa jest duża.
run();
runFast();
getBackground();
zmi­enne Zmi­enne lokalne, zmi­enne obiek­tu,  pola klasy,  są zapisy­wane za low­er­Camel­Case. Nie powin­ny one zaczy­nać się od pod­kreśl­ni­ka (_), albo znaku dolara  ($), być może znasz taki zapis z np. z C++, w Javie go nie sto­su­je­my.

Nazwy zmi­en­nych powin­ny być krótkie, ale tłu­maczące się. Powin­ny zdradzać intenc­je tzn. pro­gramista, który zapoz­na­je się z pro­jek­tem po przeczy­ta­niu nazwy powinien wiedzieć do czego ta zmi­en­na służy.

Nazwy zmi­en­nych w postaci jed­nej litery nie powin­ny być stosowane, wyjątkowo, moż­na ich uży­wać do tym­cza­sowych zmi­en­nych (np. zakres iterowa­nia w pętli for). Zwycza­jowo zmi­enne typu int nazy­wamy kole­jno i, j, k, m…, a zmi­enne typu char  c, d,e…

int i;
String surname;
float myWidth;
stałe Nazwy stałych powin­ny być zapisane duży­mi lit­era­mi, a wyrazy w nice powin­ny być odd­zielone pod­kreś­likiem.  Jeśli w nazwie musisz użyć licz­by, to pamię­taj, by nie była ona na początku nazwy.
static final int MAX_PARTICIPANTS = 10;
paki­ety Nazwy paki­etów pisze­my mały­mi lit­era­mi, zwycza­jowo nazwa paki­etu jest nazwą dome­ny, ale odwracamy kole­jność poszczegól­nych członów.
pl.kobietydokodu.kursjavy

Nasz czytel­nik, Piotr (zerknij na komen­tarz poniżej!) pro­ponu­je dodatkowe reguły, z który­mi całkowicie się zgadza­my:

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

  • dla metod void — nazwą jest po pros­tu cza­sown­ik (np. ‘run’, ‘init’, ‘exe­cute­Work­flow’ itp)
  • dla metod zwraca­ją­cych wartość boolean, uży­wamy pre­fik­sów is*, has*, can* (np.: ‘isValid’, ‘can­Han­dle’, ‘hasChil­dren’ itp)1
  • dla metod zwraca­ją­cych określony typ obiek­tu — rzec­zown­ik (typ obiek­tu) lub getRzec­zown­ik (get­Ty­pO­biek­tu) (np. ‘getN­ode’, ‘getEle­ment’, ‘ele­ments’, ‘node’ itp)

Co bard­zo ważne — jeśli nazwa metody jest cza­sown­ikiem (z wyłącze­niem pre­fik­sów ‘is’, ‘has’, ‘can’, oraz ‘get’), to meto­da nie powin­na nigdy niczego zwracać (czyli być po pros­tu oper­acją).

1) Ofic­jal­na kon­wenc­ja Javy mówi, że w przy­pad­ku metod zwraca­ją­cych boolean (prymi­tyw) należy stosować prze­drostek ‘is’, nato­mi­ast w przy­pad­ku metod zwraca­ją­cych Boolean (obiekt) — prze­drostek ‘get’. Wyni­ka to z fak­tu, że meto­da zwraca­ją­ca obiekt w teorii może także zwró­cić wartość null — sugeru­jąc się nazwą, nie sposób określić co ta wartość oznacza (to tak, jak­by na pytanie ‘czy jesteś człowiekiem’ nic nie odpowiedzieć). Ta kon­wenc­ja jest częstym przed­miotem sporów i dyskusji, jest jed­nak częś­cią ofic­jal­nej doku­men­tacji języ­ka.

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

Lep­szy spójny kod w pro­jek­cie, niż część kodu, która jest zgo­da z ogól­nie przyję­ty­mi zasada­mi. Dlaczego? Odsyłam Cię do cytatu z początku tek­stu. Twój kod nie jest na tu i ter­az, ktoś kiedyś go po Tobie prze­jmie, ktoś będzie musi­ał zrozu­mieć o co w nim chodzi, umieć go zde­bu­gować, utrzy­mać i rozwi­jać. Dlat­ego lep­sza spój­na całość, niż 1001 pomysłów zaczy­nanych i porzu­canych po drodze. Stąd raz na jak­iś czas warto sprawdz­ić, czy nie należy zro­bić refak­toru, i wtedy jak najbardziej jest miejsce na popraw­ian­ie. Całoś­ci.

2. Trzymanie się ustalonej architektury

Spój­na architek­tu­ra również ułatwia rozu­mie­nie aplikacji. Jeśli macie ustalone pewne zasady, trzy­ma­j­cie się ich.

Załóżmy, że macie klasy typu „Helper” – jeśli w jed­nej częś­ci aplikacji ich metody są staty­czny­mi meto­da­mi klasy, a w drugiej są udostęp­ni­ane w postaci ser­wisu Springa, to mamy bała­gan. Pomyślisz sobie, że prze­cież obie te rzeczy dzi­ała­ją, to po co się czepi­am? Zadam więc pytanie, dlaczego w jed­nym miejs­cu zas­tosowałeś to specy­ficzne pode­jś­cie i nie możesz w nim zas­tosować tego drugiego? Jeśli nie potrafisz odpowiedzieć na to pytanie, to oznacza, że to nie architek­tu­ra, a bała­gan.

3. Dostosuj się do reguł w zespole

Bard­zo fajnie gdy od początku współpra­cy, twój zespół ustali pewne stan­dardy pra­cy. Wspól­ny for­mat­ter (albo cho­ci­aż decyz­ja tab­u­la­to­ry czy spac­je- my gło­su­je­my za spac­ja­mi!), określony schemat nazy­wa­nia branchy, com­mitów, to nie zbyt­nia for­mal­iza­c­ja, ale coś, co ułatwi Wam pracę. W szczegól­noś­ci, że narzędzia takie jak Jenk­ins, Jira itp mogą z takich reguł zro­bić użytek :)

Nic nie stoi na przeszkodzie, byś podzielił się swoi­mi pomysła­mi w tym zakre­sie, być może masz naprawdę fajny patent, który ułatwi Wam pracę. Ważne jed­nak, by mieć wspólne zasady, na które wszyscy się godzi­cie, i na który­mi każdy z członków zespołu czuwa (a wspier­ać dziel­nie może go np. check­style).

4. JavaDocs

Pisanie samotłu­maczącego się kodu to jed­no, doku­men­towanie go to drugie. O ile znaczące nazwy metod, zmi­en­nych, są Ci w stanie wytłu­maczyć lin­ij­ka po lin­i­jce co robi dany frag­ment kodu, o tyle doku­men­tac­ja pozwala na zrozu­mie­nie intencji znacznie szy­b­ciej. Pole­camy więc pisanie javadoców do Twoich klas, inter­fe­jsów i metod pub­licznych (o ile nie są one ogól­nie rozu­mi­any­mi kon­cep­ta­mi).

Java doców nie należy mylić z komen­tarza­mi do kodu, jeśli musisz do każdej lin­ij­ki dopisać wytłu­macze­nie, to nie sto­su­jesz się do numeru zero naszego zestaw­ienia.

5. Nie wymyślaj koła na nowo

Jed­ną z rzeczy, które od razy spodobały mi się pod­czas nau­ki pro­gramowa­nia to stan­dardy, na których moż­na pole­gać. Ba, nawet trze­ba. Jeśli coś jest dobrym, pole­canym, sprawd­zonym rozwiązaniem nie ma potrze­by by go nie stosować. Warto być też na bieżą­co. Jeśli masz pro­jekt w Javie 8, to do dat uży­waj api z Javy, a nie zewnętrznych bib­liotek (joda date time — jest świetne, ale dlat­ego, że gdy powstawało,cześć Javy do dat i cza­su stwarza­ła prob­le­my), czy właśnie wspom­ni­anej java.util.date. Zan­im stwierdzisz, ze napiszesz to sam, sprawdź 4 razy w internecie, czy to już nie ist­nieje. Oczy­wiś­cie, stworze­nie włas­nej bib­liote­ki może być zabawą, ale w pro­jek­cie liczy się czas, efek­ty i pewność, że rozwiązanie będzie sta­bilne i skuteczne.

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

Jeśli pisząc kod myślisz sobie: ‘a to popraw­ię, jak będzie trochę więcej cza­su’, to właśnie zmarnowałeś go przy­na­jm­niej dwa razy więcej. Nie ist­nieje coś takiego jak pisanie kodu ‘na chwilę’ (*poza hot fix­a­mi na 30 min przed demo — ale tutaj mówimy o reg­u­larnej pra­cy devel­opera), w szczegól­noś­ci, jeśli dostrze­gasz, że Two­je rozwiązanie nie jest eleganckie/zgodne ze standardami/działające/zgodne z Twoim sum­ie­niem. Pod­sta­wowy prob­lem, z takim odkładaniem na później jest taki, że w pro­jek­cie rzad­ko kiedy jest czas na przeglą­danie ot tak kodu i ewen­tu­al­ny refak­tor. Ale może być jeszcze gorzej, możesz nie zdążyć zupełnie tego popraw­ić, możesz o tym zapom­nieć, albo uznać, że dzi­ałało pół roku, to prze­cież będzie dzi­ałać… Pomyśl o oso­bie, która pewnego pięknego dnia siądzie do Two­jego kodu i w najlep­szym razie złapie się za głowę, a w naj­gorszym nie będzie w stanie napraw­ić tego chwilowego rozwiąza­nia.

Potrze­bu­jesz hot fixa? oznacz go jako //TODO i od razu wrzuć jako task do przyszłego sprintu. Nie pro­dukuj dodatkowego długu tech­nicznego, naprawdę go nie chcesz w swoim pro­jek­cie.

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

O wyborze bib­liotek pisal­iśmy już na blogu, więc tutaj najważniejszy wniosek — zawsze, ale to zawsze, miej uza­sad­nie­nie do zas­tosowa­nia tej konkret­nej tech­nologii. Uza­sad­nie­niem nie jest to, że jest mod­na, ani  że była w poprzed­nim pro­jek­cie, ani, że chcesz sobie ją wypróbować (to możesz zro­bić na boku, jako proof of con­cept). Upraszcza­jąc najlep­szym uza­sad­nie­niem jest to, że real­izu­je konkret­ną potrze­bę, której nie moż­na było zaspokoić z dostęp­nym stack­iem. Oczy­wiś­cie potrze­bą może być konkret­ny prob­lem biz­ne­sowy, efek­ty­wność pra­cy Two­jego zespołu, wyda­jność aplikacji itp.

Pamię­taj, że każ­da nowa technologia/tool/biblioteka to coś, z czym cały zespół będzie musi­ał się zapoz­nać, by móc stosować ją w swo­jej pra­cy. Waż­na jest więc też doku­men­tac­ja, tuto­ri­ale, opinie innych devel­op­erów. Warto zro­bić małą anal­izę i sprawdz­ić, czy nie jest to zbyt duże ryzyko do Two­jego pro­jek­tu.

8. done, done, done

To określe­nie pochodzi ze Scru­ma a poszczególne ‘done’ oznacza­ją:

  • kod napisany, czyli taki, który dzi­ała lokalnie (“u mnie dzi­ała”)
  • kod przetestowany, za pomocą testów jed­nos­tkowych, a także inte­gra­cyjnych
  • kod zatwierd­zony, czyli taki, który został zatwierd­zony przez Prod­uct Own­era, jako real­izu­ją­cy konkret­ną potrze­bę biz­ne­sową wynika­jącą z zada­nia. Taki kod jest zmerdżowany do mas­tera i stanowi cześć naszego pro­duk­tu.

Moż­na do tego dodać jeszcze jeden “done”, który będzie oznaczał pro­duk­cyjną gotowość, a więc doku­men­tac­je dla użytkown­i­ka, opra­cow­anie szkoleń z użytkowa­nia itp.

9. Staraj się tworzyć bezstanowe serwisy

Pomyśl, że ser­wisy to takie pio­n­ki w grze wiel­kich mafiozów. Jedyne co wiedzą, to co mają robić, nie wol­no im jed­nak pamię­tać, co robiły, ani zapamię­ty­wać szczegółów doty­czą­cych poszczegól­nych spraw. Po pros­tu dosta­ją zlece­nie, real­izu­ją je i czeka­ją na kole­jne :)

Metody ser­wisów, mogą więc mody­fikować obiekt który dostały, mogą tworzyć i zwracać nowy, ale nie powin­ny w sobie prze­chowywać żad­nych infor­ma­cji w sobie. W razie przesłucha­nia, mają prze­cież nic nie wiedzieć ;)

10. Nie rzucaj nullami, ale …

W Javie 8 mamy cud­owne wrap­pery, czyli klasę Option­al, która pozwala nam nie robić ‘ulu­bionego’ przez wszys­t­kich sprawdzenia: if (object != null), więc prosimy Cię bard­zo, sto­suj je (a w Javie 7 lub niżej możesz po pros­tu przekazy­wać kolekc­je — pus­ta kolekc­ja nie da nam równie ‘ulu­bionego’ Null­Point­erEx­cep­tion, lub korzys­tać z bib­liotek takich jak Gua­va). Jest tylko jed­no ale… nie opakowuj za pomocą Option­ali kolekcji! Wyni­ka to z tego co napisałam powyżej, kolekc­ja może być pus­ta, więc np. Optional (Ele­ments to lista ele­men­tów w bard­zo spry­t­nej bib­liotece do par­sowa­nia 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 prak­tyk jest pewnie znacznie więcej… Nie zrażaj się jed­nak, bo stan­dardy, to coś naprawdę ekstra. Mając je opanowane, nie musimy się martwić o sta­bil­ność naszego kodu, czy to jak napisać pewne rzeczy. Może­my skupić się na dostar­cze­niu wartoś­ci dla biz­ne­su.

Od nas to tyle (przy­na­jm­niej na razie), ale prosimy naszych bardziej doświad­c­zonych czytel­ników, o podzie­le­nie się w komen­tarzach swoi­mi zasada­mi, tak byśmy mogli na bieżą­co uzu­peł­ni­ać ten wpis!

Rady od Was:

  •  Lep­iej pra­cow­ać nad kodem godz­inę codzi­en­nie niż dwie godziny co dru­gi dzień. (Jarek)
    Wdrażanie się do kodu, zostaw­ionego, “roz­grze­banego zada­nia” zawsze zaj­mu­je trochę cza­su. Warto więc nie robić sobie prz­er­wy od pro­gramowa­nia, jeśli nie trze­ba i cho­ci­aż mniej, to pracować/uczyć się codzi­en­nie. W szczegól­noś­ci gdy jesteś na początku/uczysz się nowej tech­nologii, każ­da dłuższa prz­er­wa zmusi Cię do prze­r­o­bi­enia mate­ri­ału na nowo.
  •  
  •  
  •  
  • 31
  •  
  • Cześć, wielkie dzię­ki za uwa­gi! Uzu­pełn­imy zaraz wpis o wspom­ni­ane kon­wenc­je nazewnict­wa.
    W kwestii zwraca­nia nulli/optionali — nie do koń­ca się zgadza­my ;) O ile wspom­ni­any wzorzec jest jak najbardziej poprawną opcją, jego zas­tosowanie jest bard­zo nieprak­ty­czne w aplikac­jach, z który­mi pracu­je się na codzień. Jed­nocześnie ist­nieją oper­ac­je, które mogą zwró­cić ‘nic’, jed­nocześnie jest to popraw­na wartość (vide: nie chce­my tutaj wyjątku). Przykła­dem niech będzie API w rodza­ju ‘get­ById’ w więk­szej aplikacji — z różnych powodów takie zapy­tanie może skutkować zwróce­niem ‘niczego’, co jed­nak nie wyma­ga rzu­ca­nia wyjątku (z uwa­gi na specy­fikę sys­te­mu, dany węzeł może po pros­tu nie ‘wiedzieć’ o określonym obiek­cie). Wzorzec ten nie do koń­ca sprawdza się też w przy­pad­ku ser­wisów i sys­temów rozpros­zonych — imple­men­tac­ja pełnego typowa­nia i hier­ar­chii obiek­tów na poziomie API tylko po to, aby go zaim­ple­men­tować to trochę prze­sa­da po stron­ie ‘poprawnoś­ci’ (i częs­to z uwa­gi na różne zas­tosowane tech­nolo­gie rzecz niemożli­wa).
    Poz­draw­iamy!

  • Darek

    Nieste­ty trzy­manie się szty­wno reguły, iż meto­da z cza­sown­ikiem nic zwraca, nie jest możli­we, albo raczej nieefek­ty­wne.
    Nie będę tu pod­pier­ał się pop­u­larny­mi bib­lioteka­mi czy frame­worka­mi, w których takie metody coś zwraca­ją, ale podam przykład z włas­nego pod­wór­ka. Wywołanie metod przez rmi, które jest zawsze kosz­towne i dodatkowo wyko­nanie kodu w ramach transakcji. Żądanie wyma­ga zwróce­nia wyników z poszczegól­nych kroków tej metody. Owszem moż­na później zaciągnąć potrzeb­ne dane z bazy, ale to niepotrzeb­na i zbęd­na akc­ja, sko­ro wcześniej mamy do nich dostęp w metodzie z cza­sown­ikiem.

  • Darek

    Powyższe prak­ty­ki będą przy­datne nie tylko juniorom.
    Chęt­nie poz­nałbym Wasze reguły komi­towa­nia.
    Sam mam prob­lem z utrzy­maniem w tej dziedzinie porząd­ku i spójnoś­ci.

    • To tem­at, w którym moż­na by wiele napisać ;) I ciekawy pomysł na kole­jny artykuł, dzię­ki! Obaw­iam się że w komen­tarzu trochę ciężko będzie ująć wszys­tko, ale najważniejsze:
      1) Opisy com­mitów, które mają jakąkol­wiek wartość (np. odniesie­nie do zada­nia w bug track­erze + opis zmi­an)
      2) Jeden com­mit / istot­ną mody­fikację, a nie jeden com­mit na fea­ture — dużo łatwiej robić review i testować co tak naprawdę poszło nietak
      3) Każdy com­mit musi się budować, a aplikac­ja dzi­ałać (jeśli z jakiegoś powodu to niemożli­we — więk­szy refac­tor, budowanie nowego fea­ture itp — wtedy wyraźne zaz­nacze­nie tego w opisie com­mi­ta)
      4) Push na główny branch tylko po review (i z linkiem do review w opisie!)

  • Łukasz

    ale nie powin­ny w sobie prze­chowywać żad­nych infor­ma­cji w sobie” : P Mały błąd