Использовать одну и ту же логику предварительных условий метода между контроллерами и службами?

У меня есть Service и Controller .

У каждого метода в сервисе свои предусловия, например:

  public void doSomething(Parameter para1 , Parameter para2 ...) {
    if ( something wrong ) {
      throw new RuntimeException1();
    }
    if ( another thing wrong ) {
      throw new RuntimeException2();
    }
    // continue do something 
  }

А на уровне контроллера есть два метода: один — showForm(), который отображает форму для ввода пользователем; другой - doApplyForm(), который принимает форму и вызывает подстилающий слой Service.doSomething().

Ниже приведен псевдокод (я исключил некоторые коды BindingResult, attr.addFlashAttribute):

  @Injected Service service;

  public String showForm() {
    if ( something wrong ) {
      throw new RuntimeException1();
    }
    if ( another thing wrong ) {
      throw new RuntimeException2();
    }
    return "showForm";
  }

  public String doApplyForm(@Validated Form form) {
    try {
      service.doSomething(para1 , para2 ...);
      return "redirect:/";
    } catch (Exception e) {
      // error handling 
      return "redirect:/error";
    }
  }

Работает хорошо, но я не доволен. Внутри плохой запах.

Проблема в showForm() , который имеет те же предварительные условия, что и Controller.doSomething() .

Если Service.doSomething() добавит другие предварительные условия в будущем, Controller.showForm() должен будет внести соответствующие изменения.

Интересно, существуют ли какие-либо шаблоны проектирования или фреймворки для устранения такого неприятного запаха?

Приветствуются функциональные решения Java8.

Спасибо.


person smallufo    schedule 11.11.2015    source источник


Ответы (2)


Вы можете определить класс util с именем Preconditions и переместить туда всю свою логику проверки. Это распространенный шаблон, и существует ряд фреймворков, которые его используют. Например, Guava: документация по предварительным условиям.

По крайней мере, так ваш if (condition) throw new exception будет капсулирован и им будет легче управлять.

person Danail Alexiev    schedule 11.11.2015

Введите объект параметра для запроса на обслуживание и поместите логику проверки в объект запроса. Например.

public class DoSomethingRequest {

   private Parameter param1;
   private Parameter param2;


   public void validate(){
       if ( something wrong ) {
           throw new RuntimeException1();
       }
       if ( another thing wrong ) {
           throw new RuntimeException2();
       }
   }

}

Ваша служба будет более легкой

public void doSomething(DoSomethingRequest request) {
    request.validate();
}

так что контроллер

public String showForm() {
    DoSomethingRequest request = ... // obtained somehow
    request.validate();
    // ...
    return "showForm";
}

Это инкапсулирует предварительные условия метода службы в объект.

person René Link    schedule 12.11.2015