I have a class that transforms a complex model, for example an abstract syntax tree or intermediate model. The model can be either valid, invalid or partially invalid, i.e. it contains errors but some parts of it are valid and can be processed further.
The most simple error reporting is to use exceptions:
public class Transformer {
public TargetModel transform(SourceModel model)
throws InvalidModelException {
// ...
}
}
Obviously this doesn't allow to report multiple errors (at least if you don't attach further information to the exception) and exceptions should be for exceptional situations.
Martin Fowler addressed this problem in his article Replacing Throwing Exceptions with Notification in Validations. If you apply his method to the problem you have something like this:
public abstract class Problem {
// ...
}
public final class ProblemA {
// ...
}
public final class ProblemB {
// ...
}
public class Transformer {
public static final class TransformationResult {
private Collection<Problem> problems;
private Optional<TargetModel> targetModel;
// ...
}
public TransformationResult transform(SourceModel model) {
// ...
}
}
You then either use the visitor pattern or instanceof
checks to distinguish the errors. It's a trade-off between type-safety and verbosity.
Another possible solution would be to use the observer pattern:
public class Transformer {
public interface ProblemListener {
public void onProblemA(...);
public void onProblemB(...);
// ...
}
public void addProblemListener(ProblemListener listener) {
// ...
}
public void removeProblemListener(ProblemListener listener) {
// ...
}
public TargetModel transform(SourceModel model) {
// ...
}
}
Using the observer pattern has the advantage that you don't have to accumulate the errors in memory and that excessive instanceof
checks or visitors are not needed. On the other hand it obscures the control flow even more than the visitor pattern.
I think all solutions do not lead to readable code. In a functional language and with enough memory I would use the second solution but in Java structural pattern matching either entails instanceof
checks or use of visitor pattern which are both verbose. Is there anything that I have overlooked? Or implications that I did not consider? What is your experience with any of the solutions?