2012-01-26 3 views
12

Я нехороший программист, и до сих пор я неправильно обрабатывал ошибки (например, просто ловил java.lang.Exception, печатал сообщение отладки и двигался дальше). Когда я «обрабатываю» их, просто закрывать компилятор.Обработка ошибок - это приличный шаблон?

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

Предположим, у меня есть блок кода, который делает следующее:

... 
x.method1(); // throws ExceptionTypeA 
    ... 
y.method2(); // throws ExceptionTypeB 
    ... 
z.method3(); // throws ExceptionTypeC 
    ... 
x.method4(); // throws ExceptionTypeA (again) 
    ... 

Из того, что я собрал, правильный способ справиться с этим:

try { 
     ... 
    x.method1(); // throws ExceptionTypeA 
     ... 
    y.method2(); // throws ExceptionTypeB 
     ... 
    z.method3(); // throws ExceptionTypeC 
     ...  
    x.method4(); // throws ExceptionTypeA (again) 
     ... 
} catch (ExceptionTypeA e) { 
    // do something about condition A 
} catch (ExceptionTypeB e) { 
    // do something about condition B 
} catch (ExceptionTypeC e) { 
    // do something about condition C 
} 

Это кажется довольно просто я, но, похоже, становится беспорядочным, когда у меня длинный блок кода, который вызывает различные ошибки. Кажется, я завершаю только одну гигантскую попытку/поймать весь мой метод! Альтернативой представляется следующее:

try { 
     ... 
    x.method1(); // throws ExceptionTypeA 
     ... 
} catch (ExceptionTypeA e) { 
    // do something about condition A 
} 

try { 
     ... 
    y.method2(); // throws ExceptionTypeB 
     ... 
} catch (ExceptionTypeB e) { 
    // do something about condition A 
} 

try { 
     ... 
    z.method3(); // throws ExceptionTypeC 
     ... 
} catch (ExceptionTypeC e) { 
    // do something about condition C 
} 
try { 
     ... 
    x.method4(); // throws ExceptionTypeA 
     ... 
} catch (ExceptionTypeA e) { 
    // do something about condition A 
} 

Это выглядит действительно противно. В таких случаях, я рассматривал делать что-то вроде следующего:

private void doSomething() throws exceptionTypeA, exceptionTypeB, exceptionTypeC { 
     ... 
    x.method1(); // throws ExceptionTypeA 
     ... 
    y.method2(); // throws ExceptionTypeB 
     ... 
    z.method3(); // throws ExceptionTypeC 
     ... 
    x.method4(); // throws ExceptionTypeA (again) 
     ... 

} 

public void doSomething_andHandleErrors() { 
    try { 
     this.toSomething(); 
    } catch (ExceptionTypeA e) { 
     // do something about condition A 
    } catch (ExceptionTypeB e) { 
     // do something about condition B 
    } catch (ExceptionTypeC e) { 
     // do something about condition C 
    } 
} 

... а затем просто призывая doSomething_andHandleErrors(); снаружи. Является ли это «хорошей» практикой? Я попадаю в какой-то анти-шаблон?

Спасибо!

+0

Я бы переместил это на http://codereview.stackexchange.com –

ответ

8

Основное различие между вашим первым и вторым примером заключается в том, как вы справляетесь с самой ошибкой. Это транзакция? В первом примере y.method2() не будет выполняться, если x.method1() выдает исключение. В вашем втором примере это возможно в зависимости от того, что делает обработка ошибок.

Оба они являются достойными шаблонами, это дело делового случая, требуемого здесь. Вы хотите, чтобы исключение было передано вызывающему абоненту, чтобы они могли его обработать? Вы хотите сделать что-то еще из-за ошибки?

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

+1

Согласовано, первое или второе, в зависимости от желаемой обработки. В-третьих, не очень хорошо. – CPerkins

+1

Или попробуйте использовать ресурсы вместо блоков finally. – biziclop

+0

Да, если вы используете Java 7, вы можете использовать try-with-resources вместо этого, как сказал biziclop. Вы не указали, какую версию java вы использовали. – Michael

0

Я бы предпочел ваш первый пример, если между вызовами метода существует не более нескольких строк. В противном случае ваш второй пример подойдет лучше. Я никогда не видел шаблон в последнем примере, никогда в нашей кодовой базе.

1

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

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

boolean success = false; 
try { 
    success = doStuff(); 
} catch(...) { 
    ... 
} 

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

private boolean doStuff() { 
    try { 
     ...do stuff... 
     return true; 
    } catch(SomeException ex) { 
     ...fidget around... 
     return false; 
    } 
} 

И вы просто назвать его так:

boolean success = doStuff(); 

Как и в сторону, Java 7 помогает вам обработки исключений много, вы можете catch multiple exceptions in one catch block, or catch and rethrow neatly. Это также помогает вам do away with catch blocks altogether для таких вещей, как закрытие соединений. Если нет другого фактора, удерживающего вас, я бы подумал о том, чтобы переключиться на него.

0

Хорошо, что вы пытаетесь создать чистый код.

ИМХО, то, что вы делаете, является чрезмерно чрезмерным. Предполагая, что вам нужно обрабатывать исключения, все, что вы делаете, это создание другого вызова метода. Вам все равно нужен блок try/catch. Я бы просто сделал то, что вы назвали «правильным способом», чтобы справиться с этим.

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

0

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

Сохранение обработки ошибок, отделенных от остальной части кода, как правило, является хорошей практикой. Он сохраняет другой код в чистоте.

+0

Не могли бы вы пояснить, что вы подразумеваете под «если исключения распространяются на RuntimeException»? – loneboat

+0

Проверяемые исключения должны быть рекламированы при каждом способе, который они могут быть выброшены внутри, что сильно загромождает код. Для исключений времени выполнения не требуется реклама, поэтому, если вы выбрали некоторые пользовательские исключения в своих методах, я бы удостоверился, что они являются исключениями времени исполнения. – fivedigit

+0

Прохладный, спасибо!(текст наполнителя) – loneboat

0

Как ваш первый, так и третий пример кажутся мне лучшим способом справиться с такими ситуациями, и именно так я обычно занимаюсь обработкой иногда избыточного количества исключений, которые мой код может бросить. Лично я предпочитаю третий вариант, его гораздо более организованный и лаконичный и не попадает в какой-либо анти-шаблон, который я знаю. Второй вариант просто уродлив, и его следует избегать, это просто большая трата пространства, так как вам не нужно такое большое количество предложений try.

2

Точка обработки исключений заключается в том, что вы должны продолжить движение, даже если вы столкнулись с исключением. Первый путь и третий способ в основном одинаковы. Если в method1() возникает исключение, вы сразу же выйдете из всего родительского метода, даже если не попытаетесь выполнить method2() и другие. Второй метод может казаться сначала загроможденным, но на самом деле является способом его выполнения.

Даже лучше, чем это было бы, чтобы обрабатывать исключения, которые вы ожидаете, что он будет использовать сам метод и вернуть значение по умолчанию, которое позволит продолжить выполнение без нарушения бизнес-логики или вызвать несогласованность.

EDIT:

Пример преимущества при использовании 2 метода:

Предположим, что вы делаете текстовый анализатор и ожидает в формате DD-MM-YYYY с date. Но при разборе вы обнаружите, что получаете date в формате DD-MON-YYYY. Эти типы исключений синтаксического анализа могут обрабатываться и до сих пор разрешать дальнейшее выполнение.

0

Если есть некоторые исключения, которые выбрасываются только один раз только один блок кода, и если место обработки исключений не влияет на бизнес-логику, я предпочитаю, чтобы справиться с ним на месте.

Но если же исключение может быть выброшен из нескольких мест, я хотел бы справиться с этим в конце концов, как ваш надлежащим образом.

Добавление дополнительного вызова метода выглядит немного неуклюжим для меня, потому что вы не избегаете проблемы, все, что вы делаете, размещает его где-то в другом месте.

Важная вещь отсутствует здесь finally блок, который я считаю необходимым, независимо от стиля обработки исключений.

Конечно, это личный выбор, я не думаю, что нет правильных или неправильных ответов.

1

Это действительно зависит от того, где (на каком уровне) вы хотите catch это исключение.

Метод, который генерирует исключение, просто означает, что «я не хочу иметь дело с этим исключением/проблемой, пусть кто-нибудь еще поймает» его. Чистый код должен появиться после этого способа мышления; должен ли я поймать здесь или нет ...

В вашем последнем случае, если вы повторите эти исключения, это означало, что вам не понадобятся объекты x, y, z во время обработки ошибок, поскольку они, скорее всего, будут недоступны ,

+0

Хороший вопрос о сфере применения в последнем примере. Благодаря! – loneboat

1

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

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

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

Смежные вопросы