ś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?

Brak komentarzy:

Prześlij komentarz

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