2017-04-05 39 views
3

链检验器将像下面如何用Java

public class MyClass { 

    private Integer myField; 
    private Result result; 
    // more global variables 

    public MyResult check(Integer myParameter) { 
     init(myParameter); 

     if (myField < 0) { 
      result.setErrorMessage("My error message"); 
      return result; 
     } 

     // a lot more 'checks' like above where something may be written 
     // to the error message and the result gets returned. 
    } 

    private void init(Integer myParameter) { 
     result = new Result(); 
     result.setExistsAnnouncement(/*search a certain object via crudService with myParameter*/); 
     // initialize other global variables including myField 
    } 

} 

问题一类是上面的check方法是太长,有许多return语句。我想到了一些重构,但我仍然不确定要做什么。我在想像一个连锁模式。然后,我会执行几个检查员类,他们或者调用链中的下一个检查器,或者返回result与相应的errorMessage

但是后来我有了一个更好的主意(至少我这么认为):为什么不像java 8那样表现?我想过使用像Try - Success - Failure - 模式。但我不知道如何实现这一点。我想到的是这样的:

entrancePoint.check(firstChecker) 
    .check(secondChecker) 
    .check // and so on 

的想法是:当check失败时,它会表现得像Optional.map()和返回类似的Optional.EMPTY(或在这方面有点像Failure)。当check成功时,它应该继续下一步检查(返回Success)。

你有这样的经验吗?

+0

我以前做过同样的事情,但没有链接。我做到这一点的方式是1.)创建一个'Checkable'接口,它包含一个方法'check()',它需要抛出一些'CheckFailedException',以及这个接口的一些具体实现。 2.)在'MyClass'中创建'List '变量,并且'MyClass'中的check()遍历每个checkable元素并在其上调用check()。 3.)使用构造函数或setter方法初始化要执行的检查列表(我使用的是spring,所以我是这么做的)。 – Jay

+0

是的,这可能有效。但我认为那里有更好的解决方案。目前我仍然试图通过Try-Failure-Success-Pattern来完成此项工作,如下所示:https://dzone.com/articles/whats-wrong-java-8-part-iv。区别在于:在链接他们做例外...我使用此尝试成功失败实现btw:https://gist.github.com/mariofusco/8287951 – Chris311

+0

你可以使用Hibernate验证器,如果你在编译时知道约束条件? http://hibernate.org/validator/ –

回答

4

当我们想到的验证,它通常是一个复合模式。它大致描述为:

如果这是有效的,那么做一下SOMETHING

而且,由于您正在施加压力,您希望链条上的多个检查器在其区域内执行验证,您可以实施责任链模式。

考虑一下:

你可以有一个Result对象,它可以包含一个有关故障信息以及简单的真/假。

您可以有一个Validator对象,该对象不需要验证,并返回Result的实例。

public interface Result { 
    public boolean isOk(); 
    public String getMessage(); 
} 

// We make it genric so that we can use it to validate 
// any type of Object that we want. 
public interface Validator<T> { 
    public Result validate(T value); 
} 

现在,当你说你要验证“X”使用多个棋子,你堂堂一个验证规则这不过是Validator对象的集合,同时的Validator一个实例本身。话虽如此,你不能再使用Result对象来检查规则的验证结果。您将需要一个可以保留结果为{Validator=Result}的复合对象Result。它看起来不像HashMap<Validator, Result>的执行吗?是的,因为它是。

现在你可以实现你的RuleCompositeResult为:

public class Rule extends ArrayList<Validator> implements Validator { 

    public Rule(Validator<?> ... chain) { 
     addAll(Arrays.asList(chain)); 
    } 

    public Object validate(Object target) { 
     CompositeResult result = new CompositeResult(size()); 
     for (Validator rule : this) { 
      Result tempResult = rule.validate(value); 
      if (!tempResult.isOk()) 
       result.put(rule, tempResult); 
     } 
     return result; 
    } 
} 

public class CompositeResult extends HashMap<Validator, Result> implements 
     Result { 

    private Integer appliedCount; 

    public CompositeResult(Integer appliedCount) { 
     this.appliedCount = appliedCount; 
    } 

    @Override 
    public boolean isOk() { 
     boolean isOk = true; 
     for (Result r : values()) { 
      isOk = r.isOk(); 
      if (!isOk) 
       break; 
     } 
     return isOk; 
    } 

    @Override 
    public String getMessage() { 
     return toString(); 
    } 

    public Integer failCount() { 
     return size(); 
    } 

    public Integer passCount() { 
     return appliedCount - size(); 
    } 

} 

就是这样!现在,为了实现你的棋子:

public class Checker1 implements Validator<Integer> { 
    /* Implementation */ 
} 

public class CheckerN implements Validator<Integer> { 
    /* Implementation */ 
} 

,它的时间做了验证:

Validator<Integer> checkingRule = new Rule(new Checker1(), new CheckerN()); 
CompositeResult result = checkingRule.validate(yourParameter); 
if (result.isOk()) 
    System.out.println("All validations passed"); 
else 
    System.out.println(result.getFailedCount() + " validations failed"); 

简单利落。

我上传了一个example in my public repo供您玩耍。

+0

非常好的解决方案!只有一个区别:你所有的验证器都被执行,而我想在第一次检查失败后返回。但可以轻松调整您的“验证”以满足我的要求。 – Chris311

+0

@ Chris311你是对的。如果规则的任何组件失败,您都可以抛出异常,但我太懒惰,无法实现该规则。 – Jay

+0

那么我宁愿使用'break'。 – Chris311

0

您可以使用Hibernate Validator(或其他实现的JSR 303/349/380 Bean验证标准)。

示例用户类在使用无效参数实例化时抛出异常。您可以检查所有的内置限制in the docs

public final class User { 

     @NotNull 
     private final String username; 

     @NotNull 
     private final String firstName; 

     @NotNull 
     private final String lastName; 

     public User(String username, String firstName, String lastName) { 
     this.username = username; 
     this.firstName = firstName; 
     this.lastName = lastName; 

     MyValidator.validate(this); 
     } 
     // public getters omitted 
    } 

而且验证器类:

import java.security.InvalidParameterException; 
import java.util.Set; 

import javax.validation.ConstraintViolation; 
import javax.validation.Validation; 
import javax.validation.Validator; 
import javax.validation.ValidatorFactory; 

public final class MyValidator { 

    public static void validate(Object object) { 

     ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); 
     Validator validator = factory.getValidator(); 

     Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object); 

     if (!constraintViolations.isEmpty()) { 
      ConstraintViolation<Object> firstViolation = constraintViolations.iterator().next(); 

      throw new InvalidParameterException("not valid " 
      + object.getClass() 
      + " failed property ' " + firstViolation.getPropertyPath() + " ' " 
      + " failure message ' " + firstViolation.getMessage() + " ' "); 
     } 
    } 
} 

和消息:

java.security.InvalidParameterException:无效类 com.foo .bar.User失败的属性'firstName'失败消息'可能不为空'

不要忘记,包括the dependency在你的pom.xml(如果你使用Maven)

<dependency> 
    <groupId>org.hibernate</groupId> 
    <artifactId>hibernate-validator</artifactId> 
    <version>5.4.1.Final</version> 
</dependency>