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

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

emailUtil.sendEmail(userId, "foo");

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

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

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

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

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

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


person SCdF    schedule 22.08.2008    source источник


Ответы (11)


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

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

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

throws SpecificException1, SpecificException2, SpecificException3 ...

генерал

throws GeneralException

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

person Mnementh    schedule 26.09.2008

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

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

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

public bool isEmailConfigured ()

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

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

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

person jm.    schedule 22.08.2008
comment
Вы также регистрируете свою серверную часть исключений? - person pjp; 07.08.2009
comment
@php: Да, хороший момент, если это серверный код. Для реальных клиентских приложений (не веб) у меня также обычно есть возможность регистрировать исключения на стороне клиента для отладки. - person jm.; 08.08.2009

@ Chris.Lively

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

person FlySwat    schedule 22.08.2008

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

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

person Thorbjørn Ravn Andersen    schedule 11.01.2009

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

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

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

person stimms    schedule 22.08.2008

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

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

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

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

person abigblackman    schedule 26.08.2008

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

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

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

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

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

ОБНОВЛЕНИЕ: объединение ответов

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

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

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

person NotMe    schedule 22.08.2008
comment
Исключения очень полезны и при правильном использовании делают код более читабельным. Производительность языковых функций, таких как исключение, меняется с каждым выпуском современных виртуальных машин, поэтому вам нужно каждый раз измерять ее по условиям if (которые вы явно должны использовать для проверки результата метода). Проверки if для проверки результатов методов загромождают ваш код и усложняют его поддержку. Не поймите меня неправильно - иногда статус-объекты - хорошее решение. Особенно, если вы обрабатываете несколько ошибок (как вы упомянули), они более полезны, чем исключения. Но решение должно быть хорошо продуманным. - person Mnementh; 14.08.2009

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

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

person dlinsin    schedule 22.08.2008

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

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

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

person Telcontar    schedule 22.08.2008

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

person Jim    schedule 26.08.2008

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

throw new exception("WhatCausedIt")

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

person niklasfi    schedule 22.08.2008
comment
Вместо кода в исключении, переданном в качестве параметра, вы можете создать подкласс исключения и использовать instanceof. - person Mnementh; 14.08.2009