Вы пишете исключения для конкретных проблем или общие исключения?

StackOverflow https://stackoverflow.com/questions/21652

  •  09-06-2019
  •  | 
  •  

Вопрос

У меня есть код, который передает идентификатор пользователя утилите, которая затем отправляет этому пользователю электронное письмо.

emailUtil.sendEmail(userId, "foo");

public void sendEmail(String userId, String message) throws MailException {
    /* ... logic that could throw a MailException */
}

MailException могло быть выброшено по ряду причин: проблемы с адресом электронной почты, проблемы с шаблоном почты и т. д.

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

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

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

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

Это было полезно?

Решение

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

Примером Java-API является IOException, у которого есть подклассы, такие как FileNotFoundException или EOFException (и многие другие).

Таким образом, вы получаете преимущества обоих, у вас нет таких предложений, как:

throws SpecificException1, SpecificException2, SpecificException3 ...

генерал

throws GeneralException

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

Другие советы

В моем коде я обнаружил, что БОЛЬШИНСТВО исключений проникает на уровень пользовательского интерфейса, где они перехватываются моими обработчиками исключений, которые просто отображают сообщение пользователю (и записывают его в журнал).В конце концов, это неожиданное исключение.

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

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

public bool isEmailConfigured()

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

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

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

@Chris.Lively

Вы знаете, что можете передать сообщение в своем исключении или даже «коды состояния».Здесь вы изобретаете велосипед.

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

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

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

  • Приложение имеет высокую доступность
  • Отправка электронной почты особенно важна
  • Объем приложения невелик, и отправка электронной почты составляет большую его часть.
  • Приложение будет развернуто на удаленном сайте, и вы получите только логи для отладки.
  • Вы можете восстановить некоторые исключения, инкапсулированные в mailException, но не другие.

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

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

В зависимости от проблемы вы можете генерировать разные исключения.напримерОтсутствует адрес электронной почты = ArgumentException.

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

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

Вместо использования исключений я предпочитаю возвращать список объектов состояния из методов, при выполнении которых могут возникнуть проблемы.Объекты состояния содержат перечисление серьезности (информация, предупреждение, ошибка и т. д.), имя объекта состояния, например «Адрес электронной почты», и читаемое пользователем сообщение, например «Плохо отформатированный адрес электронной почты».

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

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

Другая причина использования списка объектов состояния заключается в том, что выявление нескольких ошибок (например, во время проверки) НАМНОГО проще.В конце концов, вы можете создать только одно исключение, которое необходимо обработать, прежде чем двигаться дальше.

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

ОБНОВЛЯТЬ: объединение ответов

@Джонатан:Моя точка зрения заключалась в том, что я могу оценить действие, в данном случае отправив электронное письмо, и отправить обратно несколько причин сбоя.Например, «неправильный адрес электронной почты», «пустой заголовок сообщения» и т. д.

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

Изобретение велосипеда..возможно.Однако большинству приложений следует анализировать всю транзакцию, чтобы предоставить пользователю максимально возможную информацию.Представьте, что ваш компилятор остановился при первой же ошибке.Затем вы исправляете ошибку и снова нажимаете компиляцию, но она снова останавливается из-за другой ошибки.Какая боль в заднице.На мой взгляд, это именно проблема с созданием исключений и, следовательно, причина, по которой я сказал использовать другой механизм.

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

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

Хотя вы можете различать выполнение кода, глядя на исключение, не имеет значения, выполняется ли оно с помощью «режима иерархии типа исключения catch» или «if (...) else... режима кода исключения»

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

Когда я использую библиотеку, и ее методы просто запускают «Исключение», я всегда задаюсь вопросом:Что может вызвать это исключение? Как должна реагировать моя программа? Если есть Javadoc, возможно, причина будет объяснена, но в большинстве случаев Javadoc отсутствует или исключение не объяснено.Слишком больших накладных расходов можно избежать с помощью WellChossenExceptionTypeName

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

я бы просто прошел мимо

throw new exception("WhatCausedIt")

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top