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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With