累积验证违规的设计模式

Ser*_*gey 5 java validation design-patterns exception-handling

让我们想象一下,我们有一个进程,它接受以下类型的数据:

{"date":"2014-05-05", "url":"http://some.website.com","counter":3}
Run Code Online (Sandbox Code Playgroud)
  • 这些数据应该被正式验证:值date应该是一个可解析的日期,url也应该符合正常的 url 语法。
  • 此外,这些数据应该在逻辑上得到验证:date应该在未来,url应该是一个可访问的地址,返回200 OK.

为了使它干净,必须将这两个验证例程分成不同的单元(类、实用程序等)。然而,所需的最终行为必须让用户清楚地了解数据中存在的所有违规行为。就像是:

{"Errors":[ "Specified date is not in the future",//Formal validation failed "Specified URL has invalid syntax"//Logical validation failed ]}

  • 我已经看到了所需行为的一些实现,但它们使用了那些利用Error对象的实现,并且充满了像Error.hasErrors()or之类的检查 error==null,这看起来并不优雅。
  • 我还看到了 的实现javax.validation,它同时为您提供所有领域的所有违规行为。可以为内容验证实施相同的方法,但我不确定这是执行此操作的最佳方法。

问题:处理各种性质的多个异常/违规的最佳做法是什么?

UPD:答案的简短摘要: collect Violations, build an Exception,包含它们的上下文、原因和描述,使用拦截器进行渲染。请参阅答案中的参考链接:

http://beanvalidation.org/1.0/spec/ JSR 303 规范

http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/validation.html Spring Bean 验证

http://docs.oracle.com/javaee/6/tutorial/doc/gircz.html Java EE 验证

使用哪种设计模式进行验证

为什么不使用异常作为常规控制流?

Ita*_*man 3

您可以执行以下操作:

定义一个抽象Check类,如下:

public abstract class Check {
  private final List<Check> subChecks = new ArrayList<Check>();

  public Check add(Check subCheck) { subChecks.add(subCheck); return this }

  public void run(Data dataToInspect, List<Error> errors) {
    Error e = check(dataToInspect);
    if (e != null) {
       errors.add(e);
       return;
    }
    for (Check subCheck : subChecks) {
      subCheck.run(dataToInspect, errors);
    }
  }

  // Returns null if dataToInspect is OK.
  public abstract Error check(Data dataToInspect);
}
Run Code Online (Sandbox Code Playgroud)

classData是保存数据的类(需要检查)。可以是字符串、JSON 对象,等等。

Error表示在数据中检测到的问题应该大致类似于:

public class Error {
  private String problem;
  public Error(String problem) { this.problem = problem }
  public String getProblem() { return problem }
  // maybe additional fields and method to better describe the detected problem...
}
Run Code Online (Sandbox Code Playgroud)

然后,您就有了针对数据运行检查的代码:

public class Checker {

   private final List<Error> errors = new ArrayList<Error>();
   private final List<Check> checks = new ArrayList<Check>();

   public Checker() {
     checks.add(new DateIsParsableCheck().add(new DateIsInTheFurutreCheck());
     checks.add(new UrlIsWellFormed().add(new UrlIsAccessible());

     checks.add();
     ..
   }

   public void check(Data d) {
     for (Check c : checks) {
       Error e = c.run(d, errors);
       if (e != null) 
         errors.add(e);
     }
   }
}
Run Code Online (Sandbox Code Playgroud)

稍微改变了我原来的答案。在当前的答案中存在子检查的概念:如果调用的检查x有一个调用的子检查y,则y仅当检查成功时该检查才会运行x。例如,如果日期不可解析,则没有必要检查它是否是将来的。

在你的情况下,我认为所有/大多数逻辑检查应该是正式检查的子检查。