Reputation:
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:
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.
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?
Upvotes: 0
Views: 697
Reputation: 23562
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:
you can implement different rule behaviors (without changing rules) by passing in different scope implementations. For example:
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.
Upvotes: 0
Reputation: 1162
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.
Upvotes: 0
Reputation: 10003
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.
Upvotes: 0
Reputation: 391952
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.
Upvotes: 1