Ocena wypożyczalni samochodów

0

Cześć. Przychodzę do Was z prośba o sprawdzenie wypożyczalni samochodów.
Jest to mój pierwszy projekt w którym użyłem mavena, testów jednostkowych, mockito czy slf4j.

Uprzedzam pytania, slf4j użyłem z tego względu, iż czytałem w necie, że lepiej jest tego użyć aniżeli sys.out.
Jeżeli chodzi o Client/Worker DataGetter, nie ma tam jakis ifów, jeżeli ktoś by wpisał litery zamiast cyfr w peselu. Wiadomo, program się wysypie, ale nie skupiałem się zbytnio nad tym, by pobieranie danych było idealne, a na ogólnym ćwiczeniu programowaina.

Chciałbym się takżę Was poradzić, co powinienem dalej zrobić? Jakiś nowy projekt, moze o czyms poczytać.

Dzięki wielkie za każdą odpowiedź.
Link do githuba: https://github.com/must1/CarRental

4
  1. https://github.com/must1/CarRental/blob/master/src/test/java/car/model/CarTest.java - ten test nie pokrywa absolutnie nic - równie dobrze mógłbyś tam wszędzie mieć assertEquals(1, 1); z reguły nie pisze się testów do modeli, ponieważ powinno się testować zachowanie (a modele zachowania na ogół nie mają, gdyż są tylko pojemnikiem na dane).

  2. https://github.com/must1/CarRental/blob/master/src/test/java/car/model/ClientTest.java - jak wyżej.

  3. https://github.com/must1/CarRental/blob/master/src/test/java/car/rental/data/getters/ClientDataGetterTest.java - ten test za to jest już jak najbardziej poprawny, ponieważ sprawdza zachowanie klasy ClientDataGetter (tzn. czy poprawnie wczytuje dane oraz czy poprawnie generuje obiekt) :-)

  4. when(carRentalStorageMock.getAllCustomers()).thenReturn(listOfClients); - czy na pewno musisz tutaj wykorzystywać mocki?

  5. https://github.com/must1/CarRental/blob/master/src/main/java/car/rental/DB/CarRentalSQLDatabase.java - god object, brak separation of concerns (metody zarówno operują na bazie, jak i interfejsie użytkownika - System.out.println).

  6. https://github.com/must1/CarRental/blob/master/src/main/java/car/rental/model/RentingACar.java - modele powinny być nazwane z wykorzystaniem rzeczownków; RentingACar nim nie jest. Może CarRental?

Od siebie mogę polecić poczytanie o wzorcu repository - sprawdzi się tutaj idealnie.


Chciałbym się takżę Was poradzić, co powinienem dalej zrobić?

Sam sobie odpowiedziałeś:

[...] nie skupiałem się zbytnio nad tym, by pobieranie danych było idealne, a na ogólnym ćwiczeniu programowaina.

Zaimplementuj tę brakującą walidację ;-)

0
Patryk27 napisał(a):
  1. https://github.com/must1/CarRental/blob/master/src/test/java/car/model/CarTest.java - ten test nie pokrywa absolutnie nic - równie dobrze mógłbyś tam wszędzie mieć assertEquals(1, 1); z reguły nie pisze się testów do modeli, ponieważ powinno się testować zachowanie (a modele zachowania na ogół nie mają, gdyż są tylko pojemnikiem na dane).

Będzie usunięte

  1. https://github.com/must1/CarRental/blob/master/src/test/java/car/model/ClientTest.java - jak wyżej.

To też

  1. when(carRentalStorageMock.getAllCustomers()).thenReturn(listOfClients); - czy na pewno musisz tutaj wykorzystywać mocki?

Wydaje mi się, ze tak. Szczerze powiedziawszy nie widzę innego rozwiązania :D

  1. https://github.com/must1/CarRental/blob/master/src/main/java/car/rental/DB/CarRentalSQLDatabase.java - god object, brak separation of concerns (metody zarówno operują na bazie, jak i interfejsie użytkownika - System.out.println).

Tutaj sys.out zamienie na loggery, bo widzę ze zapomniałem ale nadal będzie się to pokrywało z interfejsem. Jest ich mało, bo może z 2-3 ale faktycznie są. Może jakaś obsługa błędów w innej klasie?

  1. https://github.com/must1/CarRental/blob/master/src/main/java/car/rental/model/RentingACar.java - modele powinny być nazwane z wykorzystaniem rzeczownków; RentingACar nim nie jest. Może CarRental?

Zamienię nazwę.

Od siebie mogę polecić poczytanie o wzorcu repository - sprawdzi się tutaj idealnie.

Poczytam na pewno!


Chciałbym się takżę Was poradzić, co powinienem dalej zrobić?

Sam sobie odpowiedziałeś:

[...] nie skupiałem się zbytnio nad tym, by pobieranie danych było idealne, a na ogólnym ćwiczeniu programowaina.

Zaimplementuj tę brakującą walidację ;-)

Tak, ale to jest chwila moment, dodanie kilku ifów ew. jakieś wyjątki. Ale co potem?

1

Tutaj sys.out zamienie na loggery, [...]

Nie, nie - całkowicie nie o to chodzi czy masz tam System.out.println, czy sys.out.

Masz klasę, która ma ogrom odpowiedzialności - poczytaj o wzorcu repozytorium i zrefaktoryzuj ją, pamiętając o tym, że interfejs użytkownika a dostęp do danych to dwie odrębne rzeczy, więc powinny być też rozdzielone między odrębne klasy.

Ale co potem?

Powoli - najpierw wprowadź poprawki, dodaj walidację i pogadamy ;-)

0

Update poleciał na repozytorium. Zrobiłem wszystko prócz tego repository pattern. Ciężko mi strasznie ogarnąć jak to dokładnie działa, jakies business layer etc... Poszukam na pewno jeszcze info o tym na innych stronach, ale na pewno dłużej zejdzie o ile to zrobie.

0

Szczerze powiedziawszy nie wiem co tu mogę zmienić jeszcze oprócz tego wypisywania jakiś informacji. Interfejs jest, metody implementujące ten interfejs są.
Jeżeli chodzi o to wypisywanie, to nie wiem czy zrobić jakąś klase tak jak piszesz. Coś na zasadzie tego:
Jeżeli mam metode taką:

@Override
    public void makeCarAvailable(Car car) throws SQLException {
       ...

        if (isAvailable) {
            preparedStatement = connection.prepareStatement("update car " + " set available='1'" + " where brand=? AND productionYear=?");
            preparedStatement.setString(1, car.getBrand());
            preparedStatement.setString(2, car.getProductionYear());
            preparedStatement.executeUpdate();
            System.out.println(car.getBrand() + " was made unavailable");
        } else {
            logger.info("No " + car.getBrand() + " in system!");
        }
    }

To czy to wyświetlanie zrobić na zasadzie:

metoda()
{
...
metoda_wyswietlajaca_komunikat();
}

A w tej metodzie własnie to: logger.info("No " + car.getBrand() + " in system!");

2

Twoja metoda nadal robi dwie rzeczy:

  1. Zmienia coś w bazie.
  2. Informuje użytkownika o tej zmianie.

Problem w tym podejściu jest taki, że wymusza ona pewne zachowanie, którego nie da się obejść [1] - gdybyś chciał np. dodać do swojej aplikacji GUI, nie mógłbyś tej metody wykorzystać.

A tak wcale być nie musi:

public boolean makeCarAvailable(Car car) throws SQLException {
    /* ... */
 
    if (!isAvailable) {
        return false;
    }

    preparedStatement = connection.prepareStatement("update car " + " set available='1'" + " where brand=? AND productionYear=?");
    preparedStatement.setString(1, car.getBrand());
    preparedStatement.setString(2, car.getProductionYear());
    preparedStatement.executeUpdate();

    return true;
}

/* ... gdzieś dalej w kodzie ... */

if (obj.makeCarAvailable(car)) {
    System.out.println("Udało się.");
} else {
    System.out.println("Nie udało się.");
}

Zauważ, że w tym konkretnym wypadku efekt (z punktu widzenia użytkownika) będzie identyczny, lecz w kodzie zaszła ogromna zmiana: część odpowiedzialna za bazę danych nie wie już nic o interfejsie użytkownika (nie ma żadnego System.out.println czy dowolnej innej formy komunikacji) - moglibyśmy zamiast TUI mieć GUI, i tej metodzie to zwisa ;-)


[1] fachowo chodzi tutaj o separation of concerns - dostęp do bazy i komunikacja z użytkownikiem to dwie niepowiązane wprost ze sobą czynności, stąd powinny być rozdzielone również w kodzie.

0

Dobra udało mi się to zrobić. Repozytorium zaktualizowane.

0

Co powinienem robić dalej i czy coś ewentualnie jeszcze poprawić?

3

Jest coraz ładniej! :-)

  1. https://github.com/must1/CarRental/blob/master/src/main/java/car/rental/DB/CarRentalStorage.java - ten interfejs zarządza wieloma rodzajami obiektów (samochodami, klientami i wypożyczeniami), przez co ma bardzo szeroką odpowiedzialność. Wygodniej będzie wydzielić trzy odrębne interfejsy: CarStorageInterface, ClientStorageInterface oraz RentalStorageInterface. Klasy implementujące te interfejsy mogą być nazwane np. MySQLCarStorage, MySQLClientStorage itd.

  2. Zdecyduj się, czy chcesz wykorzystywać rzeczownik client czy customer ;-)

  3. Za co odpowiedzialne są klasy znajdujące się w tej - https://github.com/must1/CarRental/tree/master/src/main/java/car/rental/activities - przestrzeni?

  4. https://github.com/must1/CarRental/blob/master/src/main/java/car/rental/CarRentalEngine.java#L126 - troszeczkę długa ta linijka; dlaczego nie porozdzielasz tego na osobne wywołania logger.info?

  5. Dlaczego np. ta - https://github.com/must1/CarRental/blob/master/src/main/java/car/rental/DB/CarRentalSQLDatabase.java#L138 - metoda ma w sobie System.out.println? Dostęp do bazy miał być całkowicie uniezależniony od interfejsu użytkownika.

  6. https://github.com/must1/CarRental/blob/master/src/main/java/car/rental/DB/CarRentalSQLDatabase.java#L117 - ta metoda jest trochę długa. Co powiesz o takim podejściu?

  7. Dlaczego statement, preparedStatement oraz result są polami klasy, a nie zmiennymi lokalnymi?

  8. Wszędzie posługujesz się warunkiem WHERE brand=? AND productionYear=? - dlaczego nie utworzysz po prostu pola id?

  9. https://github.com/must1/CarRental/blob/master/src/main/java/car/rental/DB/CarRentalSQLDatabase.java#L172 - listOfClients brzmi brzydko i długo; raczej pisze się po prostu clients - z racji wykorzystania rzeczownika w liczbie mnogiej wiadomo, że to lista czy tablica.

  10. https://github.com/must1/CarRental/blob/master/src/main/java/car/rental/DB/CarRentalSQLDatabase.java#L195 - ta metoda jest nazwana getRentedCars, co sugeruje, że zwraca listę samochodów, co nie jest prawdą. Może getClientRentals?

  11. https://github.com/must1/CarRental/blob/master/src/test/java/car/rental/data/getters/InputTakerTest.java#L35 - straaaaaaasznie długa nazwa; pomyśl nad czymś krótszym.

  12. Twoje testy w klasie InputTakerTest nie pokrywają przypadków skrajnych:
    a) co się stanie w momencie, gdy odpalę takeStringInput na pustym scannerze?
    b) co się stanie w momencie, gdy odpalę takeIntInput / takeLongInput z ujemną liczbą w buforze?
    c) co się stanie w momencie, gdy odpalę takeIntInput / takeLongInput z niepoprawną liczbą w buforze? (np. "asdf")

  13. Czym rózni się https://github.com/must1/CarRental/blob/master/src/test/java/car/rental/data/getters/InputTakerTest.java od https://github.com/must1/CarRental/blob/master/src/test/java/car/rental/data/getters/InputTakersTest.java?

  14. Wszystko, oprócz ostatnich trzech metod w https://github.com/must1/CarRental/blob/master/src/test/java/car/rental/CarRentalOptionsTest.java jest zbędne, ponieważ tak właściwie nic one nie sprawdzają. Co takiego pokrywa Ci ten test? Jeśli odpalę metodę X, to uruchomi się metoda X - to nic nie znaczy. Lub - w inną stronę - podaj mi przypadek skrajny, który sprawi, że ten test się nie powiedzie. Jeśli taki nie istnieje, test jest zbędny (ponieważ nic nie pokrywa). Lub - jeszcze inaczej: jeśli jesteś w stanie napisać test w ogóle nie myśląc, tylko robiąc kopiuj-wklej i zmieniając nazwę metody, taki test jest zbędny ;-)

  15. https://github.com/must1/CarRental/blob/master/src/main/java/car/rental/user/communication/DataBaseCommunication.java#L14 - ta klasa wcale nie potrzebuje konkretnie CarRentalSQLDatabase - wystarczy CarRentalStorage. Dzięki temu mógłbyś podmienić implementację np. z bazy danych na pliki, i nic w tej klasie nie będzie wymagało zmiany - na tym polega cała zaleta wykorzystywania interfejsów.

  16. https://github.com/must1/CarRental/blob/master/src/main/java/car/rental/CarRentalOptions.java - jaka jest w zasadzie odpowiedzialność tej klasy i po co ona istnieje?

1
Patryk27 napisał(a):

Jest coraz ładniej! :-)

  1. https://github.com/must1/CarRental/blob/master/src/main/java/car/rental/DB/CarRentalStorage.java - ten interfejs zarządza wieloma rodzajami obiektów (samochodami, klientami i wypożyczeniami), przez co ma bardzo szeroką odpowiedzialność. Wygodniej będzie wydzielić trzy odrębne interfejsy: CarStorageInterface, ClientStorageInterface oraz RentalStorageInterface. Klasy implementujące te interfejsy mogą być nazwane np. MySQLCarStorage,
    MySQLClientStorage itd.

Dodałem 3 interfejsy i 3 klasy implementujące je.
Przeprowadziłem zmiany w innych klasach, tak aby wszystko działało

  1. Zdecyduj się, czy chcesz wykorzystywać rzeczownik client czy customer ;-)

Zostało samo client :D

  1. Za co odpowiedzialne są klasy znajdujące się w tej - https://github.com/must1/CarRental/tree/master/src/main/java/car/rental/activities - przestrzeni?

Klasy służy temu, abyw klasie CarRentalEngine nie było tzw. "magic numbers". Na przykład:

private void executeOptionsForClient(int option) throws SQLException {
        switch (option) {
            case ClientActivities.RENT_CAR:
                CarRental carRental = clientDataGetter.rentACar(input);
                carRentalOptions.rentACar(carRental);
                dataBaseCommunication.executeCarRentalMessage(carRental);
                break;
            case ClientActivities.RETURN_CAR:
                car = clientDataGetter.returnACar(input);
                carRentalOptions.returnACar(car);
                dataBaseCommunication.executeReturnCarMessage(car);
                break;
....
  1. https://github.com/must1/CarRental/blob/master/src/main/java/car/rental/CarRentalEngine.java#L126 - troszeczkę długa ta linijka; dlaczego nie porozdzielasz tego na osobne wywołania logger.info?

Rozdzielone.

  1. Dlaczego np. ta - https://github.com/must1/CarRental/blob/master/src/main/java/car/rental/DB/CarRentalSQLDatabase.java#L138 - metoda ma w sobie System.out.println? Dostęp do bazy miał być całkowicie uniezależniony od interfejsu użytkownika.

Nie zauważyłem tego. Zmienione.

  1. https://github.com/must1/CarRental/blob/master/src/main/java/car/rental/DB/CarRentalSQLDatabase.java#L117 - ta metoda jest trochę długa. Co powiesz o takim podejściu?

Zmienione. Aczkolwiek zastanawia mnie po co jest to this..

  1. Dlaczego statement, preparedStatement oraz result są polami klasy, a nie zmiennymi lokalnymi?

Nie są lokalnymi z tego względu, że w niektórych przypadkach są używane w wielu metodach przez co wydawało mi się bez sensu tworzyć w każdej metodzie tego samego jak można raz to zrobić.

  1. Wszędzie posługujesz się warunkiem WHERE brand=? AND productionYear=? - dlaczego nie utworzysz po prostu pola id?

Tutaj nie rozumiem, jakie id?

  1. https://github.com/must1/CarRental/blob/master/src/main/java/car/rental/DB/CarRentalSQLDatabase.java#L172 - listOfClients brzmi brzydko i długo; raczej pisze się po prostu clients - z racji wykorzystania rzeczownika w liczbie mnogiej wiadomo, że to lista czy tablica.

Zmienione. Podobne nazwy także zostały zmienione na rentalCars czy cars.

  1. https://github.com/must1/CarRental/blob/master/src/main/java/car/rental/DB/CarRentalSQLDatabase.java#L195 - ta metoda jest nazwana getRentedCars, co sugeruje, że zwraca listę samochodów, co nie jest prawdą. Może getClientRentals?

Zmienione.

  1. https://github.com/must1/CarRental/blob/master/src/test/java/car/rental/data/getters/InputTakerTest.java#L35 - straaaaaaasznie długa nazwa; pomyśl nad czymś krótszym.

Zmieniłem na krótszą, w sumie nie wiem czy dobra. Np. shouldReturnFirstValidIntegerInput.

  1. Twoje testy w klasie InputTakerTest nie pokrywają przypadków skrajnych:
    a) co się stanie w momencie, gdy odpalę takeStringInput na pustym scannerze?
    b) co się stanie w momencie, gdy odpalę takeIntInput / takeLongInput z ujemną liczbą w buforze?
    c) co się stanie w momencie, gdy odpalę takeIntInput / takeLongInput z niepoprawną liczbą w buforze? (np. "asdf")

a) Tutaj nie wiem, czy chodzi Ci o coś takiego? Scanner scanner = new Scanner(""); ?
b) Dodałem warunek w InputTaker, który eliminuje warunek ujemnych liczb. W metodach, które już wyłuskiwały błędny input dodałem ujemne liczby. Np tutaj: https://github.com/must1/CarRental/blob/master/src/test/java/car/rental/data/getters/InputTakerTest.java#L36
c) A nie jest to już zrobione? Np. właśnie w tym linku co podałem. Jest tam napisane: "Test%n-33%n42%n43%n" czyli jest tam taki String jak Test.

  1. Czym rózni się https://github.com/must1/CarRental/blob/master/src/test/java/car/rental/data/getters/InputTakerTest.java od https://github.com/must1/CarRental/blob/master/src/test/java/car/rental/data/getters/InputTakersTest.java?

Usunięte.

  1. Wszystko, oprócz ostatnich trzech metod w https://github.com/must1/CarRental/blob/master/src/test/java/car/rental/CarRentalOptionsTest.java jest zbędne, ponieważ tak właściwie nic one nie sprawdzają. Co takiego pokrywa Ci ten test? Jeśli odpalę metodę X, to uruchomi się metoda X - to nic nie znaczy. Lub - w inną stronę - podaj mi przypadek skrajny, który sprawi, że ten test się nie powiedzie. Jeśli taki nie istnieje, test jest zbędny (ponieważ nic nie pokrywa). Lub - jeszcze inaczej: jeśli jesteś w stanie napisać test w ogóle nie myśląc, tylko robiąc kopiuj-wklej i zmieniając nazwę metody, taki test jest zbędny ;-)

Usunąłem niepotrzebne testy.

  1. https://github.com/must1/CarRental/blob/master/src/main/java/car/rental/user/communication/DataBaseCommunication.java#L14 - ta klasa wcale nie potrzebuje konkretnie CarRentalSQLDatabase - wystarczy CarRentalStorage. Dzięki temu mógłbyś podmienić implementację np. z bazy danych na pliki, i nic w tej klasie nie będzie wymagało zmiany - na tym polega cała zaleta wykorzystywania interfejsów.

Chodziło Ci o coś takiego?
https://github.com/must1/CarRental/blob/master/src/main/java/car/rental/user/communication/DataBaseCommunication.java#L20

  1. https://github.com/must1/CarRental/blob/master/src/main/java/car/rental/CarRentalOptions.java - jaka jest w zasadzie odpowiedzialność tej klasy i po co ona istnieje?

Szczerze powiedziawszy skupia ona wszystkie możliwości jakie posiada ten program. W wyniku czego łatwiej mi jest się posługiwać metodami w CarRentalEngine. Wydaje mi się, że wygląda to estetyczniej :D

Jeżeli miałbym usunąć tę klasę, to gdzieś musiałbym przenieść te 3 metody:
https://github.com/must1/CarRental/blob/master/src/main/java/car/rental/CarRentalOptions.java#L46
https://github.com/must1/CarRental/blob/master/src/main/java/car/rental/CarRentalOptions.java#L56
https://github.com/must1/CarRental/blob/master/src/main/java/car/rental/CarRentalOptions.java#L66

Do klasy silnikowej na pewno nie pasują.

3
  1. Dodałem 3 interfejsy i 3 klasy implementujące je. Przeprowadziłem zmiany w innych klasach, tak aby wszystko działało

Brawo! W ten sposób zaimplementowałeś właśnie wzorzec repozytorium - tak jak polecałem na początku ;-)
Możesz teraz dumnie zmienić nazwę Storage na Repository, a przestrzeń nazw DB na Repositories.

Btw, nie ma sensu trzymać interfejsów w dodatkowej przestrzeni CarRentalInterfaces - mogą być wszystkie obok siebie.

  1. Klasy służy temu, abyw klasie CarRentalEngine nie było tzw. "magic numbers".

Nie są to "magic numbers", jeśli kilka linijek wyżej wprost pytasz użytkownika Wpisz 1, aby uruchomić ....

  1. Aczkolwiek zastanawia mnie po co jest to this.

Faktycznie - teraz zauważyłem, że Ty akurat nie wykorzystujesz this. - jeden rabin powie tak, inny powie inaczej.
W tym wypadku rzeczywiście, trzymając się konwencji, powinno się obejść bez tego.

  1. Nie są lokalnymi z tego względu, że w niektórych przypadkach są używane w wielu metodach przez co wydawało mi się bez sensu tworzyć w każdej metodzie tego samego jak można raz to zrobić.

Nah, raczej takiego patentu się nie wykorzystuje:

  1. Sprawiasz, że Twoja klasa przestaje być thread-safe (jeden wątek może nadpisać zapytanie drugiemu - gdybyś korzystał ze zmiennych lokalnych, nie byłoby tego problemu),
  2. Zyskujesz kilka bajtów, w zamian utrudniając JVM analizę i optymalizację swojego kodu - przez to, że zapisujesz obiekt zapytania do pola klasy (a nie zmiennej lokalnej), garbage collector nie może się tego zapytania pozbyć (usunąć z pamięci) do momentu, do którego go nie nadpiszesz następnym (co w przypadku tak małej aplikacji nie stanowi żadnego problemu, lecz mogłoby zacząć w przypadku większych).
  3. Zyskujesz kilka bajtów, w zamian utrudniając innym ludziom analizę swojego kodu - na samym początku nie zauważyłem wcale, że jest to pole klasy i tak przyglądałem się temu kodowi myśląc od kiedy w javie można pominąć deklarację zmiennej.
  1. Tutaj nie rozumiem, jakie id?

Poczytaj o podstawach projektowania baz danych, ze szczególnym zwróceniem uwagi na to, czym różni się klucz podstawowy (primary key) od klucza złożonego (composite key) oraz czym różni się klucz naturalny (natural key) od klucza sztucznego (surrogate key). Nie jest to czarna magia, a znacznie uprości też kod w paru miejscach.

Ad 12: testy wydają się teraz spoko :-)

  1. Chodziło Ci o coś takiego?

Prawie - zauważ, że Twoje klasy DataBaseCommunication i CarRentalOptions są zależne od konkretnej implementacji: wymagają ona podania jej repozytoriów MySQLowych i nawet gdybyś miał zaimplementowane inne, Twoje klasy tego nie przyjmą.

Porównaj to z takim podejściem: https://4programmers.net/Pastebin/9356

Mógłbyś mieć np. MySQLCarStorage, FileCarStorage, InMemoryCarStorage i nieskończenie wiele innych... i dla każdej możliwej implementacji Twoja klasa będzie działać bez jakiejkolwiek zmiany! :-)

Fachowo nazywa się to dependency inversion principle - też polecam rzucić okiem w wolnej chwili.

Nie musisz od razu rzucać się na głębokie morze i uczyć sposobu działania konkretnych implementacji DI w Javie - poczytaj i zrozum, na czym polega ten pattern, tyle wystarczy.

  1. Szczerze powiedziawszy skupia ona wszystkie możliwości jakie posiada ten program. W wyniku czego łatwiej mi jest się posługiwać metodami w CarRentalEngine. Wydaje mi się, że wygląda to estetyczniej.

Ok, czyli jest to realizacja wzorca fasada - fine with me ;-)

Jeżeli miałbym usunąć tę klasę, to gdzieś musiałbym przenieść te 3 metody:

Te metody za dużo nie robią - nie potrzeba tworzyć dla nich od razu odrębnej klasy.
Tym niemniej nie ma źle - IMO może zostać tak, jak jest.

0
Patryk27 napisał(a):
  1. Klasy służy temu, abyw klasie CarRentalEngine nie było tzw. "magic numbers".

Nie są to "magic numbers", jeśli kilka linijek wyżej wprost pytasz użytkownika Wpisz 1, aby uruchomić ....

Powinienem w takim razie to usunąć? Wg mnie wygląda to faktycznie przejrzyściej. Oczywiście nie wiem czy to nie jest zła praktyka.

  1. Nie są lokalnymi z tego względu, że w niektórych przypadkach są używane w wielu metodach przez co wydawało mi się bez sensu tworzyć w każdej metodzie tego samego jak można raz to zrobić.

Nah, raczej takiego patentu się nie wykorzystuje:

  1. Sprawiasz, że Twoja klasa przestaje być thread-safe (jeden wątek może nadpisać zapytanie drugiemu - gdybyś korzystał ze zmiennych lokalnych, nie byłoby tego problemu),
  2. Zyskujesz kilka bajtów, w zamian utrudniając JVM analizę i optymalizację swojego kodu - przez to, że zapisujesz obiekt zapytania do pola klasy (a nie zmiennej lokalnej), garbage collector nie może się tego zapytania pozbyć (usunąć z pamięci) do momentu, do którego go nie nadpiszesz następnym (co w przypadku tak małej aplikacji nie stanowi żadnego problemu, lecz mogłoby zacząć w przypadku większych).
  3. Zyskujesz kilka bajtów, w zamian utrudniając innym ludziom analizę swojego kodu - na samym początku nie zauważyłem wcale, że jest to pole klasy i tak przyglądałem się temu kodowi myśląc od kiedy w javie można pominąć deklarację zmiennej.

Okej, rozumiem. Pozmieniałem.

  1. Tutaj nie rozumiem, jakie id?

Poczytaj o podstawach projektowania baz danych, ze szczególnym zwróceniem uwagi na to, czym różni się klucz podstawowy (primary key) od klucza złożonego (composite key) oraz czym różni się klucz naturalny (natural key) od klucza sztucznego (surrogate key). Nie jest to czarna magia, a znacznie uprości też kod w paru miejscach.

Szczerze powiedziawszy dalej mam z tym problem. Czytam o tych kluczach. Raczej rozumiem cały zamysł tego, ale na ten moment nie zrealizuje tego. Na pewno jeszcze dziś o tym poczytam.

Prawie - zauważ, że Twoje klasy DataBaseCommunication i CarRentalOptions są zależne od konkretnej implementacji: wymagają ona podania jej repozytoriów MySQLowych i nawet gdybyś miał zaimplementowane inne, Twoje klasy tego nie przyjmą.

Porównaj to z takim podejściem: https://4programmers.net/Pastebin/9356

Tak zrobiłem.
Tutaj: https://github.com/must1/CarRental/blob/master/src/main/java/car/rental/CarRentalOptions.java#L17
i tutaj: https://github.com/must1/CarRental/blob/master/src/main/java/car/rental/user/communication/DataBaseCommunication.java

  1. Szczerze powiedziawszy skupia ona wszystkie możliwości jakie posiada ten program. W wyniku czego łatwiej mi jest się posługiwać metodami w CarRentalEngine. Wydaje mi się, że wygląda to estetyczniej.

Ok, czyli jest to realizacja wzorca fasada - fine with me ;-)

Jeżeli miałbym usunąć tę klasę, to gdzieś musiałbym przenieść te 3 metody:

Te metody za dużo nie robią - nie potrzeba tworzyć dla nich od razu odrębnej klasy.
Tym niemniej nie ma źle - IMO może zostać tak, jak jest.

Jednak jest z tą klasą problem, a może nie z tylko z nią ale także z DataBaseCommuncation. Z tego względu, że dwa razy wszystko wywołuje w CarRentalEngine przez co mam np. 2 te same samochody w bazie. Wynika to z tego, ze w warunku if'a w DataBaseCommuncation jest na nowo wywoływana metoda. Da się to jakoś obejść?

1
  1. Powinienem w takim razie to usunąć?

Tak, IMO najlepiej to usunąć.

Z tego względu, że dwa razy wszystko wywołuje [...] mam np. 2 te same samochody w bazie [...] Da się to jakoś obejść?

Em, nie wywołuj tych metod podwójnie? ;-)

0
Patryk27 napisał(a):
  1. Powinienem w takim razie to usunąć?

Tak, IMO najlepiej to usunąć.

Dobra to usunąłem.

Z tego względu, że dwa razy wszystko wywołuje [...] mam np. 2 te same samochody w bazie [...] Da się to jakoś obejść?

Em, nie wywołuj tych metod podwójnie? ;-)

Zgodze się :D Ale jak to zrobić, usunąć carRentalOptions?

Wywołanie wygląda u mnie tak:

 case ClientActivities.RENT_CAR:
                CarRental carRental = clientDataGetter.rentACar(input);
                carRentalOptions.rentACar(carRental); // tutaj raz, normalnie z klasy
                dataBaseCommunication.executeCarRentalMessage(carRental); // tutaj drugi raz, gdy sprawdza warunek if'a
1

Nie ma co się zapędzać w miliard klas - coś takiego jak najbardziej wystarczy:

case ClientActivities.RENT_CAR:
    CarRental carRental = clientDataGetter.rentCar(input); // może zmień nazwę metody na "readCarRental"?
  
    if (carRentalStorage.rentCar(carRental)) {
        System.out.println("yay!");
    } else {
        System.out.println("nay!");
    }

Jeszcze odnośnie nazewnictwa:

  1. Zwyczajowo w identyfikatorach pomija się przedimki - więc rentCar, a nie rentACar.
  2. Jeden katalog nazwałeś getters (liczba mnoga), lecz potem już repository (liczba pojedyncza) - zdecyduj się.
  3. Może InputTaker -> InputParser? Podobnie jak nie takeStringInput, tylko readString i już robi się przejrzyściej.
0

Okej, zrobiłem wszystko. Po usunięciu tego **CarRentalOptions **,**DataBaseCommuncation ** czy activites package jakoś puste dla mnie się to wszystko wydaję :D Ale stawiam, że tak to ma wyglądać.

Btw. Połowa z tych wszystkich punktów odnosi się do samych nazw, teraz widzę że nie jest to łatwy orzech.

///poleciał jeszcze jede update na engine

1 użytkowników online, w tym zalogowanych: 0, gości: 1