Что является альтернативой исключениям для управления потоком?
Я унаследовал Java-приложение, которое обрабатывает запросы и выдает исключение, если оно решает, что запрос должен быть отменен. Исключения были удобны для предыдущего разработчика, потому что они - простой способ выйти из дерева логики, которое больше не применяется (да, goto), и это печатает трассировку стека в журнал, который является хорошей информацией. Похоже, что на форумах и в блогах все согласны с тем, что исключения не должны использоваться для управления потоком, и более половины обработанных запросов отменяются, поэтому они определенно не являются исключительными ситуациями. Одним из аргументов является производительность, которая не применяется, потому что наш исключительный код работает достаточно быстро в течение многих лет. Еще один аргумент в том, что с этим неясно, с чем я согласен. Мой вопрос: какова альтернатива. Единственное, о чем я могу думать, - это чтобы каждый метод возвращал значение true, если обработка должна продолжаться, или false, если это не так. Похоже, это сильно раздуло бы мой код, изменив это:
checkSomething();
checkSomethingElse();
в это:
boolean continueProcessing = false;
continueProcessing = checkSomething();
if (!continueProcessing) {
return false;
}
continueProcessing = checkSomethingElse();
if (!continueProcessing) {
return false;
}
И что тогда, если метод должен что-то возвращать? Любое руководство было бы здорово. Мне бы очень хотелось посмотреть, какие "лучшие практики" доступны.
Обновить:
Еще один момент, который я, вероятно, должен был упомянуть в первую очередь, это то, что запрос отменяется более 50% времени, и это не означает, что что-то пошло не так, это означает, что запрос в конце концов не нужен.
5 ответов
В вашем сценарии альтернативы выдают исключение и возвращают результат.
Что лучше (а что является "наилучшей практикой"), зависит от того, следует ли классифицировать сбой методов "проверки..." как нормальный или исключительный. В какой-то степени это суждение, которое вы должны сделать. При совершении этого звонка нужно помнить несколько вещей:
Создание + throwing + catching исключение примерно на 3 порядка меньше, чем тестирование логического результата.
Большая проблема с возвратом кодов состояния заключается в том, что их легко забыть проверить.
Таким образом, лучше всего не использовать исключения для реализации нормального управления потоком, но вам решать, где находится граница между нормальным и исключительным.
Не видя реального кода / контекста, я чувствую, что это подходящее место для использования исключений.
См. Как медленно работают исключения Java? для большой дискуссии на эту тему.
ТЛ; др
Разделение концернов, и IMO, вы должны сделать это:
continue = checkSomething() && checkSomethingElse();
или, возможно, есть другие проблемы дизайна в коде.
Что касается функции - как вы хотите ее определить (это может быть субъективным вопросом)? Если ошибка вписывается в суть функции, не создавайте исключение. Если вы не уверены, подходит ли ошибка к проблеме, то, возможно, функция пытается решить слишком много задач сама по себе (плохой дизайн).
Варианты контроля ошибок:
- не сообщать об ошибке. Он либо обрабатывается напрямую функцией, либо не имеет значения
- возвращаемое значение
- нуль вместо объекта
- информация об ошибке (возможно, даже другой тип данных, чем объект, возвращаемый в случае успеха).
- переданный аргумент будет использоваться для хранения данных об ошибках.
- вызвать событие
- вызвать закрытие, переданное функции, если происходит ошибка.
- бросить исключение. (Я утверждаю, что это обычно следует делать, только если это не является частью произвольно определенного назначения функции)
Если целью кода является проверка какого-либо состояния, то зная, что состояние ложно, является непосредственно точкой функции. Не выбрасывайте исключение, а возвращайте false.
Вот как это выглядит, что вы хотите. У вас есть процесс X, на котором запущены контролеры Y и Z. Поток управления для процесса X (или любого вызывающего процесса) - это не то же самое, что проверка состояний системы.
int error = 0;
do
{//try-alt
error = operation_1(); if(error > 0){break;}
error = operation_2(); if(error > 0){break;}
error = operation_3(); if(error > 0){break;}
}while(false);
switch(error) //catch-alt
{
case 1: handle(1); break;
case 2: handle(2); break;
case 3: handle(3); break;
}
Как насчет
if (!checkSomething()) return false;
if (!checkSomethingElse()) return false;
Нет необходимости в флаге, если вы выходите рано.