Считается ли try / catch для каждого отдельного оператора, вызывающего исключение, анти-шаблоном?

В настоящее время я просматриваю код Java коллег, и я вижу много случаев, когда каждый отдельный оператор, который может вызвать исключение, инкапсулируется в его собственном try / catch. Где все блоки catch выполняют одну и ту же операцию (какая операция не имеет отношения к моему вопросу).

Мне это кажется запахом кода, и я помню, как читал, что это распространенный антипаттерн. Однако я не могу найти никаких ссылок по этому поводу.

Итак, считается ли try / catch для каждого отдельного оператора, который вызывает и исключение, анти-шаблоном, и какие аргументы подтверждают это?


Сконструированный пример: (Не относится к исходной проблеме, поэтому, пожалуйста, не обращайте внимания на другие проблемы с этим примером, поскольку он просто создан для иллюстрации того, что я имею в виду.)

public int foo()
{
    int x, y = 7;

    try
    {
        x = bar(y);
    }
    catch(SomeException e)
    {
        return 0;
    }

    try
    {
        y = baz(x,y);
    }
    catch(SomeOtherException e)
    {
        return 0;
    }

    /* etc. */

    return y;
}

(Предположим, что здесь уместно перехватывать оба исключения, т.е. мы знаем, что с ними делать, и уместно вернуть 0 в обоих случаях.)


person Bjarke Freund-Hansen    schedule 17.08.2011    source источник
comment
При написании примера довольно очевидно, что это антипаттерн. Однако мне все же нужен хороший источник или аргумент в пользу этого.   -  person Bjarke Freund-Hansen    schedule 17.08.2011


Ответы (6)


Я не могу предоставить вам авторитетный источник, но это основные принципы обработки исключений:

  • исключения следует перехватывать в том месте, где их можно правильно обработать, и
  • вы не должны проглатывать исключения - всегда должен быть (по крайней мере) след выброшенного исключения, поэтому минимум - это зарегистрировать его, чтобы, по крайней мере, у разработчиков была возможность заметить, что произошло что-то плохое. Это приводит к третьему пункту:
  • исключения следует использовать только для сигнализации об исключительных плохих событиях, а не для управления выполнением программы.

Есть много более ранних сообщений о SO, посвященных этому, например Лучшие практики управления исключениями в JAVA или C #.

person Péter Török    schedule 17.08.2011

Моя первая реакция была, что это плохо. (Также у меня изначально были аргументы об отлове исключений в целом, но я удалил их, чтобы сосредоточиться на примере, приведенном в вопросе.) Трудно судить о возврате 0 для исключения, потому что неизвестно, что делают bar и baz и что представляют исключения . Если вызывающей стороне действительно не нужно знать, что что-то пошло не так, и вызывающей стороне не нужно различать возвращаемое значение как условие ошибки и возвращаемое значение как данные, тогда это нормально. С другой стороны, если возникшие исключения - это проблемы с недоступностью ресурсов, лучше быстро потерпеть неудачу, чем пытаться продолжить.

Я сомневаюсь, что это хорошо, потому что, если для этого кода разумно возвращать 0 при исключении, было бы, по крайней мере, разумно, чтобы bar и baz возвращали 0 в первую очередь.

Чтобы решить проблему перехвата исключения для каждого отдельного оператора в целом (в отличие от немедленного возврата со значением по умолчанию, как в примере), это плохо, потому что, как только у вас возникло исключение, что-то пошло не так, и ваш метод или объект может быть в плохом состоянии. Цель обработки исключений - иметь средство выхода из контекста, в котором что-то пошло не так, и вернуться в место с известным состоянием. Если бы перехват каждого исключения и промахи был в порядке, все наши языки поддерживали бы `On Error Resume Next '.

person Nathan Hughes    schedule 17.08.2011
comment
Я не беспокоюсь о том, является ли возвращение 0 хорошей идеей или уместным является перехват исключений или их распространение. Я согласен с тем, что это действительно очень важные вопросы, но мой вопрос конкретно касается использования большого количества попыток / уловок для каждого отдельного оператора. Пример, как я писал, просто сконструированный. - person Bjarke Freund-Hansen; 17.08.2011
comment
@bjarkef: хорошо, ваш примерный код смутил меня, потому что он менее опасен, чем обычный случай, когда есть исключения и что-то происходит (это то, что я вижу повсюду в местах, где я работаю в последнее время :-(), я был обеспокоен тем, что я не был обращаясь к вашему конкретному вопросу. - person Nathan Hughes; 17.08.2011
comment
Извини за это. И да, общий случай просто поедания исключений также очень распространен здесь и определенно является анти-паттерном (с возможными огромными последствиями), но я знаю, как убедить своих коллег в том, что это неправильный способ сделать это. - person Bjarke Freund-Hansen; 17.08.2011
comment
@bjarkef: у меня есть ответ для более общего случая, который может быть применим, по адресу stackoverflow.com/questions/7001077/ - person Nathan Hughes; 17.08.2011

Дьявол кроется в деталях. Это зависит от контекста. Когда вы ожидаете этих исключений? Например, если SomeException указывает на нарушение бизнес-правил, которое является довольно распространенным, и возвращение 0 допустимо в этом контексте, я вижу, что приведенный выше код вполне действителен. Наличие блока try..catch рядом с вызывающим его методом - неплохая вещь, поскольку определение того, какой метод может вызвать исключение, может оказаться довольно сложной задачей, если вы заключите 10 методов в один гигантский try..catch.

Однако, если это исключения, которые указывают на то, что что-то пошло не так, и SomeException не должно возникать, то возврат «0», поскольку скрывается индикатор магической ошибки, потенциально может скрыть, что что-то пошло не так, это может быть кошмаром обслуживания для узнать, где возникла проблема.

Если исключение не является чем-то, что вы можете восстановить, вы можете также поместить его в предложение throws или обернуть его и повторно выбросить (если исключение зависит от реализации, я бы не стал просто помещать его в предложение throws, как вы не хотел бы загрязнять чистый интерфейс исключениями, специфичными для реализации). См. Также эту применимую статью SO о проверенных и непроверенных исключениях.

Отсутствие регистрации исключения также не обязательно плохо. Если исключение возникает очень часто и не является признаком того, что что-то пошло не так, тогда вы не захотите писать в журнал каждый раз, иначе вы «отравите журналы» (т. Е. Если 99% записей журнала связаны с SomeException, то какова вероятность того, что вы пропустите исключение в журнале и / или у вас закончится дисковое пространство, потому что вы получаете сообщение журнала 50 000 раз в день).

person beny23    schedule 17.08.2011

В данном примере аргумент состоит в том, что это избыточно. Вам нужен только один try/catch блок с одним return null.

person wilx    schedule 17.08.2011
comment
Я почти уверен, что компилятор пожалуется, если вы попытаетесь вернуть null из метода, возвращающего примитивный тип int. - person Anthony Grist; 17.08.2011
comment
@Anthony: Да, это исправлено. Как я уже писал, это просто сконструированный пример. - person Bjarke Freund-Hansen; 17.08.2011
comment
В указанном примере, какие исключения будут перехвачены или нет, будет зависеть от того, какой метод их выбрасывает. Использование одного гигантского улова потребует, чтобы все вложенные операторы имели одинаковую политику улавливания исключений. - person supercat; 08.01.2013

Думаю, ваш коллега прочитал старые передовые практики J2EE «Поймайте исключение как можно ближе к его источнику». а потом он переборщил с этим.

person Petteri H    schedule 17.08.2011

Я вообще-то не думаю, что это антипаттерн как таковой. Но это определенно не чистый код, что почти так же плохо.

Причина, по которой я не верю, что это антипаттерн, заключается в том, что это не кажется хорошей идеей для начала, а это требование для чего-либо, чтобы быть антипаттерном. Более того, это решение проблемы кодирования, тогда как антишаблоны - это общие (плохие) решения архитектурных проблем.

person hvgotcodes    schedule 17.08.2011
comment
Интересный момент, но как критерий достаточно субъективный. Очевидно, для коллеги OP это действительно показалось хорошей идеей ... Может быть, мы можем улучшить это так: антипаттерн - это решение проблемы в контексте, который многие считают хорошим, но тем не менее в корне ошибочен. - person Péter Török; 17.08.2011
comment
@peter right это полностью субъективно. Мне это просто кажется плохим стилем кодирования, большинство опытных разработчиков не стали бы этого делать - они использовали бы одну попытку / уловку с несколькими типами исключений и обрабатывали бы каждый тип, или они бы разделяли сложные вещи на отдельные методы. - person hvgotcodes; 17.08.2011
comment
действительно, мне это тоже кажется плохой идеей. Я имел в виду, что критерии антипаттерна субъективны, а не то, является ли это конкретное решение хорошей идеей или нет. ИМО, что его негодность можно обосновать достаточно объективными аргументами. - person Péter Török; 17.08.2011
comment
@ Питер, что ты имеешь в виду? Тем не менее, я рассматриваю этот конкретный случай как наличие 16 уровней вложенных if и циклов. Это просто не чисто. ИМХО, плохой код! = Антипаттерн, но это всего лишь семантика. - person hvgotcodes; 17.08.2011
comment
Кажется, я не могу четко объяснить, что я имею в виду, извините ... Я полностью согласен с вами в отношении (не) чистоты кода. Я хотел прокомментировать только последнее предложение вашего ответа, где вы даете критерии антипаттернов. - person Péter Török; 17.08.2011
comment
@peter, я согласен с вашим первым комментарием относительно определения антипаттерна. Но то, что у OP был коллега, который подумал, что это хорошая идея, не означает, что это антипаттерн (IMHO). Может быть, лучше сказать, что точка доступа - это проектный выбор для решения общей архитектурной проблемы. Эта проблема больше связана с проблемой кодирования, то есть с тем, как обрабатывать исключения. - person hvgotcodes; 17.08.2011