5 rzeczy, które musi znać każdy Junior Java Developer

Ten wpis jest streszczeniem książki, którą przeczytałem jako Junior Java Developer. Czysty Kod.

Są to zagadnienia, które na pewno warto szlifować, ponieważ na 100% spotkasz je w swojej przyszłej pracy.

Pamiętaj, że warto rozwinąć te tematy:

Potrenuj pisząc kod i przeczytaj książkę jeśli coś jest dla Ciebie niejasne.

Spis treści

1. Odpowiednie nazywanie funkcji, zmiennych i klas

2. Pisanie krótkich funkcji

3. Komentarze w kodzie

4. Tworzenie klas od siebie niezależnych

5. Stosowanie jednej asercji na test + wyjaśnienie dla początkujących

6. Podsumowanie

1. Odpowiednie nazywanie funkcji, zmiennych i klas.

Nazewnictwo jest kluczowe w Twojej własnej pracy jak i z innymi programistami.

Kod powinno się czytać bez problemu, a w tym pomaga używanie odpowiednich nazw.

Ustalono dobre praktyki, których powinno się trzymać i musisz je poznać.

Przykładowo:

Zamiast nazywać zmienną x nazwij ją peopleCounter.

Zamiast nazywać funkcję people() nazwij ją getPeople() albo calculatePeople().

Funkcje powinny być czasownikami lub jak w przypadku getPeople() z przedrostkiem get.

Zamiast nazywać klasę czasownikiem Customize nazwij ją rzeczownikiem Customer.

2. Pisanie krótkich funkcji

W pracy (prawdopodobnie przyszłej) i w swoim własnym kodzie pisz jak najmniejsze funkcje.

Lepsza funkcja, która ma 10 linii niż 50.

Najważniejsze by funkcja robiła jedną rzecz, mówi się by: była odpowiedzialna za jedną czynność.

Jeśli nazywamy funkcję mowTheLawn() (czyli skoś trawnik), to powinna ona TYLKO skosić trawnik, a nie jeszcze nalewać paliwo do kosiarki.

Zerknij na poniższy przykład ze złą i dobrą praktyką pisania funkcji

Długa i nieczytelna funkcja getAllRates() – DON’T DO IT!


public Rates getAllRates() {

    if (nbpServiceClientRepository != null) {
        return nbpServiceClientRepository.getAllRates();
    }

    final Rates[] exchangeRatesResponse = webClient.get()
        .retrieve()
        .bodyToMono(Rates[].class)
        .block();

    assert exchangeRatesResponse != null;

    final Rates first = Arrays.stream(exchangeRatesResponse)
        .collect(Collectors.toList())
        .stream()
        .findFirst()
        .orElseThrow(() - > new IllegalStateException("Not found"));

    this.nbpServiceClientRepository = new NbpServiceClientRepository(first);

    return first;
}

Ta funkcja robi zbyt dużo: 

  • 1. w linii od 30 do 33 pobiera odpowiedź za pomocą clienta http 
  • 2. w linii 36 ma bardzo długi łańcuch wywołań, który trzeba analizować i zajmie to sporo czasu
  • 3. w linii 38 zapamiętuje wyniki tworząc nowy obiekt

Jednym słowem jest to brzydka funkcja, bo robi zbyt wiele i trzeba się zastanawiać nad nią bardzo długo.

A można to zrobić lepiej 😉

Ta sama funkcja getAllRates(), ale podzielona na mniejsze funkcje – DO IT!

public Rates getAllRates() {
   if (nbpServiceClientRepository != null) {
      return nbpServiceClientRepository.getAllRates();
   }
   final Rates rates = getRates();
   saveToCacheRepository(rates);
   return rates;
}

private Rates getRates() {
   return getAllExchangeRates()
      .orElseThrow(() -> new IllegalStateException("Not found"));
}

private Optional <Rates> getAllExchangeRates() {
   final Rates[] exchangeRatesResponse = getRateResponse();
   return getFirstRate(exchangeRatesResponse);
}

private Rates[] getRateResponse() {
   return webclient.get()
      .retrieve()
      .bodyToMono(Rates[].class)
      .block();
}

private Optional <Rates> getFirstRate(Rates[] exchangeRatesResponse) {
   return Arrays.stream(exchangeRatesResponse)
      .collect(Collectors.toList())
      .Stream()
      .findFirst();
}

private void saveToCacheRepository(Rates rates) {
   this.nbpServiceClientRepository = new NbpServiceClientRepository(rates);
}


Spójrz na funkcje getAllRates() teraz ma tylko 6 linii zamiast 13!

Oprócz tego zyskaliśmy więcej mniejszych funkcji, które mają swoje nazwy opisujące jedną konkretną czynność.

O wiele łatwiej czyta się taki kod i w przypadku błędu poprawiamy tylko jedną z nich.

Plusem również jest to, że czyta się ten kod jak gazetę z góry na dół. 

Do tego mamy nagłówki funkcji, dzięki temu nie musimy od razu poznawać całej implementacji danej funkcji jeśli nie chcemy.

3. Komentarze w kodzie

Komentarze są martwe, tzn. nikt nie dba o komentarze i w pewnym momencie je nawet ignorujemy wzrokiem.

Z mojego doświadczenia i na podstawie książki wychodzi na to, że bardzo ważne jest, by stosować ich jak najmniej.

Powinno się pisać kod na tyle zrozumiale (nazywać odpowiednio funkcje, zmienne lub klasy) by komentarze nie były potrzebne.

W “Czystym Kodzie” podany jest sztandarowy przykład i go tutaj przytoczę:

   // Sprawdzenie, czy pracownik ma prawo do wszystkich korzyści
   if ((employe.flags & HOURLY_FLAG) && (employee.age > 65))

Wystarczy zmienić odrobinę implementację i ukryć warunek w funkcji isEligibleForFullBenefits()⁣, która opisuje dokładnie co robi. Wtedy nie musimy pisać komentarza tak jak w powyższym kodzie.

   if (employee.isEligibleForFullBenefits())

4. Tworzenie klas od siebie niezależnych

W Javie im więcej klas tym lepiej.

Wolę byście na początku swojej drogi myśleli tą strategią niż tworzyli “klasy worki” zawierające tysiące linii kodu.

Poniżej jest klasa, która zawiera mnóstwo funkcji i od razu wiadomo, że jest ona za duża (nawet bez podanych ciał tych metod).

Widać, że klasa ta nie wykonuje jednej czynności, czyli na przykład:

  • Powinna przetwarzać tylko zapytania HTTP,
  • a po pierwsze przetwarza ona te zapytania, po drugie zapisuje je do bazy danych, po trzecie drukuje zawartość na ekranie, po czwarte wysyła emaila z rezultatami.
class Manager {

   public String getCustomizerLanguagePath()
   public void setSystemConfigPath(String systemConfigPath)
   public String getSystemConfigDocument()
   public void setSystemConfigDocument(String systemConfigDocument)
   public boolean getGuruState( )
   public boolean getNoviceState()
   public boolean getOpenSourceState()
   public void showObject(MetaObject object)
   public void showProgress(String s)
   public boolean isMetadataDirty()
   public void setIsMetadataDirty(boolean isMetadataDirty)
   public boolean getLastFocusedComponent()
   public void setLastFocused(String lastFocused)
   public void setMouseSelectState(boolean isMouseSelected)
   public boolean isMouseSelected()
   public Project getLanguageManager()
   public Project getProject()
   public Project getFirstProject()
   public Project getLastProject()
   public String getNewProjectName()
   public void setComponentSizes(Dimension dim)
   public String getCurrentDir()
   public void setCurrentDir(String newDir)
   public void updateStatus(int dotPos, int markPos)
   public Class[] getDataBaseClasses()
   public Project getMetadataFeeder()
   public void addProject(Project project)
   public boolean setCurrentProject(Project project)
}

Jeśli jeszcze tego nie widzisz…

Przypominaj sobie, w momencie pisania każdej klasy, poniższą regułkę:

Moje klasy są od siebie tak niezależne, jak dziecko, które znalazło pierwszą pracę i jest w stanie się samo utrzymać, wtedy staje się samodzielne i może, jeśli chce, współpracować z rodzicami i innymi dorosłymi, ale już od nich nie zależy finansowo 🙂

Współpracować jest tutaj ważnym określeniem.

Jeśli klasy ze sobą współpracują, korzystają ze swoich umiejętności, wzajemnie nie wpływając na swoje implementacje, wtedy Twój projekt jest bliski sukcesu.

Tak trzymać 😉

Aby to osiągnąć, idealnym na początek drogi, założeniem byłoby tworzenie klasy z tylko jedną metodą publiczną, a reszta metod to metody prywatne.

Klasy wzajemnie będą korzystać właśnie tylko z tej jednej dostępnej metody w każdym przypadku.

Zobacz poniższą klasę Version.

Nie jest idealna jak w formułce powyżej, ponieważ ma dwie metody publiczne.

Natomiast, zdecydowanie realizuje jedną czynność.

Po prostu wszystko w niej dotyczy wersji i tyle.

Nie ma tutaj żadnego drukowania czy umieszczania w bazie danych.

Jedna zależność.

DO IT!!!

public class Version {
   
   public int getSimpleVersionNumber() {return 1;}

   public int getExtendedVersionNumber() {return 1000;}
}

Jeśli chcemy to możemy w innej klasie użyć metod getSimpleVersion() lub getExtendedVersionNumber().

Bardzo złą praktyką byłoby dodanie tutaj nowej metody odpowiedzialnej właśnie za wydrukowanie tych wartości.

Jeśli chcemy dodać nową funkcję to patrzymy na regułkę z dzieckiem powyżej i już wiemy, że powinniśmy stworzyć nową klasę.

5. Stosowanie jednej asercji na test + wyjaśnienie dla początkujących

Zdaję sobię sprawę, że testy są dla większości osób czarną magią, dlatego wyjaśnię ten podpunkt najłatwiej jak tylko potrafię.

Uważam, że: kandydat na Junior Java Developera zyska bardzo wiele na rynku pracy jeśli potrafi napisać chociaż JEDEN sensowny test jednostkowy i integracyjny.

Dlatego przekażę Wam moją wiedzę na ten temat i tego jak ja się tego uczę w kolejnych wpisach 😉

W przyszłości rozwiniemy temat testów jednostkowych z perspektywy Java developera.

Na końcu prawie każdego testu jednostkowego (zdjęcie poniżej) musimy sprawdzić, czy kod, który wykonujemy zgadza się z naszym założeniem i działa prawidłowo.

Testujemy jednostkowo, więc z naszego kodu odpalamy funkcję

getLoanForClient(int id, String name, String surname)

Napisaliśmy naszą funkcję tak, że oczekujemy dla klienta z

id=1,

imieniem=“Bartlomiej” 

nazwiskiem=“Kalka” 

zwróci nam wartość pożyczki w banku równą 2000.

No i to jest właśnie nasza asercja!

Asercję możesz przeczytać tak:

Zakładam, że zwrócona wartość po uruchomieniu metody getLoanForClient jest równa liczbie 2000.

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;

class LoanTest {

@Test
public void should_return_client_loan_when_client_exists_in_database() {
   LoanService loanService = new LoanService();

   int loanValue = loanService.getLoanForClient( id: 1, name: "Bartlomiej", surname: "Kalka"); 

   assertEquals( expected: 2000, loanValue);
}

}

I nic więcej, według “Czystego Kodu” żadnych asercji więcej w tym teście nie piszemy.

Nazwanie odpowiednio testu i jedna asercja to coś, co Ty jako autor i inni programiści później czytający ten kod, jesteście w stanie bardzo szybko przeczytać i zrozumieć, o co tutaj chodzi.

A o to chodzi w programowaniu. Piszmy kod jak najbardziej zrozumiały dla nas (ludzi)!

6. Podsumowanie

Z pewnością osoby, które poznały i zrozumiały tych 5 streszczonych założeń z książki “Czysty Kod” są w lepszej pozycji na rynku od innych Juniorów, którzy dopiero zamierzają je poznać i starać się o pracę 🙂

Miłego dnia!

Jakie jeszcze aspekty powinien znać Junior? Czy czegoś tutaj Twoim zdaniem brakuje? Daj znać w komentarzu!

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *