【问题标题】:How to chain checks in java如何在java中进行链式检查
【发布时间】:2017-04-05 08:40:40
【问题描述】:

考虑一个像下面这样的类

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-Pattern 之类的东西。但我不知道如何实现这一点。我正在考虑类似的事情:

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

这个想法是:当check 失败时,它的行为类似于Optional.map() 并返回类似于Optional.EMPTY 的内容(或者在这种情况下类似于Failure)。当check 成功时,它应该继续进行下一次检查(返回Success)。

你有做这种事情的经验吗?

【问题讨论】:

  • 我以前做过同样的事情,但没有链接。我这样做的方式是 1.) 创建一个接口Checkable,其中包含一个需要抛出一些CheckFailedException 的方法check(),以及该接口的一些具体实现。 2.) 在MyClass 中创建一个List&lt;Checkable&gt; 变量,MyClass 中的check() 遍历checkable 的每个元素并在其上调用check()。 3.)使用构造函数或setter方法初始化要执行的检查列表(我使用的是spring,所以我这样做了)。
  • 是的,这可能有效。但我认为那里有更好的解决方案。目前,我仍在尝试使用 Try-Failure-Success-Pattern 来做到这一点,就像这里所做的那样:dzone.com/articles/whats-wrong-java-8-part-iv。不同之处在于:在链接中,他们是为异常做的......我正在使用这个 Try-Success-Failure-Implementations 顺便说一句:gist.github.com/mariofusco/8287951
  • 如果你知道编译时的约束,你能使用 Hibernate Validator 吗? hibernate.org/validator
  • @aepure:检查不仅与数据库相关的事情有关。
  • @Chris311 我喜欢你的实现。事实上,我也可能在我的应用程序中使用它。我的想法是,您想连锁检查,对吗?你可以尝试做类似Try&lt;Checkable&gt; 的事情,但我担心这会造成更多的开销和混乱,或者在最坏的情况下使事情变得混乱......

标签: java refactoring chaining


【解决方案1】:

当我们考虑验证时,它通常是一种复合模式。它大致被描述为:

如果这是有效的,那就做点什么

正如您所施加的,您希望将多个检查器链接在一个链中以在其区域内执行验证,您可以实现 责任链 模式。

考虑一下:

您可以有一个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 对象的集合,同时是 @ 的一个实例987654327@ 本身。话虽如此,您不能再使用Result 对象来检查您的规则的验证结果。您将需要一个复合Result 对象,该对象可以将结果保持为{Validator=Result}。它看起来不像HashMap&lt;Validator, Result&gt; 的实现吗?是的,因为它是。

现在您可以将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 供你玩。

【讨论】:

  • 非常好的解决方案!只有一个区别:您的所有验证器都已执行,而我想在第一次检查失败后返回。但是可以很容易地调整您的validate 以满足我的要求。
  • @Chris311 你是对的。如果规则的任何组件失败,您可以抛出异常,但我懒得实现。
  • 我宁愿使用break
【解决方案2】:

您可以使用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”失败消息“可能不为空”

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多