如何在 java 中链接检查
How to chain checks in java
考虑如下 class
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
语句。我想到了一些重构,但仍然不确定该怎么做。我在想类似链式模式的东西。然后我会实现几个 checker-classes,它们要么调用链中的下一个检查器,要么用相应的 errorMessage
调用 return result
。
但后来我有了一个更好的主意(至少我是这么认为的):为什么不像 java 8 那样呢?我想到了使用 Try
-Success
-Failure
-Pattern 之类的东西。但我不知道如何实现这一点。我在想类似的东西:
entrancePoint.check(firstChecker)
.check(secondChecker)
.check // and so on
这个想法是:当 check
失败时,它会表现得像 Optional.map()
和 return 像 Optional.EMPTY
(或者在这种情况下像 Failure
).当 check
成功时,它应该继续进行下一次检查 (return a Success
)。
你有过这样的经历吗?
您可以使用 Hibernate Validator (or another implementation of the JSR 303/349/380 Bean 验证标准)。
示例用户 class 在使用无效参数实例化时抛出异常。您可以检查所有内置约束 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
}
验证器class:
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: not valid class
com.foo.bar.User failed property ' firstName ' failure message ' may not be null '
不要忘记在您的 pom.xml 中包含 the dependency(如果您使用 Maven)
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.1.Final</version>
</dependency>
当我们想到验证时,它通常是一种复合模式。它大致描述为:
如果这是有效的,那么做一些事情。
当你强加时,你想在一条链中链接多个检查器以在他们的区域执行验证,你可以实现一个责任链模式。
考虑一下:
您可以有一个 Result
对象,它可以包含有关失败的消息以及简单的 true/false。
您可以有一个 Validator
对象,它可以执行任何需要的验证,并且 returns 是 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>
的实现吗?是的,因为它是。
现在您可以将 Rule
和 CompositeResult
实现为:
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给你玩。
考虑如下 class
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
语句。我想到了一些重构,但仍然不确定该怎么做。我在想类似链式模式的东西。然后我会实现几个 checker-classes,它们要么调用链中的下一个检查器,要么用相应的 errorMessage
调用 return result
。
但后来我有了一个更好的主意(至少我是这么认为的):为什么不像 java 8 那样呢?我想到了使用 Try
-Success
-Failure
-Pattern 之类的东西。但我不知道如何实现这一点。我在想类似的东西:
entrancePoint.check(firstChecker)
.check(secondChecker)
.check // and so on
这个想法是:当 check
失败时,它会表现得像 Optional.map()
和 return 像 Optional.EMPTY
(或者在这种情况下像 Failure
).当 check
成功时,它应该继续进行下一次检查 (return a Success
)。
你有过这样的经历吗?
您可以使用 Hibernate Validator (or another implementation of the JSR 303/349/380 Bean 验证标准)。
示例用户 class 在使用无效参数实例化时抛出异常。您可以检查所有内置约束 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
}
验证器class:
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: not valid class com.foo.bar.User failed property ' firstName ' failure message ' may not be null '
不要忘记在您的 pom.xml 中包含 the dependency(如果您使用 Maven)
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.1.Final</version>
</dependency>
当我们想到验证时,它通常是一种复合模式。它大致描述为:
如果这是有效的,那么做一些事情。
当你强加时,你想在一条链中链接多个检查器以在他们的区域执行验证,你可以实现一个责任链模式。
考虑一下:
您可以有一个 Result
对象,它可以包含有关失败的消息以及简单的 true/false。
您可以有一个 Validator
对象,它可以执行任何需要的验证,并且 returns 是 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>
的实现吗?是的,因为它是。
现在您可以将 Rule
和 CompositeResult
实现为:
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给你玩。