Гранулярность исключений

9

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

  • BadRequestException
  • AuthenticationFailureException
  • ProductNotFoundException

Каждый из них построен на основе кода ошибки, возвращенного из API.

Следуя преимуществам исключений, это кажется идиоматическим для Java. Однако мнение моих друзей не является чем-то необычным.

Есть ли предпочтительный способ с точки зрения читабельности кода и удобства использования API, или он действительно сводится к предпочтениям?


источник
Страница, на которую вы ссылаетесь, является, вероятно, лучшим окончательным ответом, который мы можем получить. Вы спрашиваете мнение, действительно. Я могу ответить с моим опытом и мнением, но это не объективный ответ.
Марстато
@marstato это честно. Я думаю, я как бы ищу оправдание в своей позиции. Я бы предпочел придерживаться того, что люди ожидают в библиотеках, которые я пишу, вместо того, чтобы следовать руководству, если это означает, что это облегчает использование моих вещей, понимаете?
Я абсолютно согласен. У меня тоже есть классы исключений. Кроме того, вы можете определить abstractи обобщить классы исключений с помощью методов получения, а затем заставить гранулярные расширять общие. Например AuthenticationFaliureException extends ClientErrorException. Таким образом, каждый пользователь может выбрать, как он хотел бы иметь дело с исключениями. Это больше работает, хотя, очевидно. Однако при написании приложения (вместо библиотеки) ИМХО это другая ситуация. В этом случае я бы не сделал исключения более детальными, чем вам нужно, для простоты.
Марстато
@marstato, это то, как я сейчас это реализую. Я рад, что вы согласны. Я оставлю вопрос открытым на всю ночь, но, пожалуйста, объедините его в пост, чтобы я мог хотя бы зеленый проверить вас

Ответы:

15

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

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

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

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

  • какое-нибудь приложение, где вы или команда имеете всю кодовую базу под вашим контролем?

  • или какой-то повторно используемый компонент для третьих лиц, где вы не знаете всех потенциальных абонентов?

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

  • недолговечный процесс приложения, когда в случае ошибки достаточно отобразить сообщение об ошибке для пользователя, а затем перезапустить процесс?

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

Док Браун
источник
3
«Вы действительно ожидаете, что вызывающая сторона вашего кода будет реагировать по-разному, с разным потоком контроля, на эти разные виды ошибок?» это отличный вопрос. Спасибо, я запомню это.
Это хороший ответ, я думаю, я бы еще больше подчеркнул необходимость позволить клиентскому приложению управлять дизайном, генерирующим исключения. Даже обобщения, которые вы считаете целесообразными для встраивания в иерархию исключений, могут не совпадать со способами, которыми ваше клиентское приложение (если оно не написано вами) может выбрать обобщение обработчика. Если у вас есть более одного клиентского приложения с различными потребностями, то, конечно, вы сами сможете их сбалансировать.
magicduncan
1

Ответ зависит от того, на каком уровне сообщений об ошибках мы говорим.

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

Если есть общеизвестное исключение для ссылки на null (NullReferenceException), вы не должны создавать свое собственное MyObjectIsNullException. Это просто добавило бы путаницу для человеческого переводчика, дополнительную вещь для изучения, которая ничего не проясняет.

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

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

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

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