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.
1. Odpowiednie nazywanie funkcji, zmiennych i klas
4. Tworzenie klas od siebie niezależnych
5. Stosowanie jednej asercji na test + wyjaśnienie dla początkujących
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
.
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
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:
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 😉
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.
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())
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:
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ę.
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)!
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!
© 2024 JavaReady.pl Bartek Kalka | Opinie | Oferta Szkoleń | Kontakt
Trzeba przyznać że poziom wymagań względem juniorów mocno wzrósł przez lata…
Trochę tak, ale wszystkiego da się nauczyć 😉
Ciężka praca + na pewno praca z dobrym mentorem to coś, co pozwoli efektywnie się uczyć.