Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Design pattern for cumulative validation violations

Let's imagine, that we have a process, which accepts data of the following type:

{"date":"2014-05-05", "url":"http://some.website.com","counter":3}
  • This data should be validated formally: value of date should be a parseable date, url should also conform the normal url syntax.
  • Also, this data should be validated logically: date should be in the future, url should be an accessible address, returning 200 OK.

To make it clean, one must separate those two validation routines into different units (classes, utils, whatever). The desired final behaviour, however, must give user clear understanding of ALL violations, that are present in data. Something like:

{"Errors":[ "Specified date is not in the future",//Formal validation failed "Specified URL has invalid syntax"//Logical validation failed ]}

  • I have seen some implementations of the required behaviour, but they use those make use of Error objects and are full of checks like Error.hasErrors() or error==null, which does not look elegant.
  • I have also seen the implementation of javax.validation, which gives you all violations on all field at once. Same approach could be implemented for content validation, but I am not sure, that this is the best way to do this.

Question: what is the best practice for handling multiple exceptions/violations of various nature?

UPD: short digest of answers: collect Violations, build an Exception, containing their context, cause and description, use an interceptor to render. See reference links from answers:

http://beanvalidation.org/1.0/spec/ JSR 303 specification

http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/validation.html Spring Bean Validation

http://docs.oracle.com/javaee/6/tutorial/doc/gircz.html Java EE validation

Which Design Pattern To Use For Validation

Why not use exceptions as regular flow of control?

like image 941
Sergey Avatar asked May 13 '14 07:05

Sergey


People also ask

What are the pros and cons of the validation pattern?

There are good and bad sides to this pattern: it’s familiar and well tried; unfortunately, it results in a potential explosion of code volume (if you have 30 validation checks, you’ll need 30 classes), which makes it difficult to read. It also doesn’t deal with the scenario whereby one validation condition depends on the success of another.

What is data validation?

Data validation is performed before registration or deletion of the object. There are a set of rules to be checked before the object can be registered and another set of rules for deletion.

What is the best way to Approach validation?

When doing validation, There’s a number of options to how you approach it: you could simply have a series of conditional statements testing logical criteria, you could follow the chain of responsibility pattern, use some form of polymorphism with the strategy pattern, or even, as I outline below, try using the builder pattern.

How do I chain multiple validators in Java?

When we want to chain additional validator we call add (handler: ValidationHandler<InputType>) which links our new handler to the previous handler and sets it as the new tailValidationHandler. Returning self is what allows us to build up our handlers.


2 Answers

You can do the following:

define an abstract Check class, as follows:

public abstract class Check {
  private final List<Check> subChecks = new ArrayList<Check>();

  public Check add(Check subCheck) { subChecks.add(subCheck); return this }

  public void run(Data dataToInspect, List<Error> errors) {
    Error e = check(dataToInspect);
    if (e != null) {
       errors.add(e);
       return;
    }
    for (Check subCheck : subChecks) {
      subCheck.run(dataToInspect, errors);
    }
  }

  // Returns null if dataToInspect is OK.
  public abstract Error check(Data dataToInspect);
}

class Data is the class holding the data (that needs to be checked). Can be a String, a JSON object, what have you.

class Error represents a problem detected in the data should be roughly something like:

public class Error {
  private String problem;
  public Error(String problem) { this.problem = problem }
  public String getProblem() { return problem }
  // maybe additional fields and method to better describe the detected problem...
}

You then have code that runs the check against piece of data:

public class Checker {

   private final List<Error> errors = new ArrayList<Error>();
   private final List<Check> checks = new ArrayList<Check>();

   public Checker() {
     checks.add(new DateIsParsableCheck().add(new DateIsInTheFurutreCheck());
     checks.add(new UrlIsWellFormed().add(new UrlIsAccessible());

     checks.add();
     ..
   }

   public void check(Data d) {
     for (Check c : checks) {
       Error e = c.run(d, errors);
       if (e != null) 
         errors.add(e);
     }
   }
}

Slightly changed my original answer. In the current answer there is the notion of subchecks: if a check called x has a subcheck called y then the y check will run only if the x check succeeded. For instance, if the Date is not parseable there is no point to check it it is in the future.

In your case I think that all/most logical check should be sub-checks of a formal check.

like image 101
Itay Maman Avatar answered Oct 16 '22 13:10

Itay Maman


I don't think there is a best practice, because it depends on what you try to achieve. In my opinion, exceptions and their messages should not be used to be displayed directly to the user. Exceptions are way too technical and do depend heavily on the context where they get thrown.

Hence, my approach would be to design a container type which collects all the exceptions thrown by your validations. Those exceptions should preserve as much of the context as possible (not in form of an exception message, but in form of fields passed into the constructor). Provide getter methods to make those fields (properties) accessible. When rendering the view, you may iterate over all entries of your container and generate a proper, human readable, i18n message.

Here is some pseudo-code as requested by the comment of @AlexandreSantos. It is not polished nor proven, just my first draft. So do not expect excellent design. It's just an example how it could be implemented / designed:

public static void main(String[] args) {
    Violations violations = new Violations();
    Integer age = AgeValidator.parse("0042", "age", violations);
    URL url = UrlValidator.parse("http://some.website.com", "url", violations);
}

// Validator defining all the rules for a valid age value
public class AgeValidator {

    // Collection of validation rules for age values
    private static final Collection<Validator<String>> VALIDATORS = ...;

    // Pass in the value to validate, the name of the field
    // defining the value and the container to collect all
    // violations (could be a Map<String, ValidationException>)
    //
    // a return value of null indicates at least one rule violation
    public static Integer parse(String value, String name, Violations violations) {
        try {
            for (Validator<String> validator : VALIDATORS) validator.validate(value);
        } catch (ValidationException e) {
            violations.add(name, e);
        }
        return violations.existFor(name) ? null : Integer.parseInt(value);
    }
}
like image 23
Harmlezz Avatar answered Oct 16 '22 14:10

Harmlezz