How to chain checks in java

前端 未结 2 670
难免孤独
难免孤独 2021-01-06 08:11

Consider a class like the following

public class MyClass {

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

    public          


        
相关标签:
2条回答
  • 2021-01-06 08:36

    When we think about validation, it is generally a Composite pattern. It broadly is depicted as:

    If THIS is valid, then do SOMETHING.

    And as you are imposing, you want to chain up multiple checkers in a chain to perform validation in their area, you can implement a Chain of Responsibility pattern.

    Consider this:

    You can have a Result Object, that can contain a message about failure as well as simple true/false.

    You can have a Validator Object, that does whatever validation is required and returns an instance of 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);
    }
    

    Now when you say that you want to validate 'X' using multiple checkers, you're imposing a Validation Rule that is nothing but a collection of Validator objects while being an instance of Validator itself. That being said, you can no more use the Result object to check the validation result of your rule. You will need a composite Result object that can keep the results as {Validator=Result}. Doesn't it look like an implementation of HashMap<Validator, Result>? Yes, because it is.

    Now you can implement your Rule and CompositeResult as:

    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();
        }
    
    }
    

    And that's it! Now, to implement your checkers:

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

    And it's time to do the validation:

    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");
    

    Easy and neat.

    I have uploaded an example in my public repo for you to play around.

    0 讨论(0)
  • 2021-01-06 08:41

    You could use Hibernate Validator (or another implementation of the JSR 303/349/380 Bean Validation standards).

    Example user class that throws an exception when being instantiated with invalid arguments. You can check all the built in constraints 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
        }
    

    And the validator 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() + " ' ");
          }
        }
    }
    

    And message:

    java.security.InvalidParameterException: not valid class com.foo.bar.User failed property ' firstName ' failure message ' may not be null '

    Don't forget to include the dependency in your pom.xml (if you use Maven)

    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>5.4.1.Final</version>
    </dependency>
    
    0 讨论(0)
提交回复
热议问题