Создание исключения во время выполнения в качестве логики управления, безусловно, плохая идея. Причина, по которой вы чувствуете себя грязным, заключается в том, что вы обходите систему типов, то есть возвращаемый тип ваших методов - ложь.
У вас есть несколько вариантов, которые намного более чисты.
1. Функтор исключений
Хорошая техника для использования, когда вы ограничены в исключениях, которые вы можете выбросить, если вы не можете выбросить проверенное исключение, вернуть объект, который вызовет проверенное исключение. Например, java.util.concurrent.Callable является экземпляром этого функтора.
Смотрите здесь для подробного объяснения этой техники.
Например, вместо этого:
public Something visit(Node n) {
if (n.someting())
return new Something();
else
throw new Error("Remember to catch me!");
}
Сделайте это:
public Callable<Something> visit(final Node n) {
return new Callable<Something>() {
public Something call() throws Exception {
if (n.something())
return new Something();
else
throw new Exception("Unforgettable!");
}
};
}
2. Несвязанный Союз (a.k.a. Любой Бифунктор)
Этот метод позволяет вам вернуть один или два разных типа из одного и того же метода. Это немного похоже на методику Tuple<A, B>
, с которой большинство людей знакомо для получения более одного значения из метода. Однако вместо того, чтобы возвращать значения обоих типов A и B, это предполагает возврат одного значения типа A или B.
Например, при перечислении Fail, которое может перечислять применимые коды ошибок, примером становится ...
public Either<Fail, Something> visit(final Node n) {
if (n.something())
return Either.<Fail, Something>right(new Something());
else
return Either.<Fail, Something>left(Fail.DONE);
}
Выполнение вызова теперь намного чище, потому что вам не нужно пытаться / ловить:
Either<Fail, Something> x = node.dispatch(visitor);
for (Something s : x.rightProjection()) {
// Do something with Something
}
for (Fail f : x.leftProjection()) {
// Handle failure
}
Класс Either не очень сложен для написания, но полнофункциональная реализация предоставляется библиотекой Functional Java .
3. Опциональная монада
Немного похоже на типобезопасный ноль, это хороший метод, который нужно использовать, когда вы не хотите возвращать значение для некоторых входных данных, но вам не нужны исключения или коды ошибок. Обычно люди возвращают так называемую «дозорную ценность», но Option значительно чище.
Теперь у вас есть ...
public Option<Something> visit(final Node n) {
if (n.something())
return Option.some(new Something());
else
return Option.<Something>none();
}
Звонок приятный и чистый:
Option<Something> s = node.dispatch(visitor));
if (s.isSome()) {
Something x = s.some();
// Do something with x.
}
else {
// Handle None.
}
А тот факт, что это монада , позволяет вам связывать вызовы без обработки специального значения None:
public Option<Something> visit(final Node n) {
return dispatch(getIfPart(n).orElse(dispatch(getElsePart(n)));
}
Класс Option написать даже легче, чем Either, но, опять же, полнофункциональная реализация предоставляется библиотекой Functional Java .
См. Подробное обсуждение Option и Either.