Что такое хороший Шаблон “Проверки ошибок” (Java)?

Я объясню, что я подразумеваю под входной проверкой ошибок.

Скажите, что у Вас есть функция doSomething(x).

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

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

  1. Отметьте проверку ошибок. Если doSomething(x) завершает успешно возврат null. Иначе это возвращает булевскую переменную или строку ошибки. Проблема: Побочные эффекты.

  2. Выдача исключения. Выдайте исключение если doSomething(x) встречается с ошибкой. Проблема: Если Вы выполняете проверку ошибок на параметры только, бросая IllegalArgumentExceptionкажется несоответствующим.

  3. Проверка входа до вызова функции. Если проверка ошибок только предназначена для параметров функции, то можно вызвать функцию блока проверки допустимости прежде, чем звонить doSomething(x) функция. Проблема: Что, если клиент класса забывает вызывать функцию блока проверки допустимости перед вызовом doSomething(x)?

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

17
задан aioobe 10 June 2010 в 13:31
поделиться

8 ответов

  1. Проверка ошибок по флагам

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

Пример из API: Если вы пытаетесь добавить объект в Set, который уже содержит другой объект, равный новому объекту, метод add как бы "проваливается" и указывает на это, возвращая false. (Обратите внимание, что мы находимся на уровне, на котором технически это даже не "ошибка"!)

2. Выброс исключения

Это вариант по умолчанию.

Вопрос в том, следует ли вам выбрать проверенное исключение (для которого вам нужно объявление throws или try/catch клаузулы) или непроверенное исключение (исключение, которое расширяет RuntimeException). Здесь есть несколько правил.

Из Java Practices -> Checked versus unchecked exceptions:

  • Unchecked exceptions: Представляют собой дефекты в программе (баги) - часто недействительные аргументы, переданные неприватному методу.

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

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

Если вам нужно бросить проверяемое исключение, вы можете A) создать свое собственное, расширив Exception, B) использовать какое-то существующее проверяемое исключение или C) "цепочку" исключений времени выполнения, например, IOException: throw new IOException(new IllegalArgumentException("reason goes here..."));

3. Проверка ввода перед вызовом функции

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

5
ответ дан 30 November 2019 в 13:53
поделиться

Выбросить исключение - лучший способ.

Если вы выполняете проверку ошибок только для параметров, бросать IllegalArgumentException кажется неуместным.

Почему? Это цель данного исключения.

6
ответ дан 30 November 2019 в 13:53
поделиться

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

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

3
ответ дан 30 November 2019 в 13:53
поделиться

Я согласен с выдачей исключений. Я хочу добавить еще один вариант, который сочетает в себе №2 и №3 - шаблон прокси . Таким образом, ваш код остается достаточно связным - проверка в одном месте и бизнес-логика в другом. Это имеет смысл, если у вас есть большой набор вызовов, которые необходимо проверить.

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

2
ответ дан 30 November 2019 в 13:53
поделиться

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

  • Пользовательский интерфейс (GUI): Я проверяю перед вызовом бизнес-методов, потому что пользователь хочет знать, что было не так.

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

1
ответ дан 30 November 2019 в 13:53
поделиться

Выбрасывать проверяемое исключение.

 doSomething(WithX x ) throws BusinessRuleViolatedException 
0
ответ дан 30 November 2019 в 13:53
поделиться

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

1
ответ дан 30 November 2019 в 13:53
поделиться

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

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

  2. Выбрасывать исключение, когда ваша функция работает с неопределенным поведением. Если у вас есть математическая функция, которая может работать только с положительными целыми числами, и кто-то передает -1, вы должны выбросить InvalidArguementException. Если вашей функции задан идентификатор продукта в базе данных, но продукт не может быть найден запросом, вы можете создать специальное исключение ProductNotFound.

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

Я обычно использую №2 и №3. Я какое-то время не писал код с флагами ошибок. Исключением может быть функция, возвращающая перечисление, где одно возможное значение указывает код ошибки. Это было обусловлено скорее бизнес-правилом, чем чем-либо еще.

И вообще, старайтесь не усложнять.

1
ответ дан 30 November 2019 в 13:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: