Optional'e, wyjątki, ocena projektu

Optional'e, wyjątki, ocena projektu
JL
  • Rejestracja:ponad 8 lat
  • Ostatnio:ponad 4 lata
  • Lokalizacja:Ruda Śląska
  • Postów:7
0

Witam,

Jestem w trakcie realizacji swojego pierwszego "nie banalnego" projektu (praca inżynierska) i mam parę problemów architektonicznych. Serwer jest wielowarstwowy i zastosowano wzorzec MVC.

  1. Optional -> kiedy wypakować obiekt?, kiedy wyrzucić wyjątek jeśli obiekt jest null?, czy może w logice biznesowej należy cały czas przekazywać sobie Optional'e i dopiero w klasie Controller go wypakować? Widziałem różne projekty na githubie i były różne konwencje stosowania Optional'i.
    Ja w swoim projekcie gdy pobieram Optional'a z bazy od razu go weryfikuje czy nie jest null`em i wyrzucam wyjątek.

  2. Wyjątki -> znajdując adnotacje @ResponseStatus przestałem używać try catch`e, ponieważ jeśli wyjątek zostanie wyrzucony to serwer zareaguje mi tak jak chce, gdyż @ResponseStatus umożliwia mi ustalenie HttpStatus oraz reason, który wyświetli się w przeglądarce. Jestem na 100% pewien, że porzucenie try catch'ów było bardzo złym krokiem, także dlatego potrzebuje wyjaśnienia.

To chyba już wszystko :)

Jestem w miarę początkującym javowcem i nie mam jeszcze komercyjnego doświadczenia :)

Tak przy okazji projekt jest na githubie: https://github.com/coverdm/TeamWorkSupportSystem

Chciałbym również, aby ktoś "zerknął" na projekt i go skrytykował. Tworzę aplikacje podobną do JIRY (nie jest to skończony projekt). Clienta jeszcze nie zacząłem pisać. Aktualnie czytam clean code i zasady Jeff'a Bay'a które pomagają mi zarządzać kodem. Ostatnio bardzo dużo czasu siedzę przy zasadach SOLID (najwięcej przy SRP) i zależy mi na tworzeniu oprogramowania zgodnie z zasadami.

S9
  • Rejestracja:ponad 10 lat
  • Ostatnio:5 miesięcy
  • Lokalizacja:Warszawa
  • Postów:3573
1

Z całym szacunkiem nie wygląda to najlepiej :D Dobra, no to lecim z tym koksem:
1)Zamiast dzielić projekt "pionowo" podzieliłbym poziomo, czyli pakiety repository, entity, controller etc.
2)Po co kwiatki w stylu UserPersistence ? Właściwie to widze że to głównie wywołanie operacji z Repository, ale jest na przykład

Kopiuj
private String getLoggedUser(){
        return SecurityContextHolder.getContext().getAuthentication().getName();
    }

To później przekazywane jest do innej metody która ma zmienić hasło. To jest architektoniczna tragedia, jeśli mamy repository to na pewno nie powinno wiedziec jaki jest użytkownik zalogowany.
Do metody ze zmianą hasła powinno być przekazane hasło i login. To chyba jednak ma byc obiekt od logiki? W takim razie nazwa niezbyt trafna. Zreszta własnie zauważyłem że nazwałeś jest z adnotacją @Service. Słabo, zmienić nazwe na UserService
3)Operowanie na encjach bezpośrednio, nie korzystanie z DTO -> słabo. Poczytaj o transakcjach w JPA


"w haśle <młody dynamiczny zespół> nie chodzi o to ile masz lat tylko jak często zmienia się skład"
hcubyc
Co jest złego w jego podziale? Po pierwszym rzucie oka wygląda spoko, jeżeli nie ma zależności między tymi pakietami lub są ograniczone do minimum to IMHO bardzo dobrze
Shalom
  • Rejestracja:około 21 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Space: the final frontier
  • Postów:26433
3

@scibi92 nie zgadzam się z pomysłem o twoim podziale. Nie ma nic złego w tym co OP zrobił. Ba, często ma to dużo więcej sensu jeśli te moduły są na tyle niezależne że można z nich zrobić coś w stylu "mikroserwisów", a tak właśnie jest. Ja bym to w środku jeszcze trochę ewentualnie podzielił na pakiety/podmoduły żeby oderwać elementy zależne od frameworka (np. kontrolery) od logiki biznesowej, ale poza tym jest zupełnie ok. W badzo dużych projektach to co sugerujesz się zwyczajnie nie sprawdza, bo życzę powodzenia z pakietem controllers który ma powiedzmy tysiąc klas ;]
Zresztą zastanów się co sie dzieje jak coś modyfikujesz -> zwykle interesuje cię jeden z modułów i chcesz dodać np. nowy mapping kontrolera, nowe serwisy, nowe klasy domenowe itd, ale wszystko to związane jest z jednym modułem i w nim możesz pracować.
Jeśli ktoś zrobi tak jak sugerujesz to nagle modyfikuje każdy z modułów aplikacji, bo każdy zawiera mały kawałek. To się sprawdza dla małej aplikacji, albo takiej która ma mocno spójne funkcjonalności.

Wracając do wątku:

  1. Nie ma prostej zasady, ot wypakowujesz tam gdzie potrzebujesz :)
  2. Zwykle potrzebujesz jakiejś "translacji" błędu. Rzucenie userowi do UI jakiegoś NullPointerException to zły pomysł bo ani on nie wie co jest nie tak ani ty, kiedy dostaniesz ticked z bug reportem. Zresztą często wcale nie chcesz od razu "wywalić" całej akcji bo poleciał jakiś wyjątek. Z wielu wyjątków można jeszcze zrobić jakieś recovery...

"Nie brookliński most, ale przemienić w jasny, nowy dzień najsmutniejszą noc - to jest dopiero coś!"
edytowany 1x, ostatnio: Shalom
JL
  • Rejestracja:ponad 8 lat
  • Ostatnio:ponad 4 lata
  • Lokalizacja:Ruda Śląska
  • Postów:7
0

Dzięki za szybką i wartościową krytykę! :) Jak wspominałem jestem raczej początkującym programistą, który uczy się aby zostać juniorem ^^ :)

scibi92 napisał(a):

1)Zamiast dzielić projekt "pionowo" podzieliłbym poziomo, czyli pakiety repository, entity, controller etc.

Jeszcze 2 tygodnie temu miałem katalogi typu repositories, domain, controllers..., ale to się raczej sprawdza przy małych projektach. W rozbudowanych systemach widywałem katalogowanie po modułach user, project itp. Chodzi mi o to, że ciężko byłoby się odnaleźć w projekcie przy większej ilości klas.

scibi92 napisał(a):

2)Po co kwiatki w stylu UserPersistence ? Właściwie to widze że to głównie wywołanie operacji z Repository, ale jest na przykład

Kopiuj
private String getLoggedUser(){
        return SecurityContextHolder.getContext().getAuthentication().getName();
    }

Do metody ze zmianą hasła powinno być przekazane hasło i login. To chyba jednak ma byc obiekt od logiki? W takim razie nazwa niezbyt trafna. Zreszta własnie zauważyłem że nazwałeś jest z adnotacją @Service. Słabo, zmienić nazwe na UserService

Używanie nazewnictwa typu UserService, ProjectService jest według mnie błędne. Co te nazwy oznaczają? Do takich klas można wszystko przypisać od persystencji do powiedzmy zmiany hasła użytkownika. Takie coś raczej jest błędne jeśli chodzi o zasady SOLID, a dokładniej SRP. Ubolewam nad faktem, iż mam klasę ProjectManager do której teoretycznie też można wszystko wrzucić. Aktualnie szukam fajnego sposobu, aby ten problem rozwiązać.

Stworzyłem klase UserPersistence (która jest serwisem) do komunikacji z repozytorium, tym samym odciąż klasę typu ProjectManager od posiadania metod CRUD'a. Oczywiście na siłę mógłbym mieć komunikacje bezpośrednią z repository, ale gdy chciałbym wysłać dane do kontrolera to tylko mógłbym przez repository, a samo w sobie to jest błąd gdyż kontroler nie powinien mieć dostępu bezpośredniego do repozytorium.

S9
  • Rejestracja:ponad 10 lat
  • Ostatnio:5 miesięcy
  • Lokalizacja:Warszawa
  • Postów:3573
1

@Shalom, @JustLive: oczywiście masz racje, dlatego ja jestem za podziałem tez na moduły, ale nie chodzi o to żeby jeden moduł miał jedną encję ;]
Można to rozdzielic na "mikroserwisy" i nawet zrobić z tego moduły mavenowe, ale powinno to być jednak troche większe

Używanie nazewnictwa typu UserService, ProjectService jest według mnie błędne. Co te nazwy oznaczają? Do takich klas można wszystko przypisać od persystencji do powiedzmy zmiany hasła użytkownika. Takie coś raczej jest błędne jeśli chodzi o zasady SOLID, a dokładniej SRP

Masz rację. Dlatego nie powinno być klas np. UserService tylko UserRegisterService, gdzie masz obsługę rejestracji. Tworzenie serwisów per encja też mi nie odpowiada (no chyba że rzeczywiście jest tam jedna czy 2 metody). UserPersistence to jakas dla mnie średnio dobra nazwa


"w haśle <młody dynamiczny zespół> nie chodzi o to ile masz lat tylko jak często zmienia się skład"
JL
  • Rejestracja:ponad 8 lat
  • Ostatnio:ponad 4 lata
  • Lokalizacja:Ruda Śląska
  • Postów:7
0
Shalom napisał(a):
  1. Zwykle potrzebujesz jakiejś "translacji" błędu. Rzucenie userowi do UI jakiegoś NullPointerException to zły pomysł bo ani on nie wie co jest nie tak ani ty, kiedy dostaniesz ticked z bug reportem. Zresztą często wcale nie chcesz od razu "wywalić" całej akcji bo poleciał jakiś wyjątek. Z wielu wyjątków można jeszcze zrobić jakieś recovery...

Zwykle gdy jest szansa wystąpienia NPE to tworze własne wyjątki typu UserNotFoundException bądź ProjectNotFoundException i gdy ten wyjątek zostanie wyrzucony to w przeglądarce wyświetla się komunikat: no a such project, z kodem 404. A to jest chyba coś co chcę osiągnąć :)
Aktualnie z moim bieżącym doświadczeniem zawsze gdy miałem do czynienia z wyjątkami to ...uwaga... robiłem printStackTrace hihihi :)

Wcześniej przed odnalezieniem ten adnotacji używałem try catcha w kontrolerze... ale to nie jest "seksi" rozwiązanie, także zacząłem szukać i szukać, aż znalazłem @ResponseStatus :)

S9
@JustLive: w tym samym czasie napisałem posta co Ty więc możesz nie wiedziec że odpowiedziałem :P
jarekczek
  • Rejestracja:prawie 8 lat
  • Ostatnio:ponad 4 lata
  • Lokalizacja:Siemianowice Śląskie
  • Postów:500
1

gdyż @ResponseStatus umożliwia mi ustalenie HttpStatus oraz reason

reason nie należy używać. Klient ma obowiązek ignorować ten tekst. Może w Twoim zastosowaniu (nie oglądałem) to pasuje, ale piszę o ogólnych zasadach HTTP, usankcjonowanych przez RFC (Override the HTTP response status text).

Komunikat o błędzie powinien się znaleźć w body albo jakimś dedykowanym nagłówku.


Przeważnie ignoruję niezarejestrowanych użytkowników.
JL
  • Rejestracja:ponad 8 lat
  • Ostatnio:ponad 4 lata
  • Lokalizacja:Ruda Śląska
  • Postów:7
0
scibi92 napisał(a):

Masz rację. Dlatego nie powinno być klas np. UserService tylko UserRegisterService, gdzie masz obsługę rejestracji. Tworzenie serwisów per encja też mi nie odpowiada (no chyba że rzeczywiście jest tam jedna czy 2 metody). UserPersistence to jakas dla mnie średnio dobra nazwa

UserPersistence to nazwa wymyślona na szybko, żeby rozdzielić dwie klasy :) A zmiana nazwy to już kwestia sekund ^^. Ważne żeby trzymało się wszystko zgodnie z zasadami. Chodzi mi o jakość kodu, żeby był łatwy w utrzymaniu, łatwy w testowaniu itp.
Zdaje sobie sprawę, że to co aktualnie prezentuje to jest poniżej jakiejkolwiek normy :D Jestem samoukiem i uczę się na błędach :) :)

Jeśli chodzi o mikroserwisy to jeszcze nigdy tego nie robiłem. Fakt, bawię się Dockerem i umiem postawić jedynie aplikacje w jednym kontenerze a baze mysql w drugim kontenerze i zmusić żeby aplikacja pobierała dane z bazy, ale to już wszystko :)

S9
  • Rejestracja:ponad 10 lat
  • Ostatnio:5 miesięcy
  • Lokalizacja:Warszawa
  • Postów:3573
2

To były "mikroserwisy" w sensie potocznym, chodzi o to o żeby zrobić modularyzacje "pionową" i "poziomią",np.

com.eshop
-users
--repository
--service
--constroller
--entity
--dto
-products
--repository
--service

itd. ;)


"w haśle <młody dynamiczny zespół> nie chodzi o to ile masz lat tylko jak często zmienia się skład"
edytowany 1x, ostatnio: scibi92
AreQ212
  • Rejestracja:około 11 lat
  • Ostatnio:około 5 lat
  • Postów:29
0
Koziołek
Moderator
  • Rejestracja:prawie 18 lat
  • Ostatnio:około 2 miesiące
  • Lokalizacja:Stacktrace
  • Postów:6821
4
  1. Najlepiej nigdy. Pandora już raz rozpakowała Optionala i do dziś dzień dzieci uczą się o niej w szkole. Pudełko pod tytułem Optional traktujemy jako pudełko i jeżeli chcemy coś zrobić z wartością w środku, to przekazujemy odpowiednią funkcję. Niech rozpakowaniem zajmie się kod, który potrafi to zrobić.

  2. To zależy. Jeżeli masz ogarniętą translację błędów na kody, to można sobie odpuścić try/catch i trzeba tylko zadbać, by gdzieś były logowane szczegółowe informacje o błędach. Tak by można było odgrzebać zgłoszenie użytkownika. Jeżeli nie masz tego mechanizmu, to łapanie błędów blisko punktu wystąpienia, obsługa i wyrzucenie po opakowaniu, by przechwycić wyżej w celu nadania odpowiedniego statusu HTTP będzie lepsze.

edit

aż popełniłem https://koziolekweb.pl/2017/08/07/kiedy-wypakowac-optional/


Sięgam tam, gdzie wzrok nie sięga… a tam NullPointerException
edytowany 3x, ostatnio: Koziołek
jarekr000000
Dzięki za ten wpis. Ale jako łyzkę dziegciu dodam, ża tak się kończy Java - wszystko obs....ane w monady. Map, flatMapem pogania i wtedy patrzysz na scalowego for { ... } albo Haskellowe do i zazdrościsz. Nawet w Kotlinie dorobili coś podobnego w Coroutines. Do tego cały kod bez mutowania ... fajnie ale bez value types to walenie kotka. W Scali i Haskellu tak naprawdę jest jescze bardziej wszystko opakowane monadami... tylko dzięki lepszej "Inferencji"(?) typów i tym cukrom składniowym nie widać. A czego oczy nie widzą to nie wpada do Gita.
Koziołek
@jarekr000000: w Javie już tak jest, że jak chcesz mieć coś dobrze, to musisz to albo napisać w scali, albo samodzielnie naklepać się różnych typów.
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)