У меня есть ситуация, когда я пытаюсь получить объект. Если поиск не удался, у меня есть несколько запасных вариантов, каждый из которых может дать сбой. Итак, код выглядит так:
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);
}
}
}
Это выглядит ужасно уродливо. Я ненавижу возвращать ноль, но разве это лучше в этой ситуации?
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;
Есть ли другие альтернативы?
Является ли использование вложенных блоков try-catch антишаблоном? связан, но ответы там по типу «иногда, но обычно этого можно избежать», не говоря, когда или как этого избежать.
java
exception-handling
Алекс Виттиг
источник
источник
NotFoundException
то, что на самом деле является исключительным?Ответы:
Обычный способ устранения вложенности заключается в использовании функций:
Если эти резервные правила универсальны, вы можете рассмотреть возможность реализации этого непосредственно в
repository
объекте, где вы могли бы просто использовать простыеif
выражения вместо исключения.источник
method
было бы лучше, чем словоfunction
.Это было бы действительно легко с чем-то вроде монады Option. К сожалению, у Java их нет. В Scala я бы использовал
Try
тип, чтобы найти первое успешное решение.В своем мышлении функционального программирования я настраивал список обратных вызовов, представляющих различные возможные источники, и перебирал их, пока не нашел первый успешный:
Это может быть рекомендовано, только если у вас действительно много источников, или если вы должны настроить источники во время выполнения. В противном случае это ненужная абстракция, и вы получите больше пользы от сохранения вашего кода простым и глупым и просто будете использовать эти уродливые вложенные try-catch.
источник
Try
типа в Scala, за упоминание монад и за решение с использованием цикла.Optional
монады ( доказательства ) уже была выпущена.Если вы ожидаете, что многие из этих вызовов репозитория будут выбрасываться
NotFoundException
, вы можете использовать обертку вокруг репозитория, чтобы упростить код. Я бы не рекомендовал это для нормальной работы:источник
По предложению @ amon вот более монадический ответ. Это очень упрощенная версия, в которой вы должны принять несколько предположений:
функция "unit" или "return" является конструктором класса
операция связывания происходит во время компиляции, поэтому она скрыта от вызова
функции "action" также связаны с классом во время компиляции
хотя класс является универсальным и охватывает любой произвольный класс E, я думаю, что в данном случае это на самом деле избыточно. Но я оставил это как пример того, что вы могли бы сделать.
Исходя из этих соображений, монада превращается в плавный класс-обертку (хотя вы теряете большую гибкость, которую получаете в чисто функциональном языке):
(это не скомпилируется ... некоторые детали остаются незавершенными, чтобы сохранить размер выборки)
И вызов будет выглядеть так:
Обратите внимание, что у вас есть возможность составлять операции «извлечения» так, как вам нравится. Он остановится, когда получит ответ или исключение, отличное от не найденного.
Я сделал это очень быстро; это не совсем правильно, но, надеюсь, передает идею
источник
repository.fetchElement().fetchParentElement().fetchSimilarElement();
- по моему мнению: злой кодекс (в том смысле, который дан Джоном Скитом)return this
для создания вызовов объектов цепочки уже давно. Поскольку ОО включает изменяемые объекты,return this
это более или менее эквивалентноreturn null
без цепочки. Тем не менее,return new Thing<E>
открывается дверь к другой возможности, в которую этот пример не входит, поэтому для этого шаблона важно, если вы решите пойти по этому пути.CustomerBuilder.withName("Steve").withID(403)
этим и этим кодом, потому что просто.fetchElement().fetchParentElement().fetchSimilarElement()
неясно, что происходит, и это ключевой момент. Они все забраны? Это не накапливается в этом случае, и, следовательно, не настолько интуитивно понятен. Мне нужно увидеть это,if (answer != null) return this
прежде чем я действительно получу это. Возможно, это просто вопрос правильного именования (orFetchParent
), но в любом случае это «волшебство».answer
ingetAnswer
и сбросить (очистить) самоanswer
поле перед возвратом его значения. Иначе это как бы нарушает принцип разделения команды / запроса, потому что запрос на выбор элемента (запрос) изменяет состояние вашего объекта репозитория (answer
никогда не сбрасывается) и влияет на поведениеfetchElement
при следующем вызове. Да, я немного придираюсь, я думаю, что ответ верен, я не был тем, кто отрицал это.Другой способ структурировать ряд условий, подобных этому, состоит в том, чтобы пометить флаг или проверить нулевое значение (еще лучше, если вы используете опцию Guava Optional для определения наличия хорошего ответа), чтобы связать условия в единое целое.
Таким образом, вы наблюдаете за состоянием элемента и делаете правильные вызовы, основываясь на его состоянии, то есть до тех пор, пока у вас еще нет ответа.
(Однако я согласен с @amon. Я бы рекомендовал посмотреть шаблон Monad с таким объектом-оберткой,
class Repository<E>
который имеет членыE answer;
иException error;
. На каждом этапе проверяйте, есть ли исключение, и если да, пропустите каждый оставшийся шаг. в конце концов, у вас остается либо ответ, либо отсутствие ответа, либо исключение, и вы можете решить, что с этим делать.)источник
Во-первых, мне кажется, что должна существовать функция, подобная
repository.getMostSimilar(x)
(вы должны выбрать более подходящее имя), поскольку существует логика, которая используется для нахождения ближайшего или наиболее похожего элемента для данного элемента.Затем репозиторий может реализовать логику, как показано в посте amons. Это означает, что единственный случай, когда должно быть сгенерировано исключение, - это когда нет единственного элемента, который можно найти.
Однако это, конечно, возможно только в том случае, если логика для поиска ближайшего элемента может быть инкапсулирована в хранилище. Если это невозможно, предоставьте больше информации о том, как (по каким критериям) можно выбрать ближайший элемент.
источник