views:

162

answers:

4

my company processes paper forms so that written data is being put to databases. every form has a set of fields. i'm writing a simple internal app to define the form's fields and that includes field's validations. i'm thinking of classes structure now and i can think of two approaches:

  1. i could write a set of classes, each representing a single validation, e.g. RegexValidation with its Pattern property, LengthValidation with it's Min and Max and so on. However if any new validation type appears in the future, i will probably have a lot of places in the project where i will have to write new code. i don't really think there will be any new validation types but it's an assumption i shouldn't make as a programmer.

  2. the second approach is to create an abstract class Validation and all the validator classes will inherit from it. They will have a dictionary that maps arguments names to their values (so that LengthValidation will have items with keys "max" and "min" and RegexValidation will have items with keys "pattern" and so on). It looks pretty nice, but there is a major problem - when writing data to the database i have to know which validation is which one so that i could put the values in appropriate places. I could use a strategy design pattern and let Validation class have Save method so that every class will know how it is saved in the database. but on the other hand i don't want the validation classes to be responsible for writing data to database.

so what would you suggest? any other ideas?

+1  A: 

Writing a class hierarchy is good. Validation has numerous subclasses of validation.

When a new validation shows up, nothing old needs to get rewritten. The old stuff is still in place and still works. EmailAddressValidation doesn't change when someone asks for a new SomeNewIDNumberValidation.

If you find a bug, of course, a class gets rewritten.

When you add a new validation, you will not "have a lot of places in the project where i will have to write new code". You will have the new validation and the application that needs the new validation. Nothing to it. This is the great beauty of OO software -- adding a subclass breaks nothing.

You need all of your validator subclasses to have a single, uniform "is_valid" method. This is how you make them polymorphic. They don't need to have the same constructors, just the same validator.

Additionally, you need each validator object to return the "cleaned, ready for use by the database" value. Some validators can (and should) clean their input. Removing spaces from credit card numbers. Removing any of the odd punctuation people put in phone numbers, for example.

You want to build a composite object that contains a number of validators. I'll use Python syntax, because it's simpler.

class SomeValidatableObject( object ):
    field1 = ThisValidator( size=12, otherSetting="B" )
    field2 = RegexValidator( expr=r"\d{3}-\d{2}" )
    field3 = SSNValidator()
    field4 = YetAnotherValidator( someSetting=9, size=14 )

All the constructors are specific to the validation. All of the field-by-field validations are a common is_valid method. Each validator can have a clean_data method.

Your composite object can have a save method that builds the resulting, valid object from the values of all the various fields.

[I didn't design this, I'm describing the validators that the Django Project uses.] If you look at the Forms documentation, you'll see how they solve this problem.

S.Lott
you don't read carefully S.Lott. it's really nice what you've written but it's useful if you need validators to validate values. but it's NOT useful if you use them only to DEFINE validators and write them to database, because these classes shouldn't be responsible for writing to a database.
agnieszka
Um... I don't see where these classes wrote anything to a database. That's not something I said. What should I change to clarify this for you? What words confused you?
S.Lott
"when writing data to the database i have to know which validation is which one so that i could put the values in appropriate places. I could use a strategy design pattern and let Validation class have Save method so that every class will know how it is saved in the database." by author
agnieszka
he clearly needs some classes that hold defined data and write this data to database. this data includes validation definitions - and your answer is about validators that are used for validating
agnieszka
@agnieszka: validation and database saving are unrelated. Sometimes people want to combine them. That's a simple **extension** to this design. I omitted it because it's pretty obvious how to take cleaned data from the validators and do a database insert.
S.Lott
A: 

grails has a bunch of validators: http://www.grails.org/Validation+Reference. consider building a hierarchy of validators and using a static map from member name to validator. you may want to think about i18n on the error messages when a constraint is violated.

Ray Tayek
A: 

I don't know if this exactly fits into your question but...

I've recently worked with Microsofts Patterns and practices Validation Application Block - I really love the implementation http://msdn.microsoft.com/en-us/library/cc309509.aspx

Perhaps if you looked at what they are offering it would give you some ideas.

BPAndrew
A: 

After a couple of projects with the validation logics I came to the the third option.

Generic rule is defined as:

///<summary>
/// Typed delegate for holding the validation logics
///</summary>
///<param name="obj">Object to validate</param>
///<param name="scope">Scope that will hold all validation results</param>
///<typeparam name="T">type of the item to validate</typeparam>
public delegate void Rule<T>(T obj, IScope scope);

where IScope is

/// <summary>
/// Concept from the xLim2. That's simple nesting logger that is used by
/// the validation rules. 
/// </summary>
public interface IScope : IDisposable
{
 /// <summary>
 /// Creates the nested scope with the specified name.
 /// </summary>
 /// <param name="name">New name for the nested scope.</param>
 /// <returns>Nested (and linked) scope instance</returns>
 IScope Create(string name);

 /// <summary>
 /// Writes <paramref name="message"/> with the specified
 /// <paramref name="level"/> to the <see cref="IScope"/>
 /// </summary>
 /// <param name="level">The level.</param>
 /// <param name="message">The message.</param>
 void Write(RuleLevel level, string message);

 /// <summary>
 /// Gets the current <see cref="RuleLevel"/> of this scope
 /// </summary>
 /// <value>The level.</value>
 RuleLevel Level { get; }
}

and rule levels are:

/// <summary>
/// Levels leveraged by the <see cref="Rule{T}"/> implementations
/// </summary>
public enum RuleLevel
{
 /// <summary> Default value for the purposes of good citizenship</summary>
 None = 0,
 /// <summary> The rule raises a warning </summary>
 Warn,
 /// <summary> The rule raises an error </summary>
 Error
}

With that you can define new rules even without declaring new classes:

public static void ValidEmail(string email, IScope scope)
{
 if (!_emailRegex.IsMatch(email))
  scope.Error("String is not a valid email address");
}

or compose new validators through the enclosures

/// <summary>
/// Composes the string validator ensuring string length is shorter than
/// <paramref name="maxLength"/>
/// </summary>
/// <param name="maxLength">Max string length.</param>
/// <returns>new validator instance</returns>
public static Rule<string> Limited(int maxLength)
{
 Enforce.Argument(() => maxLength, Is.GreaterThan(0));
 return (s, scope) =>
 {
  if (s.Length > maxLength)
   scope.Error("String length can not be greater than '{0}'", maxLength);
 };
}

Rules could be composed together and still stay readable:

internal static Rule<string>[] UserName = new[]
{
 StringIs.Limited(6, 256),
 StringIs.ValidEmail
};

In order to run a rule, you simply pass it an object, a scope (to write output to) and then check the scope for results.

Additional extensibility points within this paradigm:

  • rules can "communicate" with each other by checking current error level of the scope (i.e.: CPU intensive rule may skip if there are problems already)
  • you can compose rules that check complex business objects (by leveraging IScope.Create)
  • you can implement different rule behaviors (without changing rules) by passing in different scope implementations. For example:

    • Fast argument scope could be used to check for function arguments and throw exception as soon as the first problem is encountered
    • Validation scope could be used to check for function arguments to throw and exception with ALL problems encountered
    • MessageBased scopes could be used to bind rule failures of complex domain object to the UI elements via some error provider (scope path is used for this)

There are more extensibility points within this simple concept))

BTW, Open Source implementation is available for .NET.

PS: in our company we've defined some rules for the public API in a single file (rather small) and reuse these for validation everywhere.

Rinat Abdullin