Validating value objects

Apply the Visitor pattern and reflection

In a business process, you often have attributes that must not be null and other attributes that could be optional. In the case of attributes that must be an instance, you have to implement a check similar to this:

if( attribute1 == null )
{
   throw new Attribute1IsNullException()
}

If the value object has N attributes, you will get something like this:

if( attribute1 == null )
{
   throw new Attribute1IsNullException()
}
if( attribute2 == null )
{
   throw new Attribute2IsNullException()
}
...
if( attribute N == null )
{
   throw new AttributeNIsNullException()
}

The result: A lot of if statements! And you have to type them all!

Now imagine that the number of validations increases from 10 to 25 because 15 new use cases must be implemented in the next iteration. Unnerving isn't it? An effective way to reduce these checks is to move all of them from the value object class into the value object's validating class.

At this point, you might recognize that you always perform the same checks. The only difference is the name and type of the attribute. In most cases, the type is not of interest because the compiler checks it. An important point is to be sure that the methods receiving the attribute's value all start with the same name. In our case above, this would be get.

Calling all the value object's getters by reflection is easy. If you use Eclipse, for example, you can generate the get/set methods for all attributes. So for our attribute1, the getter will be getAttribute1() and the setter, setAttribute1(Integer attributeValue), if attribute1 is an integer attribute. If these preconditions are given, you can think about a generic solution. This article explains how a generic solution could be realized using reflection and the Visitor pattern.

The framework's classes and interfaces

The following class diagram shows the relationships between the classes and interfaces used for building our generic validation framework.

Class diagram

Note: You can download these classes and interfaces from Resources.

Validateable interface

The Validateable interface complements the Visitable interface. The defined method validateWith() is the analog method to the accept() method in the Visitor pattern's Visitable interface. With the validateWith() method, you can validate the value object with different validators because this method takes an implementation of the IClassAttributeValidator interface as a parameter.

IClassAttributeValidator interface

The IClassAttributeValidator interface is the counterpart of the Visitor interface in the Visitor pattern. And its validate(AbstractParameter param) method is the counterpart of Visitor's visit(object SomeObject) method. The validate() method's AbstractParameter parameter type lets you access (validate) all parameters that are subtypes of the class AbstractParameter. In addition, using this interface in the validateWith() method as a parameter allows us to change the implementation of the used validator in the future for parameters that need a different validation—for example, testing the parameter attributes for a defined value range in addition to the null checks.

AbstractParameter

The AbstractParameter class implements the validateWith() method from the Validateable interface. This implementation is, as you can see in the code snippet below, very simple. The method just calls the validate() method in the given validator and submits the parameter object to the validator:

public void validateWith( IClassAttributeValidator validator ) throws Exception
{ 
   validator.validate( this );
}

Also, the AbstractParameter implements some often used additional methods. The protected method addOptionalMethod() lets all subtypes add an optional method to optionalGetAttributeMethods HashMap(). Deriving from AbstractParameter enables you to factor out such getters that may deliver null. As you can imagine, you can add optional methods, for example, in the constructor of the value object that derives from AbstractParameter.

The isOptionalMethod() method is used to verify whether the attribute should be checked.

The toString() method is implemented for convenience because value objects consist of many attributes. So in the subtypes of AbstractParameter, it's not necessary to do a lot of System.out.printlns in the implementation. This method also uses reflection to do its job.

GenericClassAttributeValidator

The GenericClassAttributeValidator class implements the IClassAttributeValidator interface with the method validate(). The class is implemented as a singleton. validate()'s implementation looks like this:

public synchronized void validate( AbstractParameter param ) throws AttributeValidatorException
{
   Class clazz = param.getClass(  );
   Method[] methods = clazz.getMethods(  );
   //Cycle over all methods and call the getters!
   //Check if the getter result is null.
   //If result is null throw AttributeValidatorException.
   Iterator methodIter = Arrays.asList( methods ).iterator(  );
   Method method = null;
   String methodName = null;
   while ( methodIter.hasNext(  ) )
   {
      method = (Method) methodIter.next(  );
      methodName = method.getName(  );
      if ( methodName.startsWith( "get" ) &&
         clazz.equals( method.getDeclaringClass(  ) ) &&
         !param.isOptionalMethod( methodName ) )
      {
         Object methodResult = null;
         try
         {
            methodResult = method.invoke( param, null );
         }
         catch ( IllegalArgumentException e )
         {
            throw new AttributeValidatorException( e.getMessage(  ) );
         }
         catch ( IllegalAccessException e )
         {
            throw new AttributeValidatorException( e.getMessage(  ) );
         }
         catch ( InvocationTargetException e )
         {
            throw new AttributeValidatorException( e.getMessage(  ) );
         }
         if ( methodResult == null )
         {
            String attributeName = methodName.substring( 3, 4 ).toLowerCase(  ) +
               methodName.substring( 4, methodName.length(  ) );
            String className = clazz.getName(  );
            className = className.substring( className.lastIndexOf( '.' ) + 1 );
            Integer errorNumber = new Integer( 1000 );
            throw new AttributeValidatorException( "Error: " + errorNumber + " "
                + attributeName + " in " + className +" is null!!!");
         }
      }
   }
}

First, as you can see in the code, we get all methods from the value object. After that, we cycle over this collection of methods. If the method starts with a get, is a subtype of AbstractParameter, and not optional, we call the getter via reflection and check its result. If the result is null, this is an error, and if not, everything is all right. The optional methods and the derived methods from the superclass are not called.

Testing our classes

Now that we have implemented all the classes and interfaces we need, we must do some tests to check if everything works. To do that, we implement a little test class with a main() method to start the test.

TestParameter

The TestParameter class is derived from AbstractParameter and includes some private attributes that should be validated: simply four Integer attributes.

Optional attributes

To verify that optional attributes are not tested, we define the getter for attribute testParam3 as optional. For this reason, we enter this getter in the map of optional methods in the superclass AbstractParameter via addOptionalMethod(methodName) in TestParameter's constructor.

How the validation framework works

For the test, we now fill the TestParameter class in the following way:

TestParameter param = new TestParameter( );
param.setTestParam1( new Integer( 1 ) );
param.setTestParam2( new Integer( 2 ) );
param.setTestParam3( new Integer( 3 ) );
param.setTestParam4( new Integer( 4 ) );

As you can see, four Integer attributes are filled with Integer 1, 2, 3, and 4. To validate we just have to call:

param.validateWith( GenericClassAttributeValidator.getInstance( ) );

This validation's output is:

testParam1: 1
testParam2: 2
testParam4: 4

The attribute testParam3 is not validated because we marked its getter as optional. All other attributes are validated and the result of the validation is okay. Now we want to set one of the attributes to null so that we can check if the validator will detect this error. Just comment out the line:

param.setTestParam2( new Integer( 2 ) );

After we restart, we receive the following output:

testParam1: 1
Error: testParam2 in TestParameter is null!!!
testParam4: 4

Now we can see that the validator has detected the unset attribute.

What if the attribute is a kind of Collection?

If the attribute is a Collection, it's also checked if the Collection is null. But perhaps checking whether the Collection is null is not what you want. In most cases, you want to know whether the objects in Collection are null. If Collection's implementation does not allow null objects, you don't need to care about those null objects that don't extend AbstractParameter in the GenericClassAttributeValidator. The additional code for a Collection holding objects that extend the AbstractParameter looks like this:

if ( methodResult instanceof Collection )
{
   Collection col = (Collection) methodResult;
   Iterator iter = col.iterator(  );
   Object subParam = null;
   while ( iter.hasNext(  ) )
   {
      subParam = iter.next(  );
      if ( subParam instanceof AbstractParameter )
      {
         AbstractParameter abstractParam = ( AbstractParameter ) subParam;
         abstractParam.validateWith( this );
      }
   }
}

All objects in the Collection that don't extend from the AbstractParameter class are not checked because we are going to use a Collection implementation that does not allow null objects. So the Collection implementation completes the check for us. If you decide to use an implementation that allows null objects, an additional null check is necessary for all other objects in the while loop:

else if( subParam == null )
{
   System.out.println( "Error: SubParameter not set in Collection!" );
}

Dependencies between values

In some cases, an attribute is optional only if another attribute of the value object has an assigned value. The "optionality" of attribute sometimesOptional depends on the value of the attribute actionType. Maybe action holds values that stand for the actions: for example addSomething = 1, updateSomething = 2, and deleteSomthing = 3. If action's value is 1 or 3, the sometimesOptional attribute is not optional. If the action value equals 2, it is optional. We must set the optionality of sometimesOptional when we set the value for actionType:

public void setActionType(int actionType)
{
   this.actionType = actionType;
   super.clearOptionalMethods( );
   switch( this.actionType )
   {
      case ActionParameter.ACTION_ADD :
         super.addOptionalMethod( "getSometimesOptional4" );
         super.addOptionalMethod( "getSometimesOptional5" );
         break;
      case ActionParameter.ACTION_UPDATE :
         super.addOptionalMethod( "getSometimesOptional1" );
         break;
      case ActionParameter.ACTION_REMOVE :
         super.addOptionalMethod( "getSometimesOptional1" );
         super.addOptionalMethod( "getSometimesOptional2" );
         super.addOptionalMethod( "getSometimesOptional3" );
         break;
       default :
         break;
   }
}

You can see that it's necessary to clear the list of optional methods because if you set the actionType more than once, more and more methods will be added as optional methods. Another solution involves implementing an AddActionParameter, an UpdateActionParameter, and a RemoveActionParameter, which all extend the AbstractParameter class. Then you won't need the attribute actionType! But maybe the class with the actionType attribute exists and has been used often. So to refactor the class easily, you must use the switch statement.

1 2 Page 1
Page 1 of 2