Mam sytuację, w której próbuję odzyskać obiekt. Jeśli wyszukiwanie nie powiedzie się, mam kilka awarii, z których każda może się nie powieść. Kod wygląda więc tak:
try {
return repository.getElement(x);
} catch (NotFoundException e) {
try {
return repository.getSimilarElement(x);
} catch (NotFoundException e1) {
try {
return repository.getParentElement(x);
} catch (NotFoundException e2) {
//can't recover
throw new IllegalArgumentException(e);
}
}
}
To wygląda okropnie brzydko. Nienawidzę zwracać wartości zerowej, ale czy w tej sytuacji jest lepiej?
Element e = return repository.getElement(x);
if (e == null) {
e = repository.getSimilarElement(x);
}
if (e == null) {
e = repository.getParentElement(x);
}
if (e == null) {
throw new IllegalArgumentException();
}
return e;
Czy są inne alternatywy?
Czy używanie zagnieżdżonych bloków try-catch jest anty-wzorem? jest powiązany, ale odpowiedzi są podobne do „czasami, ale zwykle można tego uniknąć”, nie mówiąc, kiedy i jak tego uniknąć.
java
exception-handling
Alex Wittig
źródło
źródło
NotFoundException
coś naprawdę jest wyjątkowego?Odpowiedzi:
Zwykłym sposobem na wyeliminowanie zagnieżdżenia jest użycie funkcji:
Jeśli te zasady zastępcze są uniwersalne, można rozważyć wdrożenie tego bezpośrednio w
repository
obiekcie, w którym można użyć zwykłychif
instrukcji zamiast wyjątku.źródło
method
byłoby lepszym słowem niżfunction
.Byłoby to naprawdę łatwe z czymś takim jak monada Option. Niestety Java nie ma takich. W Scali użyłbym tego
Try
typu, aby znaleźć pierwsze udane rozwiązanie.W moim sposobie myślenia o programowaniu funkcjonalnym stworzyłem listę wywołań zwrotnych reprezentujących różne możliwe źródła i przeglądałem je, dopóki nie znajdziemy pierwszego udanego:
Może to być zalecane tylko wtedy, gdy naprawdę masz dużą liczbę źródeł lub jeśli musisz skonfigurować źródła w czasie wykonywania. W przeciwnym razie jest to niepotrzebna abstrakcja, a zyskasz więcej, utrzymując swój kod prosty i głupi, i po prostu wykorzystaj te brzydkie zagnieżdżone try-catch.
źródło
Try
typie w Scali, za wspomnienie o monadach i rozwiązanie za pomocą pętli.Optional
monady ( proof ) została już wydana.Jeśli spodziewasz się, że wiele z tych wywołań repozytorium zostanie wyrzuconych
NotFoundException
, możesz użyć opakowania wokół repozytorium, aby usprawnić kod. Nie polecałbym tego do normalnych operacji, pamiętaj:źródło
Zgodnie z sugestią @ amon, oto odpowiedź, która jest bardziej monadyczna. Jest to bardzo uproszczona wersja, w której musisz zaakceptować kilka założeń:
funkcja „unit” lub „return” jest konstruktorem klasy
operacja „bind” ma miejsce w czasie kompilacji, więc jest ukryta przed wywołaniem
funkcje „akcji” są również powiązane z klasą w czasie kompilacji
chociaż klasa jest ogólna i obejmuje dowolną klasę E, myślę, że w tym przypadku jest to przesada. Ale zostawiłem to w ten sposób jako przykład tego, co możesz zrobić.
Biorąc pod uwagę powyższe rozważania, monada przekłada się na płynną klasę opakowania (chociaż rezygnujesz z dużej elastyczności, którą uzyskałbyś w czysto funkcjonalnym języku):
(to się nie skompiluje ... niektóre szczegóły pozostają niedokończone, aby próbka była mała)
I wywołanie wyglądałoby tak:
Pamiętaj, że możesz dowolnie komponować operacje „pobierania”. Zatrzyma się, gdy otrzyma odpowiedź lub wyjątek inny niż nie znaleziony.
Zrobiłem to naprawdę szybko; nie jest to w porządku, ale mam nadzieję, że przekazuje ten pomysł
źródło
repository.fetchElement().fetchParentElement().fetchSimilarElement();
- moim zdaniem: zły kod (w sensie podanym przez Jona Skeeta)return this
do tworzenia wywołań obiektów łańcuchowych istnieje już od dawna. Ponieważ OO obejmuje obiekty zmienne,return this
jest mniej więcej równoważnereturn null
bez łączenia łańcuchów. Jednakże,return new Thing<E>
otwiera drzwi do innej możliwości, że ten przykład nie przechodzi w tak ważne dla tego wzorca, jeśli zdecydujesz się pójść tą drogą.CustomerBuilder.withName("Steve").withID(403)
tym kodem, ponieważ sam widok.fetchElement().fetchParentElement().fetchSimilarElement()
nie jest jasne, co się dzieje, i to jest kluczowe. Czy wszyscy zostali przyprowadzeni? W tym przypadku nie kumuluje się, a zatem nie jest tak intuicyjny. Muszę to zobaczyć,if (answer != null) return this
zanim naprawdę to zrozumiem. Być może jest to tylko kwestia właściwego nazewnictwa (orFetchParent
), ale i tak jest to „magia”.answer
wgetAnswer
i resetu (wyczyść)answer
Samo pole przed powrotem jego wartość. W przeciwnym razie łamie to zasadę rozdzielania poleceń / zapytań, ponieważ żądanie pobrania elementu (zapytania) zmienia stan obiektu repozytorium (answer
nigdy nie jest resetowany) i wpływa na zachowaniefetchElement
następnego wywołania. Tak, trochę podrywam, myślę, że odpowiedź jest prawidłowa, nie byłem tym, który ją ocenił.Innym sposobem ustrukturyzowania szeregu takich warunków jest noszenie flagi lub testowanie wartości zerowej (jeszcze lepiej, użyj Opcji Guava, aby określić, kiedy jest dostępna dobra odpowiedź), aby połączyć warunki razem.
W ten sposób obserwujesz stan elementu i wykonujesz odpowiednie wywołania na podstawie jego stanu - to znaczy dopóki nie masz jeszcze odpowiedzi.
(Zgadzam się jednak z @amon. Polecam przyjrzeć się wzorowi Monad, z takim obiektem opakowującym,
class Repository<E>
który ma członkówE answer;
iException error;
. Na każdym etapie sprawdzaj, czy jest wyjątek, a jeśli tak, pomiń każdy pozostały krok. koniec, masz albo odpowiedź, brak odpowiedzi, albo wyjątek i możesz zdecydować, co z tym zrobić.)źródło
Po pierwsze, wydaje mi się, że powinna istnieć taka funkcja
repository.getMostSimilar(x)
(powinieneś wybrać bardziej odpowiednią nazwę), ponieważ wydaje się, że istnieje logika służąca do znajdowania najbliższego lub najbardziej podobnego elementu dla danego elementu.Repozytorium może następnie zaimplementować logikę pokazaną w poście amons. Oznacza to, że jedynym przypadkiem wyjątku jest zgłoszenie wyjątku, gdy nie można znaleźć żadnego pojedynczego elementu.
Jest to jednak oczywiście możliwe tylko wtedy, gdy logika znajdowania najbliższego elementu może być zawarta w repozytorium. Jeśli nie jest to możliwe, proszę podać więcej informacji, w jaki sposób (według jakich kryteriów) można wybrać najbliższy element.
źródło