Подтвердить что ты не робот

Что такое "Проверка ошибок" (Java)?

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

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

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

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

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

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

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

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

4b9b3361

Ответ 1

  • Проверка ошибок флага

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

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

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

Это опция по умолчанию.

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

От Практика Java → Проверено или исключено исключение:

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

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

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

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

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

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

Ответ 2

Сделать исключение - лучший способ.

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

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

Ответ 3

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

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

Ответ 4

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

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

Ответ 5

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

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

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

Ответ 6

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

Ответ 7

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

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

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

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

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

И вообще, попробуйте сделать это простым.

Ответ 8

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

 doSomething(WithX x ) throws BusinessRuleViolatedException 

Ответ 9

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

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

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

"Шаблон списка валидатора"

В качестве примера позвольте сначала описать в коде анти-шаблон "Validation Flag", а затем мы преобразуем его в шаблон "список проверки".

public Optional<String> checkForErrorsUsingFlags( 
       ObjectToCheck objToCheck ) {
  // the small series of checks and if statements represent the
  // anti-pattern. Hard to test and many other problems crop up.
  String errMsg = checkForError1( objToCheck );
  if(errMsg != null ) {
    return Optional.of(errMsg);
  }
  errMsg = checkForError2( objToCheck );
  if(errMsg != null ) {
    return Optional.of(errMsg);
  }
  return Optional.empty();
}

/**** client usage  ****/
ObjectToCheck obj = doSomethingToReadInput(obj);
Optional<String> error = checkForErrors( obj);
if (error.isPresent()) {
  // invalid input, throw object away and request input again
} else {
  // do stuff, we have a valid input
}

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

/** The common validator interface each validator will use */
private interface MyValidator {
  public boolean isValid(ObjectToCheck obj);
  public String getErrorMessage(ObjectToCheck obj);
}

 // this method should look familiar to the above, now we 
 // have a list of validators as an additional parameter
public Optional<String> checkForErrors( ObjectToCheck objToCheck,
     List<MyValidator> validators ) {
  for(MyValidator validator : validators ) {
    if (!validator.isValid(objToCheck)) {
      String errMsg = validator.getErrorMessage(objToCheck);
      return Optional.of(errMsg);
    }
  }
  return Optional.empty();
}

/****** client usage  *****/
// now in this pattern, the client controls when the validators
// are created, and which ones are used.
MyValidator validator1 = new MyValidator() {
  @Override
  public boolean isValid(ObjectToCheck obj) {
    return checkForError1( objToCheck ) != null;
  }
  @Override
  public boolean getErrorMessage(ObjectToCheck obj) {
    return checkForError1( objToCheck );
  }
}
// note: above we call checkForError1 twice, not optimal.
// typical in real examples this can be avoided,
// and the error message generation logic split from the detection
// logic often simplifies things.
MyValidator validator2 = new MyValidator() { ... }

List<MyValidator> validators = 
  ImmutableList.of( validator1, validator2);
Optional<String> error = checkForErrors(objToCheck, validators);
if (error.isPresent()) {
    // invalid input, throw object away and request input again
} else {
    // do stuff, we have a valid input
}

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