Pomoc w zaprojektowaniu klas

0

Witam, zaczynam dopiero ze Springiem i chciałbym poprosić was o radę. W swojej aplikacji mam 3 modele: Day, Stage, Exercise. Day to jest dzień treningowy, który zawsze zawiera 3 etapy (Stage). Każdy z etapów może mieć dowolną liczbe ćwiczeń (Exercise), przyważnie byłoby to ok. 10 . Zastanawiam się w jaki sposób zaprojektować klasy i relacje. Z racji, że Day zawsze posiada odgórnie stworzone 3 etapy, które rzadko są aktualizowane, chciałem zrobić tutaj relację jeden do wielu (czyli w klasie Day jest zbiór etapów). Z racji, że ćwiczenia często będą tworzone i aktualizowane to tutaj chciałem zrobić realcje wiele do jednego, czyli klasa Stage nic nie wie o ćwiczeniach, za to każde z ćwiczeń zawiera id obiektu Stage. Wyglądało by to tak

public class Day {
    private String id;
    private Set<Stage> stages
}

public class Stage {
    private String id;
}

public class Exercise {
    private String id;
    private String stageId;
}

Czy moje myślenie jest dobre i jest to dobry sposób na zaprojektowanie tego? Problemem jednak jest to, że na frontendzie wszystkie rzeczy wyświetlam na jednym ekranie i tam potrzebowałbym za każdym razem ściągać jeden duży obiekt Day, który zawierałby tablicę etapów i każdy z etapów zawierałby tablicę ćwiczeń. W takim wypadku jest to jednak chyba nie możliwe. Co byście w takiej sytuacji doradzili? Czy może w każdej sytuacji zrobić relacje dwukieronkowe i nie przejmować się? Jest to zgodne z zasadami dobrych praktyk?

0

Skromnie okiem laika ja bym ze Stage zrobił enum i ładnie nazwał ( main, heavy, warmup, stretching, power lifting etc jak tam trzeba). Konkretny Stage bym dał do exercise, a do day set<exercise> zamiast Stage.

2

Jest kilka rzeczy. Po pierwsze nazewnictwo. Day, to raczej w kalendarzu. Lepiej Workout:

record Workout(UUID id, LocalDate date, Set<Stage> stages);

Ten Set to jest średni pomysł, bo trzeba by to jakoś walidować, ale sam Stage jest już dość prosty.

record Stage(UUID id, Set<Practice> stagePractices);

Dany etap ma pewien zestaw wykonanych ćwiczeń. On jest stały dla danego treningu i tutaj nie powinien się zmieniać. Odrobiłeś Matki Boskiej Klatkowskiej, to zapisujesz w danym treningu i tyle. Czym jest Practice? Ano będzie to instancja ćwiczenia:

record Practice(UUID id, Stage stage, Exercise template);

Ćwiczenia Exercise są szablonami dla danego, konkretnego, treningu. Szablon będzie mniej więcej taki:

record Exercise(UUID id, String description);

Ma to tę zaletę, że w Practice możesz zawrzeć wiadomości o konkretnej serii takie jak ilość powtórzeń, obciążenie, wykonane powtórzenia. Znowuż Exercise nie wie nic o konkretnym treningu.

0

Podchodzisz do tego od złej strony. Nie robi się tak, że nie wiesz jak coś zrobić, ale myślisz sobie "jakie by tu klasy dodać" i dodajesz je na pałę.

Powinieneś zaimplementować zachowanie programu imperatywnie, napisać testy, sprawdzić czy działa odpowiednio, i dopiero potem myśleć o refaktorowaniu tego na klasy. Nic dobrego z tego nie wyjdzie jak dodasz klasy tylko po to żeby były.

Michał Żłobiński napisał(a):

Problemem jednak jest to, że na frontendzie wszystkie rzeczy wyświetlam na jednym ekranie i tam potrzebowałbym za każdym razem ściągać jeden duży obiekt Day, który zawierałby tablicę etapów i każdy z etapów zawierałby tablicę ćwiczeń. W takim wypadku jest to jednak chyba nie możliwe. Co byście w takiej sytuacji doradzili? Czy może w każdej sytuacji zrobić relacje dwukieronkowe i nie przejmować się? Jest to zgodne z zasadami dobrych praktyk?

Jakkolwiek byś tego nie chciał zrobić, to zgodnie z najlepszymi praktykami frontend nie powinien nic o tym wiedzieć. Możesz sobie robić dowolne szalone struktury w aplikacji w springu, ale w momencie w którym wysyłasz je do klienta (czyli na front), to powinieneś je zaprezentować w uproszczonej, anemicznej formie, która nie ma nic co nie jest absolutnie niezbędne - np możesz je przemapować na płaską strukturę.

0

Wow, dzięki wielkie! Masz może jakiś dokładniejszy poradnik do takiego podejścia albo jakiś konkretny przykład jak to zrobić?

0
Michał Żłobiński napisał(a):

Wow, dzięki wielkie! Masz może jakiś dokładniejszy poradnik do takiego podejścia albo jakiś konkretny przykład jak to zrobić?

Czy słyszałeś o takim powiedzeniu "przerost formy nad treścią"?

U początkujących programistów to jest bardzo częsty problem, że w ich programach forma jest znacznie przerastająca nad treścią - dodawanie klas, bibliotek, kontrolerów, funkcji, generyków. Niektórzy czują że "powinni je dodać", ale nie do końca wiedzą czemu. Przy czym najważniejsza w programie jest treść - co ten program ma robić, dokładniej mówiąc - dla jakich danych wejściowych, jakie mają być dane wejściowe. To w jaki sposób program to wewnętrzne ogarnia (klasy, funkcje, struktury, czy cokolwiek innego) to szczegół implementacyjny.

Dla przykładu:

Napisałeś, że Twoja aplikacja ma takie wymagania:

Michał Żłobiński napisał(a):

W swojej aplikacji mam 3 modele: Day, Stage, Exercise. Day to jest dzień treningowy, który zawsze zawiera 3 etapy (Stage). Każdy z etapów może mieć dowolną liczbe ćwiczeń (Exercise), przyważnie byłoby to ok. 10

To jest dobry start, ale nie napisałeś co użytkownik aplikacji może z nimi robić? Może je tylko wyświetlić? Czy możę je edytować, a jeśli może to w jaki sposób (tzn. które elementy są dozwolone do edycji a które nie)? Czy może dodawać swoje dni lub ćwiczenia? Czy może je usuwać, albo dzielić się nimi z innymi użytkownikami? Czy jeśli je doda, to czy mają być zapisane, czy po zamknięciu aplikacji mają zniknąć?

Powinieneś zacząć od sprecyzowania co dokładnie użytkownik może a czego nie może zrobić w Twojej aplikacji. Dopiero potem jest czas na implementację.

(Przy czym zaznaczam że pisząc "użytkownik" nie mam na myśli użytkownika z loginem i hasłem który się rejestruje. Pisząc użytkownik mam na myśli kogoś kto włączył Twoją aplikację i jej używa. Jeśli korzystasz z programu który nie wymaga rejestracji, to też jesteś jego użytkownikiem).

0
Michał Żłobiński napisał(a):

Wow, dzięki wielkie! Masz może jakiś dokładniejszy poradnik do takiego podejścia albo jakiś konkretny przykład jak to zrobić?

Każdy przykład normalnego zdrowego projektowania w Javie, a Springa zostaw na później po silnym ugruntowaniu core..

Wydajność to swoją drogą. Na poziomie Javy interesuje cię model. Pod spodem nadal jest to takie samo zapytanie, a hibernate odpowiednio po podstawia obiekty. — Koziołek dziś, 10:31

Nie od rzeczy będzie wyraźnie powiedzieć, o jakich klasach mowa.
Encje (klasy) JPA to jedna broszka, encje biznesowe to inna
Kol @Michał Żłobiński: jakby oglądał serial od siódmego odcinka.

@Koziołek:
Twój przykład idzie przez record czyli immutable. Nie żebym miał z tego jakis zarzut, ale zależy od kontekstu.

0

Jedna mała uwaga (może się przyda). Ze Stage zrób osobna tabele dopiero wtedy jak dane ćwiczenie będzie mogło występować w różnych Stage. No i zacznij od wypisania functional requirements aby nakreślić API. Idąc dalej pewnie twój WorkoutDay jako dzień tygodnia jest albo 3, 4, 5, 6 dniowy ( Arnold i spółka to mają chyba nawet 7/7), więc mógłby być parametryczny. I to właśnie ten typ ileś tam dniowy na pewno by się powtarzał( Zakładajac że takie samo ćwiczenie może występować w różnym schemacie - interfejsie dla 3,4... dniowego rozkładu ćwiczeń). Miałbyś wtedy WorkoutDay<T extends Schema> ... Ale to już na dalszym etapie.

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