Как объединить проверку правильности и исключение в Java? - PullRequest
3 голосов
/ 18 января 2010

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

public class Something implements SomeInterface {

    // Other class stuff

    public void doTheThing(SomeParameter sp) throws SpecificRuntimeException {
        if(sp == null) throw new ParameterCannotBeNullException();
        if(sp.getNonZeroInt() == 0) throw new ShouldBeNonZeroException();
        if(!sp.someOtherCondition()) throw new SomeConditionNotMetException();

        ...
    }

    public boolean isValid(SomeParameter sp) {
       if(sp == null) return false;
       if(sp.getNonZeroInt() == 0) return false;
       if(!sp.someOtherCondition()) return false;

       ...
       return true;
    }
}

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

  1. в doTheThing() имеют строку if(!isValid(sp) throw new RuntimeException();
  2. , разделяющую часть, генерирующую исключение, в отдельный частный метод, скажем checkParameter() и в isValid() do: try { checkParameter(sp); return true; } catch (SpecificRunTimeException e) { return false; }

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

Каков наилучший способ рефакторинга этого кода, чтобы использовать ту же логику проверки достоверности?

Ответы [ 5 ]

2 голосов
/ 18 января 2010

Что делать, если вы создаете метод isValidParam, который возвращает компонент типа:

class ValidBean {
  private boolean isValid;
  private Exception exceptionOnInvalid;
  public ValidBean(boolean isValid, Exception exceptionOnInvalid) {...}
  // accessors
}

С методом:

private ValidBean isValidParam(SomeParameter sp) {
       if (sp == null) {
          return new ValidBean(false, new ParameterCannotBeNullException());
       }
       if (sp.getNonZeroInt() == 0) { 
         return new ValidBean(false, new ShouldBeNonZeroException());
       }
       if (!sp.someOtherCondition()) {
          return new ValidBean(false, new SomeConditionNotMetException());
       }
       …
       return new ValidBean(true, null);
    }

И затем вы делаете:

public boolean isValid(SomeParameter sp) {
 return isValidParam(sp).isValid();
}

А

public void doTheThing(SomeParameter sp) throws SpecificRuntimeException {
  ValidBean validbean = isValidParam(sp); 
  if (! validBean.isValid()) {
      throw validBean.getExceptionOnInvalid();
  }
  ...
}
1 голос
/ 20 января 2010

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

Ожидаете ли вы, что каждый пользователь isValid() будет использовать его для защиты последующего вызова на doTheThing()? Если это так, клиентский код будет более понятным, чтобы вместо этого использовать стандартную идиому try / catch, и вам может вообще не понадобиться метод isValid().

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

И если вы не видите, чтобы isValid () использовался таким образом, то просто реализуйте его самостоятельно с помощью try / catch; Я знаю, что это грязно, но это лучше, чем альтернативы!

1 голос
/ 18 января 2010

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

private boolean isValidInternal(SomeParameter sp, boolean throwIfInvalid)
    throws SpecificRuntimeException

Затем вы вызываетеisValidInternal с true из метода doTheThing и false из общедоступного метода isValid.

Редактирование: А затем для реализации isValidInternal у вас есть логика для проверки достоверности один раз, но либо возвращает false, либо выдает исключение в соответствии с флагом throwIfInvalid.

0 голосов
/ 18 января 2010

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

P.S .: Вы не должны использовать runtime типы исключений в объявлении throws метода. Но вы должны указать эти исключения во время выполнения в документации Javadoc @throws метода.

0 голосов
/ 18 января 2010

Ваше желание «сказать это один раз и только один раз» похвально. Хорошая мысль; это будет стоить усилий.

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

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

...