Проверенные и непроверенные исключения в слое обслуживания - PullRequest
16 голосов
/ 06 июля 2011

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

UserService.get(userId);

Я недавно подтолкнул к изменению этого API или добавлению нового API, который вместо этого выдает исключения.Последовали дебаты о проверенных и непроверенных исключениях.

Принимая к сведению разработчиков JPA / Hibernate и др., Я предположил, что непроверенные исключения могут быть наиболее подходящими.Мой аргумент заключается в том, что нельзя ожидать, что пользователи API оправятся от этих исключений, и в 99% случаев мы в лучшем случае можем уведомить пользователя приложения о возникновении некоторой ошибки.

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

Почему разработчики таких проектов, как JPA / EJB и Hibernate, выбрали для использования модель исключений без контроля?Есть ли для этого очень хорошее оправдание?Какие плюсы / минусы.Должны ли разработчики, использующие эти фреймворки, по-прежнему обрабатывать исключения времени выполнения, близкие к тому, где они создаются с помощью чего-то вроде оболочек адаптера?

Надеюсь, ответы на эти вопросы помогут нам принять «правильное» решение относительно нашего собственного уровня обслуживания.

Ответы [ 5 ]

25 голосов
/ 06 июля 2011

Хотя я согласен с мнением о том, что непроверенные исключения создают более удобный API, на мой взгляд, это не самое главное преимущество.Вместо этого это:

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

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

  • Ласточка. Поймайте исключение и полностью игнорируйте его.Продолжайте, как будто ничего не произошло, хотя приложение сейчас находится в нестабильном состоянии.
  • Регистрируйте и глотайте. Перехватите исключение и зарегистрируйте его, думая, что теперь мы несем ответственность.Затем продолжайте, как будто ничего не произошло.
  • Mystery default. Перехватите исключение и установите для некоторого поля какое-то значение по умолчанию, обычно не сообщая об этом пользователю.Например, если вы не можете загрузить роли некоторых пользователей, просто выберите роль с низким уровнем привилегий и назначьте ее.Пользователь задается вопросом, что происходит.
  • По умолчанию глупая / опасная тайна. Перехватите исключение и установите для некоторого поля какое-нибудь действительно плохое значение по умолчанию.Один пример, который я видел в реальной жизни: не может загружать роли пользователя, поэтому продолжайте в том же духе и возьмите лучшее (т.е. дайте ему роль с высокими привилегиями, чтобы никому не доставлять неудобств).
  • Искаженный отчет. Разработчик не знает, что означает исключение, и поэтому просто выдвигает свою идею.IOException становится «Не удается подключиться к серверу», даже если установление соединения не имеет ничего общего с проблемой.
  • Маска через широкий перехват и неверный отчет. Попробуйте очиститькод, перехватив Exception или (тьфу) Throwable вместо двух проверенных исключений, которые на самом деле выдает метод.Код обработки исключений не пытается отличить (скажем) проблемы доступности ресурсов (например, некоторые IOException s) от ошибок прямого кода (например, NullPointerException).Действительно, оно часто выбирает одно из исключений произвольно и искажает каждое исключение как принадлежащее этому типу.
  • Маска через огромную попытку и неверный отчет. Вариант предыдущей стратегии - поместить целоекуча объявляющих исключений вызовов в области одного большого блока try, а затем перехватывает либо Exception, либо Throwable, потому что больше ничего не будет обрабатывать все создаваемые исключения.
  • Абстракция-Подходящее повторное отбрасывание. Возврат исключения, даже если исключение не подходит для абстракции (например, повторное выбрасывание исключения, связанного с ресурсом, из интерфейса службы, который должен скрывать ресурс).
  • Возврат безобертывание. Извлеките исключение (либо не проверено, либо проверено на соответствие абстракции), но просто отбросьте вложенное исключение, которое дало бы кому-нибудь шанс выяснить, что происходит.response. Ответить на нефатальное исключение, выйдя из JVM.(Спасибо этому сообщению в блоге за это.)

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

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

2 голосов
/ 06 июля 2011

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

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

1 голос
/ 06 июля 2011

Я вижу проверенное / непроверенное исключение в следующих ракурсах (принимая подсказку из стандартного JDK)

  1. Используйте проверенное исключение, если ваш код / ​​библиотека зависит от какого-либо ресурса, который является внешним дляJVM (например, .file / socket / remote api и т. д., т. е. JVM не контролирует его).Ваш код ДОЛЖЕН обрабатывать все возможные ошибки, которые могут возникнуть из-за источникаЭто сделано для того, чтобы убедиться, что ваша программа устойчива и изящно дает сбой в случае, если что-то не так с этим ресурсом.

  2. Непроверенное исключение - это условия серьезной ошибки ИЛИ реальные БАГИ в вашем коде, которые обычноне следует обрабатывать ПОСЛЕ того, как это произошло.Вы можете исправить свой код, чтобы эта ошибка не отображалась в первую очередь.(Уравнение НЛП, приведение класса, деление на ноль и т. Д.)

0 голосов
/ 06 июля 2011

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

    try
    {
        File file = new File("file");
        FileReader fileReader = new FileReader(file);
        BufferedReader bufferedReader = new BufferedReader(fileReader);
        String temp = "";
        StringBuilder result = new StringBuilder("");
        while ((temp = bufferedReader.readLine()) != null)
        {
            result.append(temp);
        }
        System.out.println(result);
    }
    catch (FileNotFoundException e)
    {
        e.printStackTrace();
    }
    catch (IOException e)
    {
        e.printStackTrace();
    }

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

Блоки Catch, которые содержат несколько исключений, в некоторой степени уменьшают этот беспорядок

0 голосов
/ 06 июля 2011

Обычно рекомендуется генерировать исключение RuntimeException, когда ваш код на более высоком уровне не может его обработать и ничего не может сделать с исключением.

В вашем случае ваш API вернет некоторый результат, если результат успешен, и должен вернуть null, если не найдено никаких результатов, а затем выдать исключение, если что-то пошло не так при выполнении операции.

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

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

Например, У меня есть код, который похож на -

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

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

Спасибо, Sathwick

...