sobota, 30 stycznia 2021

Wprowadzenie do konteneryzacji

 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.

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

Planowałem pisać o czymś innym dzisiaj, ale tak się złożyło, że od samego rana koduję w projekcie implementację prostego algorytmu genetycznego. 

Ku mojemu nieszczęściu wysłałem commita na gałąź produkcyjną a nie na swojego brancha, co sprawia, że kod zmergował się bez PR i review. Szybko i sprawnie, ale nie o to chodzi w tym projekcie! Skoro mamy zespół to nie przemycamy swojego kodu bokiem. 

Wycofanie commita z produkcji było o wiele prostsze niż myślałem: 

git revert COMMIT
– wycofuje lokalnie wybrany commit. Później zmiany należy pushować na produkcję. 

Commit wycofany, szkody naprawione i co dalej? Tworzę swojego brancha, tak jak to powinno być zrobione i mogę wszczepić tutaj swojego commita:

git cherry-pick COMMIT 

 Jest to tak proste, że jestem autentycznie w szoku.

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:NAZWA

co 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 LINK


pią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?

Zacznę od oczywistości – każdy projekt żyje tyle, ile jest to konieczne dla osób, które go wspierają. W momencie, kiedy koszty, włożona energia nie dają odpowiedniego zwrotu, zaczyna się wycofanie. Jest to zrozumiałe i sam mam dziesiątki niedokończonych projektów. Do nielicznych wracam po czasie, co do części łudzę się, że je kiedyś przepiszę, natomiast większość spełniła już swoją funkcję szkoleniową i zostaje co najwyżej jako historia commitów.

W projekcie grupowym jest podobnie, ale trochę inaczej, bo każdy z członków grupy ma inne motywacje i poziom tolerancji, przekroczenie którego powoduje wycofanie z projektu. I właśnie jeden z pierwszych moich grupowych projektów dochodzi do takiego przełomu, gdzie członkowie zespołu mówią jasno, że kod jest zbyt złożony, problem abstrakcyjny i że już wystarczy.

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...