Wpis z mikrobloga

Wydawało mi się, że używanie wyjątków do sterowania flow programu jest dość powszechnie uznane za złe, jednak potem przeczytałem dość długą dyskusję o sensie używania Optionali (że nieczytelne itp). Sam jeszcze nawet juniorem nie jestem, więc do niedawna jeszcze w moim projekcie przy braku użytkownika leciał UserNotFoundException, no ale uznałem w końcu, że to strasznie głupie i tak stopniowo przebiegała przebudowa:
Pierwsze co przyszło mi do głowy to Optional (a konkretniej Option z vavr):

private Option findUserById(long id) {
return Option.of(repository.findOne(id));
}

Oczywiste zalety to brak głupich wyjątków oraz nullchecków. Problem zaczyna się, kiedy jest jakiś dłuższy proces gdzie więcej niż jedna rzecz może pójść nie tak (powyżej jedyne co to użytkownika może nie być). Zostając w temacie niech będzie przykład logowania gdzie po podaniu nazwy i hasła ma zostać zwrócony token. Coś w stylu
String login(String username, String password)

Co może nie wyjść? np brak użytkownika, niepoprawne hasło. Samo Option tutaj nie pomoże bo skąd mamy potem wiedzieć co nie wyszło?
Tutaj z pomocą przychodzi Either, kolejna super rzecz z vavra oraz enum z możliwymi błędami:

enum UserError {
WRONG_PASSWORD,USERNAME_NOT_FOUND
}

Teraz starczy zmienić powyższą sygnaturę na

Either login(String username, String password)
Z biegiem czasu praktycznie wszystkie metody u mnie zwracają Either, albo tam gdzie starcza to Option. Dzięki temu część funkcji można fajnie skrótowo zapisać z pominięciem ifów (tak na prawdę te ify są tylko wewnątrz Either i Option). Dla przykładu to logowanie:

return findUserByUsername(username)
.flatMap(user -> checkPasswordFor(candidatePassword, user))
.map(UserEntity::getUuid)
.map(tokenManager::generateTokenFor);

Z minusów, niestety trzeba nauczyć się nowego API i trochę innego sposobu myślenia. Dodatkowo dla niektórych pierwotnie to może się wydawać mniej czytelne.

Oczywiście nie neguje wyjątków całkowicie. Są miejsca gdzie powinny one zostać np łapie i odpowiednio rzucam dalej SQLException jakby coś złego było z bazą (bo to faktycznie jest sytuacja wyjątkowa)

Wpis powstał trochę na zasadzie dzielenia się moich przemyśleń przy tworzeniu swojego projektu, na jakie problemy trafiłem i jak je rozwiązuje. Sam za dużo doświadczenia jeszcze nie mam (niedługo ledwie stanę się juniorem(oby)) więc bardzo chętnie powymieniam się uwagami.

Jeśli komuś będzie chciało się to czytać to pewnie w przyszłości coś jeszcze napisze ;)
#java #webdev #programowanie
  • 11
@krasnoludkolo: w Scali rozwiązuje to właśnie Either. Plus jest taki,że Either jest monadą, więc zakończy się przy pierwszej wartości Left. Tyle że w Scali pracę z monadami ułatwia znacznie sama składnia (a konkretnie for comprehension)
@porn: Akurat Springiem to bym się nie sugerował w poszukiwaniu dobrych praktyk programistycznych :P

@krasnoludkolo: Oprócz Eithera możesz przyjrzeć się również strukturze Try. Nie używam Vavra ale widzę że jest w nim dostępna. Wtedy każde odstępstwo od happy patha będziesz mógł reprezentować jako wartość, nawet ten SQLException o którym piszesz. W przypadku usługi webowej, taką wartość przepychasz wówczas do kontrolera, w którym "odpakowujesz" Either/Try i robisz np. logowanie błędu