Powtarzając żart jednego z kolegów studentów, zadziwiająco szybko na naszych Podstawach programowania w Javie wprowadzamy konteneryzację. Wszystko za sprawą projektu semetralnego, w którym będziemy zajmowali się transportem morskim towarów w kontenerach oraz modelowaniem wielkiego kontenerowca, który będzie te ładunki przewoził. Jak minie deadline to pochwalę się kodem (jeszcze nic nie napisałem), są pewne ograniczenia formalne, które utrudniają pisanie - np. cała aplikacja musi znajdować się w jednym pliku.
sobota, 30 stycznia 2021
wtorek, 26 stycznia 2021
Timebomb - zaplanowana katastrofa
Spodobał mi się pomysł opisany w artykule https://bulldogjob.pl/articles/1256-bomba-czasowa-przydatna-rzecz – od strony programistycznej jest to w zasadzie bardzo banalne: w krytycznym punkcie naszej aplikacji, który wymaga w przyszłości uzupełnienia zostawiamy Exception, który pozostaje uśpiony przez pewien czas.
Idea jest taka – nasza metoda jest krytyczna dla dalszych etapów i potrzebujemy uzupełnić informacje, ale proces uzyskiwania odpowiedzi jest bardzo długi (przez skomplikowanie projektu, procedury po stronie klienta itp). Robimy w kodzie prowizoryczne rozwiązanie i zgłaszamy klientowi pilną potrzebę uzyskania dodatkowych informacji. Exception, czyli nasza bomba, wybuchnie po ustalonym przez nas czasie, wywalając całą aplikację, o ile nie dostaniemy wcześniej potrzebnych informacji, aby załatać lukę.
Wybuch bomby niezawodnie zwraca uwagę klienta, a my przecież ostrzegaliśmy, że potrzebujemy pilnie uzupełnić dokumentację, bo może stać się coś złego. Po co ryzykować wkurzenie klienta? Bo jeżeli nasze monity będą ignorowane i wadliwy kod trafi na produkcję, to konsekwencje mogą być dla nas bardzo nieprzyjemne. A przecież ostrzegaliśmy klienta, że coś się może wywalić no i się wywaliło – nikt nie powinien być zdziwony.
Przy zastawianiu bomb trzeba mieć odpowiednie wyczucie, na pewno powinna być udokumentowana tak, aby dało się bardzo szybko ustalić dlaczego aplikacja kliencka nagle przestała działać.
niedziela, 24 stycznia 2021
Sesja nadchodzi
Zbliża się sesja, co powoduje, że mam coraz więcej nauki związanej z przedmiotami na studiach niż z tym, co sam chciałbym robić. Z tego też powodu mniejsza aktywność na blogu i na gicie.
Nasz projekt algorytmu genetycznego powoli idzie do przodu, ale wolniej, niż bym tego chciał. Projekt JaNieTakiOrk – gdzie chcę się uczyć Spring Boota na przykładzie działającej aplikacji na razie jest na holdzie, ale to dobrze, bo nie wiem jakbym znalazł czas, żeby się nim zajmować teraz.
środa, 20 stycznia 2021
Nowy projekt - erpegowy!
Już od wielu dni przyglądam się projektom open source różnego rodzaju, szukając takiego, w który mógłbym się jakoś zaangażować. Zadanie nie jest wcale banalne, bo nawet prosty projekt ma sporą barierę wejścia w kod, a co dopiero taki odpowiedzialny za większą aplikację.
Dzięki pomocy mojej przyjaciółki natrafiłem na ciekawą grupkę i aplikację: https://janietakiork.projektstudencki.pl/ Na pierwszy rzut oka wydaje się, że jest tam wszystko, czego szukam: Spring Boot, z którym miałem do tej pory stosunkowo mało do czynienia, a umówmy się, że dla javowca to must have. Mają tam testy, ale mocno niekompletne. Jest nawet fajna i kompetentna warstwa frontendowa.
Pierwsze zagłębienie się w strukturę projektu pozostawia parę pytań, wynikających raczej z mojego braku wiedzy niż wad kodu, ale zgłosiłem się do pomocy i zobaczymy za jakiś czas jaki jest zespół i co jeszcze będziemy razem robić.
Nie mogę się doczekać (a powinienem uczyć się analizy matematycznej).
niedziela, 17 stycznia 2021
Algorytm genetyczny się rozrasta
Mój najbardziej zaawansowany projekt obecnie to algorytm genetyczny, który piszę razem z Kamilem Surowcem, przy pewnym udziale Janusza Brodackiego. Możecie znaleźć go na gicie tutaj: https://github.com/PawelDabrowski83/genAlgo
Dzisiaj chcę pochwalić się diagramem, który stworzyłem. Nie jest może najbardziej czytelny, ale dość dobrze podsumowuje naszą ponadmiesięczną pracę.
sobota, 16 stycznia 2021
Checklisty w GitHub issue
Polubiłem się z zakładką Issues na GitHubie i zacząłem naprawdę aktywnie z niej korzystać w moich projektach. Fajne jest to, że nie muszę już rozpisywać prostego kanbana na zewnętrznym narzędziu i takie proste zarządzanie projektem mamy w jednym miejscu, połączone z PR.
Kolejna zabawka, która zarówno ozdabia jak i bywa przydatna to checklisty – przydatne zwłaszcza jeśli issue tak naprawdę składa się z kilku etapów lub zadań. Bo nie zawsze jest sens rozbijać każdego zadania na osobny branch.
Wygląda to tak:
W szczegółach można sprawdzić jakie są składniki checklisty i je odklikiwać. Mega!piątek, 15 stycznia 2021
Poszukiwanie pracy i feedback
Niedawno zakończyłem dość stresujący okres w moim życiu, którym było poszukiwanie pracy. Pechowo złożyło się, że trafiłem w sam środek kryzysu i ofert dla osób z niewielkim doświadczeniem czy przebranżawiających się było relatywnie niewiele. No ale nie o tym.
Chodzi o feedback, jaki osoba poszukująca pracy otrzymuje. Nazywamy to feedbackiem, ale w 99% jest to mniej lub bardziej zgrabnie ustylizowany komunikat "to nie Ciebie szukamy". No dobrze, ale czego bym oczekiwał?
Rekruterzy podają bardzo mało informacji, które mogłyby być przydatne dla poszukujących pracy. Przykłady? Komunikat o tym, że nie przechodzę do kolejnego etapu brzmi zupełnie inaczej, jeśli wiem, że kandydatów było 100, a inaczej gdy tych kandydatów była piątka.
Czy ocena została dokonana na podstawie określonego kryterium (wykształcenie kierunkowe lub jego brak, analiza cv, ocena projektów w portfolio – często się tego nie dowiemy).
środa, 13 stycznia 2021
Narzekałem na Springa zanim to było modne
To oczywiście nieprawda, bo moje doświadczenie programistyczne to niespełna dwa lata. Ale Spring ma w sobie coś takiego, co można łatwo znienawidzić.
Kiedy uczyłem się absolutnych podstaw Javy, były tematy, które sprawiały mi poważne trudności – zarządzanie projektem i bibliotekami (zawsze czegoś brakowało lub coś się wywalało), zrozumienie typów zwracanych metod i jak łatwo konwertować z jednego na drugi, servlety i Spring + Hibernate.
Może nie jestem mistrzem Javy, ale nabrałem sporo pewności w pisaniu takiego prostego kodu, i po kolei pokonywałem wszystkie blokady, poza ostatnią – Spring i Hibernate, oba nazwałbym językami adnotacji, były i są bardzo zwodnicze. W teorii upraszczają sprawę, natomiast nie narzekałbym na nie, gdyby faktycznie tak było. Niestety wyszukiwanie błędów lub próba zrozumienia co się zepsuło jest lub była dla mnie bardzo ciężka.
W czasie swojej nauki napotkałem na wystąpienia Jarka Ratajskiego, który z wielką pasją opowiadał o tym jak Spring wprowadza magię do projektu, sprawiając, że nawet proste rzeczy komplikują się. Zapis jest oczywiście prosty, ale adnotacje są tylko zasłoną.
Kupiło mnie to na tyle, że kiedy zacząłem pisać prostego CRM-a, to uznałem, że zrobię to bez magii. Proste serwlety, jsp, jak nasi ojcowie i dziadowie, żadnych udziwnień. Projekt nie został zakończony, ale napisałem całe sporo, tam też stawiałem pierwsze kroki w nauce testów, stąd znajdziecie testy dla toStringów i setterów. Nie piszę już takich testów i rzadko sprawdzam test coverage.
No ale do rzeczy. Używany model klas jest dość złożony, a zależności obsługiwane są ręcznie, tak samo mapowanie pomiędzy Entity a Dto, żadnego MapStrucka (i nawet nie dlatego, że go wtedy nie znałem, ale po prostu chciałem zrozumieć jakie problemy mogą wynikać z pisania takiej funkcjonalności). I da się bez Springa napisać prostą sieciówkę? Pewnie, że się da, ale jak przyszło do testów i podstawiania zależności, to... zresztą sami zobaczcie jak wygląda kontroller jednej z klas:
@WebServlet (name="LaborController", urlPatterns = "/labors")
public class LaborController extends HttpServlet {
private static final String CHARACTER_ENCODING = "UTF-8";
private static final String FORM_LABOR = "/WEB-INF/jsp/formLabors.jsp";
private static final String PREPARE_ALL_LABORS = "/labors?action=view";
private static final String SHOW_ALL_LABORS = "/WEB-INF/jsp/allLabors.jsp";
private static final GenericDao<LaborEntity> LABOR_DAO = new LaborDaoImpl();
private static final GenericDao<VehicleEntity> VEHICLE_DAO = new VehicleDaoImpl();
private static final GenericDao<CarEntity> CAR_DAO = new CarDaoImpl();
private static final GenericDao<CustomerEntity> CUSTOMER_DAO = new CustomerDaoImpl();
private static final MapperInterface<CarDto, Car, CarEntity> CAR_MAPPER = new CarMapper();
private static final PersonDaoInterface<PersonEntity> PERSON_DAO = new PersonDaoImpl();
private static final MapperInterface<PersonDto, Person, PersonEntity> PERSON_MAPPER = new PersonMapper();
private static final MapperInterface<CustomerDto, Customer, CustomerEntity> CUSTOMER_MAPPER = new CustomerMapper(PERSON_DAO, PERSON_MAPPER);
private static final ServiceInterface<CarDto> CAR_SERVICE = new CarService(CAR_DAO, CAR_MAPPER);
private static final ServiceInterface<CustomerDto> CUSTOMER_SERVICE = new CustomerService(CUSTOMER_DAO, CUSTOMER_MAPPER);
private static final MapperInterface<VehicleDto, Vehicle, VehicleEntity> VEHICLE_MAPPER = new VehicleMapper(CAR_MAPPER, CUSTOMER_MAPPER, CAR_SERVICE, CUSTOMER_SERVICE);
private static final ServiceInterface<VehicleDto> VEHICLE_SERVICE = new VehicleService(VEHICLE_DAO, VEHICLE_MAPPER);
private static final GenericDao<EmployeeEntity> EMPLOYEE_DAO = new EmployeeDaoImpl();
private static final MapperInterface<EmployeeDto, Employee, EmployeeEntity> EMPLOYEE_MAPPER = new EmployeeMapper(PERSON_DAO, PERSON_MAPPER);
private static final ServiceInterface<EmployeeDto> EMPLOYEE_SERVICE = new EmployeeService(EMPLOYEE_DAO, EMPLOYEE_MAPPER);
private static final ValidatorInterface<LaborDto> LABOR_VALIDATOR = new LaborValidator();
private static final MapperInterface<LaborDto, Labor, LaborEntity> LABOR_MAPPER = new LaborMapper(EMPLOYEE_DAO, EMPLOYEE_MAPPER, VEHICLE_DAO, VEHICLE_MAPPER);
private static final ServiceInterface<LaborDto> LABOR_SERVICE = new LaborService(LABOR_DAO, LABOR_MAPPER);
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding(CHARACTER_ENCODING);
String action = request.getParameter("action");
int laborId = ParameterReaderService.getIdFromRequest(request, "id");
String redir = FORM_LABOR;
if ("new".equals(action) | "edit".equals(action)) {
request.setAttribute("employees", EMPLOYEE_SERVICE.findAll());
request.setAttribute("vehicles", VEHICLE_SERVICE.findAll());
// EnumSet<Labor.StatusEnum> enums = EnumSet.allOf(Labor.StatusEnum.class);
request.setAttribute("statuses", EnumSet.allOf(Labor.StatusEnum.class));
}
switch (action) {
case "view":
Set<LaborDto> dtos = LABOR_SERVICE.findAll();
request.setAttribute("labors", dtos);
redir = SHOW_ALL_LABORS;
break;
case "delete":
LABOR_SERVICE.delete(laborId);
response.sendRedirect(PREPARE_ALL_LABORS);
return;
case "edit":
LaborDto dto = LABOR_SERVICE.read(laborId);
request.setAttribute("labor", dto);
case "new":
request.setAttribute("action", action);
default:
}
request.setAttribute("error", request.getParameter("error"));
request.setAttribute("errorMessage", request.getParameter("errorMessage"));
getServletContext().getRequestDispatcher(redir).forward(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding(CHARACTER_ENCODING);
String action = request.getParameter("action");
int laborId = ParameterReaderService.getIdFromRequest(request, "laborId");
LocalDate registrationDate = ParameterReaderService.parseLocalDate(request.getParameter("registrationDate"));
LocalDate scheduledDate = ParameterReaderService.parseLocalDate(request.getParameter("scheduledDate"));
LocalDate startedDate = ParameterReaderService.parseLocalDate(request.getParameter("startedDate"));
LocalDate finishedDate = ParameterReaderService.parseLocalDate(request.getParameter("finishedDate"));
int employeeId = ParameterReaderService.getIdFromRequest(request, "employeeId");
String descriptionIssue = request.getParameter("descriptionIssue");
String descriptionService = request.getParameter("descriptionService");
String status = request.getParameter("status");
int vehicleId = ParameterReaderService.getIdFromRequest(request, "vehicleId");
double customerCost = ParameterReaderService.getDoubleFromRequest(request, "customerCost");
double materialCost = ParameterReaderService.getDoubleFromRequest(request, "materialCost");
int mhTotal = ParameterReaderService.getIdFromRequest(request, "mhTotal");
LaborDto dto = new LaborDto();
dto.setRegistrationDate(registrationDate);
dto.setScheduledDate(scheduledDate);
dto.setStartedDate(startedDate);
dto.setFinishedDate(finishedDate);
dto.setEmployeeId(employeeId);
dto.setEmployeeFullname(EMPLOYEE_SERVICE.read(employeeId).getFullname());
dto.setDescriptionIssue(descriptionIssue);
dto.setDescriptionService(descriptionService);
dto.setStatus(status);
dto.setVehicleId(vehicleId);
dto.setVehicleSignature(VEHICLE_SERVICE.read(vehicleId).getCarSignature());
dto.setCustomerCost(customerCost);
dto.setMaterialCost(materialCost);
dto.setMhTotal(mhTotal);
String validateResult = LABOR_VALIDATOR.validate(dto);
if (!validateResult.isEmpty()) {
request.setAttribute("error", true);
request.setAttribute("errorMessage", validateResult);
request.setAttribute("labor", dto);
request.setAttribute("employees", EMPLOYEE_SERVICE.findAll());
request.setAttribute("vehicles", VEHICLE_SERVICE.findAll());
request.setAttribute("statuses", EnumSet.allOf(Labor.StatusEnum.class));
if ("edit".equals(action)) {
request.setAttribute("action", "edit");
} else {
request.setAttribute("action", "new");
}
getServletContext().getRequestDispatcher(FORM_LABOR).forward(request, response);
return;
} else {
request.setAttribute("error", false);
}
if ("edit".equals(action)) {
dto.setLaborId(laborId);
LABOR_SERVICE.update(dto);
} else {
LABOR_SERVICE.create(dto);
}
response.sendRedirect(PREPARE_ALL_LABORS);
}
}
Wklejam kod bezpośrednio a nie jako link z gita, bo wierzę, że za jakiś czas przysiądę i trochę posprzątam w tym projekcie. Wciąż chcę, żeby było bez Springa, ale na pewno da się do tego podejść lepiej. Zresztą już teraz widzę, że tej logiki w kontrolerze jest o wiele za dużo, metody napuchnięte do granic możliwości. A i ta litania zależności na początku da się poprawić, right? Right?
wtorek, 12 stycznia 2021
Matematyka na studiach
To nie jest tak, że dopiero teraz pomyślałem o tym, aby studiować informatykę. Przymierzałem się do tego też znacznie wcześniej, ale wtedy pewną barierą – i doskonale rozumiem dlaczego tak było – była konieczność nauki matematyki. To właściwie dlatego wybrałem bootcamp w Coderslab – chciałem poświęcać godziny na naukę Javy i programowania, a nie teorię, której prawdopodobnie nie wykorzystam.
Przed studiami, ostatnio uczyłem się tak porządnie matematyki około 20 lat temu, w liceum. Nawet całkiem to lubiłem, no ale obrałem profil humanistyczny i jakoś sobie radziłem w życiu bez analizy matematycznej. Teraz nadrabiam braki, przypominam sobie funkcje trygonometryczne, logarytmy, badanie zmienności funkcji i inne rzeczy, których z jakiegoś powodu nie pamiętałem.
Dodatkową barierą jest covid. Nie mam tak naprawdę wykładów z matmy, innych niż pdf z sylabusem wrzucony na uczelnianym dysku. 12h godzin ćwiczeń w ciągu semestru to trochę za mało, aby przerobić te materiały, więc jakoś muszę się ratować – oglądam wykłady z Politechniki Wrocławskiej na YT, przerabiam zadania z kanału Matemaksa, ćwiczenia z etrapeza, wyniki swoich własnych wpisuję w Wolframa i staram się zrozumieć odpowiedzi.
50-60h dodatkowych zajęć i ćwiczeń sprawiło, że pierwsze kolokwium napisałem tuż pod progiem zaliczającym (48%). Drugie i ostatnie będzie za dwa tygodnie, i jeśli napiszę je na minimum 52% (punkty z obu się sumują), to zostanę dopuszczony do egzaminu.
Głupio może to przyznać, ale cele moje na ten semestr, jeśli chodzi o matematykę, są bardzo pragmatyczne – jeśli uzyskam jakąkolwiek pozytywną ocenę zaliczającą przedmiot, to będzie wielki sukces.
niedziela, 10 stycznia 2021
Zbieranie wisienek
git revert COMMIT – wycofuje lokalnie wybrany commit. Później zmiany należy pushować na produkcję.
git cherry-pick COMMIT sobota, 9 stycznia 2021
Kolokwium
Dzisiejszy dzień upływa pod znakiem kolokwium z Javy. Cały rocznik podekscytowany albo przerażony tym, że nasz surowy wykładowca będzie oceniał stan wiedzy. Wszystko odbyło się o godzinie 8 rano i – zgodnie z zapowiedziami – bez litości. Czasu na napisanie testu niewiele, aby utrudnić ściąganie i konsultacje, a zadania odpowiednio zakręcone z różnymi haczykami. Myślę, że poszło mi całkiem nieźle, chociaż wiele zależy od tego jak czepliwa będzie osoba sprawdzająca.
Zupełnie gdzieś obok tematu kolokwium rozpocząłem pracę, czy może współpracę nad jednym studenckim projektem, który ma za zadanie wyświetlać komunikaty w konsoli przez System.out.println w różnych kolorach. Jest trochę zabawy z regexami (przyda mi się powtórka) i gitem.
Z nowych rzeczy – nauczyłem się jak kolorować tekst w konsoli i jak ściągać pull requesty, żeby je testować i kompilować w Intelij. To ostatnie jest bardzo przydatne, bo code review "na wyczucie" może nie wychwycić błędów np. kompilacji.
Rzecz jest beznadziejnie prosta, trzeba zrobić:
git fetch origin pull/ID/head:NAZWAco utworzy branch o nazwie NAZWA. Następnie wchodzimy na niego checkoutem i odpalamy :)
Jeżeli nasze repo jest forkiem, to nie zobaczymy pull requesta, bo przecież on jest zgłoszony na masterze. W takim układzie zamiast origin wpisujemy adres głównego repo lub jest alias, stworzony za pomocą:
git remote add NAZWA_REPO LINKpiątek, 8 stycznia 2021
Z dyskusji o testach
Na uczelnianym Discordzie wynikła dyskusja o TDD i o różnych praktykach z tym związanych, wklejam poniżej swoją wypowiedź z krótkim przykładem jak w tym momencie rozumiem testowanie.
załóżmy prosty interfejs:
public interface Calculator {
int add(int a, int b);
}implementowany przez prostą klasę:
public class SimpleCalculatorImpl implements Calculator {
@Override
public int add(int a, int b) {
return a + b;
}
}
oraz napiszmy bardziej zaawansowaną implementację:
public class AdvancedCalculatorImpl implements Calculator {
private final CalculatorService calculatorService;
public AdvancedCalculatorImpl(CalculatorService calculatorService) {
this.calculatorService = calculatorService;
}
@Override
public int add(int a, int b) {
return calculatorService.add(a, b);
}
}
public class CalculatorService {
public int add(int a, int b) {
return a + b;
}
}Jak to przetestować? Generalnie wszystkie klasy implementujące Calculator mają tylko jedno zadanie biznesowe – przyjąć dwie zmienne w metodzie add i zwrócić odpowiedni wynik. Więc to są kluczowe testy dla TDD i dla utrzymania projektu, które pozwolą na pisanie zupełnie nowych implementacji w przyszłości.
Oprócz tego każda klasa ma swoje własne szczegóły, które można testować, np. AdvancedCalculatorImpl bazuje na właściwym działaniu CalculatorService, więc testy powinny zabezpieczać ją przed "zepsuciem" lub niepoprawnym działaniem tej zależności.
Z perspektywy teoretycznej (bo nie mam doświadczenia w open source) to testy każdej z tych klas są i mogą być pożyteczne, bo przecież ulepszenie projektu możesz nastąpić przez napisanie nowej implementacji Calculatora, ale może wystarczy tylko zoptymalizować już istniejącą lub jej zależność.
czwartek, 7 stycznia 2021
Nauka programowania na studiach
Mam taki przedmiot na studiach, nazywa się Podstawy programowania w Javie i jest on skierowany do osób rozpoczynających przygodę z Javą. Przynajmniej w teorii, bo na wykładach omawiane są dość dokładnie podstawowe operacje i konstrukcje, przy czym dobór przykładów i zadań jest dość zawiły.
Obserwując reakcje koleżanek i kolegów z roku, którzy faktycznie uczą się programowania z naszych wykładów, to mogę powiedzieć, że nasze materiały są przeładowane teorią, a przykłady kodu prawdopodobnie nieumyślnie są zawiłe. O ile mogę porównać tok nauczania z Coderslab, to tam zdecydowanie przykładali się do tego, byśmy jak najszybciej pojęli podstawy programowania – tutaj nie jest to priorytetem.
Co mam na myśli? Pojęcie metody zostało zdefiniowane po 6-8 godzinach wykładów, do tego czasu cały kod wielu zadań po prostu pisaliśmy w mainie. Nazwy zmiennych, metod są przypadkowe i mało mówiące, stosowane wcięcia bez klamerek przy jednolinijkowych ifach powodują zamieszanie wśród początkujących (co do tego gdzie się kończy blok warunkowy). Typowe nazwy zmiennych to takie koszmarki jak: tmp, array1, arr3, a szczytem wszystkiego było: boolean notSame = false.
Nasze zadania, poza takimi typowymi programistycznymi wyzwaniami, zawierają często sporą dawkę teorii, której zazwyczaj nie ma na kursach, jakie znajdziecie na YT. Operacje bitowe mamy przerobione dość szczegółowo (za to akurat jestem wdzięczny, bo chyba nikt inny nie przykłada do nich zbyt wielkiej wagi).
Wiesz co to konstruktor? To napisz klasę KulaNa, która w swoim konstruktorze przyjmuje obiekt Walec o wysokości h i promieniu podstawy r, i na podstawie przekazanego argumentu ustali promień kuli w taki sposób, aby była opisana na dostarczonym walcu.
Zadanie być może fajne, bo problem złożony i wymaga googlania albo trochę matematycznej wiedzy, ale dla początkujących, którzy po raz pierwszy piszą konstruktory z obiektami, to może być trochę za dużo. Bo zamiast ćwiczyć Javę spędzamy czas szukając wzorów na bryły przestrzenne.
środa, 6 stycznia 2021
Po czym poznać, że projekt się kończy?
Myślę, że dopiszę jeszcze niewielkie zakończenie tego projektu, bo brakuje nam pewnej klamry, aby zamknąć duży etap aplikacji i... cóż, projekt był planowany jako grupowy, więc raczej nie będę pisał go dalej sam. Będzie kejs do portfolio na przyszłość i nauka, wspomnienie miłej pracy i narad na Google Meets.
Czy projekt wróci do życia? Za miesiąc, rok? No to już nie ode mnie zależy :) Chociaż pewnie łatwiej by go było pisać znów od zera :D
poniedziałek, 4 stycznia 2021
ArgumentCaptor to the rescue
Wczoraj po raz pierwszy użyłem klasy ArgumentCaptor z pakietu Mockito i jestem pod wielkim wrażeniem jak prosto i elegancko jest to napisane.
Zadanie jest następujące – testujemy klasę implementującą interface Evaluator, która posiada metodę setFitness(Gene).
setFitness odczytuje wartość pola Gene.value, jest to wartość liczbowa zakodowana jako char. Następnie porównujemy ją z analogiczną wartością, zakodowaną w klasie Evaluator i po zastosowaniu odpowiedniego algorytmu otrzymujemy liczbę float w przedziale 0-1, opisującą trafność dopasowania. Im bliżej 1, tym dopasowanie jest dokładniejsze; w przypadku pełnej zgodności otrzymujemy wynik 1.
Następnie nasze dopasowanie zapisujemy za pomocą metody Gene.setFitness, zmieniając stan obiektu. Ponieważ testujemy klasę Evaluator, Gene będzie mockowany. I tutaj pojawia się problem – zmieniamy stan mocka, który nie jest zapamiętywany – w końcu mock podaje nam wszystko, co chcemy, dzięki komendzie:
when(gene.getValue()).thenReturn(evaluatedValue);
I tutaj pojawia się ArgumentCaptor, który przechwyci jaką wartość podajemy mockowi. Deklarujemy go jako pole klasy testowej:ArgumentCaptor<Float> floatArgumentCaptor = ArgumentCaptor.forClass(Float.class);
Przy deklaracji określamy jaki typ zmiennej będzie przechwytywany. W tym przypadku chcę złapać prymitywny typ float, więc używam odpowiedniego Wrappera.
verify(gene).setFitness(floatArgumentCaptor.capture());
W końcu po wywołaniu metody na mojej klasie Evaluator dodaję powyższą linię – verify obserwuje zachowanie mocka i kiedy odwołamy się do jego settera, floatArgumentCaptor.capture() wykona swoją robotę.
Kolejność wywołania jest ważna – najpierw wykonujemy operację na mocku, a później używamy verify, w innym przypadku metoda nie zadziała poprawnie. Nie do końca rozumiem dlaczego, ale przetestowałem, że to jest właściwy sposób używania.Teraz floatArgumentCaptor posiada zapisaną wartość float, którą mogę wrzucić do asercji za pomocą floatArgumentCaptor.getValue() i sprawdzić czy została wyliczona zgodnie z założeniami.
sobota, 2 stycznia 2021
Pisanie kodu codziennie
Tempo mojej nauki znacznie przyspieszyło, kiedy postanowiłem, że codziennie będę pisał kod. Oczywiście nie zawsze się to udaje z przyczyn obiektywnych, ale samo przełożenie liczby godzin poświęconych na kodowanie sprawia, że zwiększam swoje wyczucie.
Jednym z kłopotów, jaki mam teraz, jest to, że zdarza się, że nie doprowadzam projektów do końca. Bo też nie jest jasne jak ten satysfakcjonujący koniec ma wyglądać – niezależnie od tego czy robię projekt z tutoriali czy jakiś zupełnie własny, to zawsze jest coś co można/należałoby dodać, zmienić, przepisać.
Nie martwię się tym jakoś bardzo i staram się utrzymywać 2-3 aktywne projekty na bieżąco, czasem wygrzebuję coś starego, nad czym pracowałem wcześniej, i wprowadzam pewne zmiany.
Odwiedzanie starych projektów jest też o tyle fajne, że widzę postęp i zawsze jest coś co można poprawić, zrobić inaczej.
Server socket all over place
Kilka godzin tutoriali YT później i różnych prób, w końcu udało się zachęcić Server do współpracy :) Nie mogę powiedzieć, że to przełom, bo...
-
Wracam do zadań z codewars i przypominam sobie pisanie Javy. To zupełnie inne wrażenie niż kodowanie w JS, ale bardzo przyjemne i chciałem ...
-
Pisanie programu to ciągła karuzela pomiędzy odczuciami geniuszu (działa!) a beznadziei (znów nie działa, trzeba szukać co się wywaliło, ni...
-
Pierwszy egzamin z podstaw programowania był naprawdę trudny, pełny zadań związanych z analizą kodu i rozwiązywania wyrażeń typu 'a...
