How to bind checks in java

Consider a class like the following

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
    }

}

      

The problem is that the method check

above is too long and has many operators return

. I've been thinking about some refactorings, but I'm still not sure what to do. I was thinking about something like a chain. Then I would implement several checker classes that either call the next checker in the chain or return result

with the appropriate one errorMessage

.

But then I had an even better idea (at least I think so): Why not behave like java 8? I thought to use something like Try

- Success

- Failure

-Pattern. But I have no idea how to implement this. I am thinking of something like:

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

      

The idea would be this: when it check

fails, it will behave like Optional.map()

and return something like Optional.EMPTY

(or in this context something like Failure

). When check

successful, it should continue and do the next check (return a Success

).

Do you have any experience with this?

+3


source to share


2 answers


When we think about validation, it is usually a composite template. He is generally portrayed as:

If so, do SOMETHING .

And as you enforce, you want to chain multiple checkers in a chain to perform validation in your scope, you can implement the Chain of Responsibility pattern .

Consider this:

You can have an object Result

that can contain an error message as well as a simple true / false.

You can have an object Validator

that does any validation and returns an instance 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 test "X" with multiple checkers, you introduce a validation rule, which is nothing more than a collection of objects Validator

, being an instance of itself Validator

. However, you can no longer use the object Result

to check the result of your rule check. You will need a composite object Result

that can store the results as {Validator=Result}

. Doesn't this seem like an implementation HashMap<Validator, Result>

? Yes, because it is.



Now you can implement your own Rule

and CompositeResult

how:

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 this! 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 the example in my public repository so you can play around.

+4


source


You can use Hibernate Validator (or another implementation of JSR 303/349/380 Bean Validation Standards).

An example of a user class that throws an exception when instantiated with invalid arguments. You can check all inline constraints in 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 validation 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 the message:

java.security.InvalidParameterException: Invalid class com.foo.bar.User failed Property "firstName" error message "cannot be null"

Remember to include the dependency in your pom.xml (if you are using Maven)

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

      

0


source







All Articles