Jay
Jay

Reputation: 2454

Design question - java - what is the best way to doing this?

I have a design problem.

I have two data objects which are instances of say class A and class B. A and B don't have any behavior - they are java beans with getters and setters. I have a Validation interface and 10 implementations of it defining different Validations. I would like to specify in my properties file which Validation applies to which class. Something like this:

class A XYZValidation,ABCValidation

class B: ABCValidation, PPPValidation, etc

How do I write my Validation class so that it serves objects that are instances of Class A OR ClassB, or just about any other Class C that I might want to add in future?

interface Validation {
public boolean check(??);
}

> Just wanted to add this line to say thank you to all those who have responded to this post and to say that I am loving my time here on this amazing website. Stackoverflow rocks!

Upvotes: 7

Views: 694

Answers (7)

tputkonen
tputkonen

Reputation: 5739

Have you thought about using annotations to mark the fields you want to validate in your bean?

If you have 10 different validations you could specify 10 annotations. Then mark the fields using annotations:

@ValideStringIsCapitalCase
private String myString;

@ValidateIsNegative
private int myInt;

With reflection API iterate through all the fields and see if they are marked, something like this:

public static <T> validateBean(T myBean) throws IllegalAccessException {
    Field[] fields = myBean.getClass().getDeclaredFields();
    // This does not take fields of superclass into account
    if (fields != null) {
        for (Field field : allFields) {
            if (field.isAnnotationPresent(ValideStringIsCapitalCase.class)) {
                field.setAccessible(true);
                Object value = field.get(existingEntity);
                // Validate
                field.setAccessible(false);
            }
        }
    }
}

An option would be to mark the whole class with the validator you want to use.

EDIT: remember to include annotation:

@Retention(RetentionPolicy.RUNTIME)

for your annotation interface.

EDIT2: please don't modify the fields directly (as in the example above). Instead access their getters and setters using reflection.

Upvotes: 8

Reginaldo
Reginaldo

Reputation: 907

A Visitor pattern would solve this

Calling the Visitor Validator it's possible to have this:


public interface Validatable {
  public boolean validate(Validator v);

}

public interface Validator {
  public boolean validate(A a);
  public boolean validate(B b);
}

public class A implements Validatable {

  public boolean validate(Validator v){
    return v.validate(this);
  }

}

public class B implements Validatable {

  public void validate(Validator v) {
    return v.validate(this);
  }

}

// Default validator just doesn't know how to 
// validate neither A's, nor B's
public class GenericValidator implements Validator {

  public boolean validate(A a) {
    throw new UnsupportedOperationException("Cannot validate A");
  }

  public boolean validate(B b) {
    throw new UnsupportedOperationException("Cannot validate B");
  }
}

// since XYZValidation is supposed to run only on A's
// it only overrides A validation
public class XYZValidation extends GenericValidator {
  public boolean validate(A a) {
     // validate a
     return isVAlid(a);
  }
}

// since ABCValidation is supposed to run on A's and B's
// it overrides A and B validation
public class ABCValidation extends GenericValidator {
  public boolean validate(A a) {
     // validate a
     return isVAlid(a);
  }

  public boolean validate(B b) {
     // validate b
     return isVAlid(b);
  }
}


// since ABCValidation is supposed to run only on B's
// it overrides A only B validation
public class PPPValidation extends GenericValidator {
  public boolean validate(B b) {
     // validate b
     return isVAlid(b);
  }
}
 

Upvotes: 0

Huxi
Huxi

Reputation: 4311

First of all, I'd use the following interface

interface Validator {
    boolean isValid(Object object);
}

to implicitly document what the return value actually means.

Secondly, I'd suggest to document in the interface what behavior is expected if the Validator doesn't know how to handle the given instance.

interface Validator {
    /**
     * @return false if this validator detects that the given instance is invalid, true if the given object is valid or this Validator can't validate it.
     */
    boolean isValid(Object object);
}

That way, you'd simply have a List of Validators that you could throw your objects at.

The performance impact of incompatible Validators should be negligible if they are implemented properly, e.g. with an early instanceof.

On a side note, I'd use a List of Validators instead of a Set so you can order them according to complexity. Put the cheap (performance-wise) Validators at the start of the List as an optimization.

You could then use a general piece of code for validation, e.g.

public class Validators {
    public static boolean isValid(Object o, Collection<Validator> validators) {
        for(Validator current : validators) {
            if(!current.isValid()) return false;
        }
        return true;
    }
}

Depending on your use-case it might be a good idea to return something different than boolean in your interface. If you need information about what is wrong, e.g. to display it, you'd need to return that info instead.

In that case it might be a good idea to keep the above loop running so you'll get all validation errors instead of only the first.

Upvotes: 0

uudashr
uudashr

Reputation: 129

Did you mean:

public interface Validation<T> {
    boolean check(T object)
}

Upvotes: 2

objects
objects

Reputation: 8677

If you just want it to deal with any object then it'll be Object's that your interface

public boolean check(Object o);

Unless you want to use some marker interface to tag classes that are suitable for validation

Upvotes: 2

Fortega
Fortega

Reputation: 19702

something like this maybe?

interface Validation {
   public boolean check(Validatable x);
}

interface Validatable {
}


class A implements Validatable {
  ...
}

class Validator {
   public boolean validateObject(Validatable x){
      boolean validated = true;
      ... //read config file, check which validation classes to call
      //for each validation class v in the config file:
          if(!v.check(x)) validated = false;
      return validated;
   }
}

Upvotes: 3

digiarnie
digiarnie

Reputation: 23465

I've probably misunderstood the question but would something like this suffice:

public class ValidationMappings {
    private Map<Class, Class<Validation>[]> mappings = new HashMap<Class, Class<Validation>[]>();

    public ValidationMappings() {
            mappings.put(A.class, new Class[]{XYZValidation.class, ABCValidation.class});
            mappings.put(B.class, new Class[]{ABCValidation.class, PPPValidation.class});
    }

    public Class[] getValidators(Class cls) {
            if (!mappings.containsKey(cls)) return new Class[]{};
            return mappings.get(cls);
    }
}

When you want to get the list of validators for a particular class, you would then call getValidators(Class cls) and iterate over each validator and create an instance of each and call your check method.

Upvotes: 3

Related Questions