Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to chain 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 check method above is way too long and has many return statements. I thought of some refactorings but am still unsure what to do. I was thinking about something like a chain-pattern. I would then implement several checker-classes who either call the next checker in the chain or return result with a corresponding errorMessage.

But then I had an even better idea (at least I think so): Why not behave like java 8? I thought of using something like Try-Success-Failure-Pattern. But I have no clue how to realize this. I am thinking of something like:

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

The idea would be: When check fails, it would behave like Optional.map() and return something like an Optional.EMPTY (or in this context something like a Failure). When check is successful it should go on and do the next check (return a Success).

Do you have any experience in doing something like this?

like image 841
Chris311 Avatar asked Feb 05 '23 17:02

Chris311


1 Answers

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.

like image 101
Jay Avatar answered Feb 07 '23 08:02

Jay