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

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

emailUtil.sendEmail(userId, "foo");

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

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

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

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

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

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

Ответ 1

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

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

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

throws SpecificException1, SpecificException2, SpecificException3 ...

общий

throws GeneralException

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

Ответ 2

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

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

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

public bool isEmailConfigured()

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

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

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

Ответ 3

@Chris.Lively

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

Ответ 4

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

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

Ответ 5

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

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

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

Ответ 6

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

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

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

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

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

UPDATE:, сочетающий ответы

@Jonathan: Я хотел сказать, что могу оценить действие, в этом случае отправить электронное письмо и отправить несколько причин сбоя. Например, "плохой адрес электронной почты", "пустое сообщение" и т.д.

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

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

Ответ 7

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

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

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

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

Ответ 8

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

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

Ответ 9

Несмотря на то, что вы можете различать выполнение кода, смотрящего на исключение, не имеет значения, было ли это сделано с помощью режима "catch exceptionType" или "if (...) else... режим кода исключения"

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

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

Ответ 10

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

Ответ 11

Я бы просто ходил

throw new exception("WhatCausedIt")

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