Подтвердить что ты не робот

Обработка ошибок - это приличный шаблон?

Я проницательный программист, и до сих пор я неправильно обрабатывал ошибки (например, просто перехватывал 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(); снаружи. Является ли это "хорошей" практикой? Я попадаю в какой-то анти-шаблон?

Спасибо!

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

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

EDIT:

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

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

Ответ 3

Обычно в блоке 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 или улавливать и реконструировать аккуратно. Это также поможет вообще отказаться от блоков catch для таких вещей, как закрытие соединений. Если нет другого фактора, удерживающего вас, я бы рассмотрел возможность переключения на него.

Ответ 4

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

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

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

Ответ 5

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

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

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

Ответ 6

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

Ответ 7

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

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

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

Ответ 8

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

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

Ответ 9

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

Ответ 10

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

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

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

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

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