I’ve lost count of the number of times I’ve seen code which fail-fast validates the state of something, using an approach like
public class PersonValidator { public boolean validate(Person person) { boolean valid = person != null; if (valid) valid = person.givenName != null; if (valid) valid = person.familyName != null; if (valid) valid = person.age != null; if (valid) valid = person.gender != null; // ...and many more } }
It works, but it’s a brute force approach that’s filled with repetition due to the valid check. If your code style enforces braces for if statements (+1 for that), your method is also three times longer and growing every time a new check is added to the validator.
Using Java 8’s new stream API, we can improve this by taking the guard condition of if (valid)
and making a generic validator that handles the plumbing for you.
import java.util.LinkedList; import java.util.List; import java.util.function.Predicate; public class GenericValidator<T> implements Predicate{ private final List<Predicate<T>> validators = new LinkedList<>(); public GenericValidator(List<Predicate<T>> validators) { this.validators.addAll(validators); } @Override public boolean test(final T toValidate) { return validators.parallelStream() .allMatch(predicate -> predicate.test(toValidate)); } }
Using this, we can rewrite the Person validator to be a specification of the required validations (or even just create a static field of type GenericValidator).
public class PersonValidator extends GenericValidator<Person> { private static final List<Predicate<Person>> VALIDATORS = new LinkedList<>(); static { VALIDATORS.add(person -> person.givenName != null); VALIDATORS.add(person -> person.familyName != null); VALIDATORS.add(person -> person.age != null); VALIDATORS.add(person -> person.gender != null); // ...and many more } public PersonValidator() { super(VALIDATORS); } }
PersonValidator
, and all your other validators, can now focus completely on validation. The behaviour hasn’t changed – the validation still fails fast. There’s no boiler plate, which is A Good Thing.
This one’s going in the toolbox.
UPDATE I saw a comment about this at when it was republished at JCG, and has been re-implemented with the suggestions there. There’s always something new to learn!
This is nice. Thanks a lot.
But i have a question. How do we know which test is failed. In most of the cases we should know the reason for the validation failure. isn’t it?