I have the concept of a "Rule" that I want to be able to process. As such, I created the Interface below:
public interface IRule<T>
{
Boolean IsSatisfiedBy(T value);
String GetViolationMessage(T value);
}
I had planned on creating a series of "Rule" classes to represent the various rules currently supported by the system such as:
public class MatchesPatternRule : IRule<String>
{
private readonly Regex _regex;
private readonly String _pattern;
public MatchesPatternRule(String pattern)
{
Verify.IsFalse(String.IsNullOrWhiteSpace(pattern));
_regex = new Regex(pattern);
_pattern = pattern;
}
public Boolean IsSatisfiedBy(String value)
{
return _regex.IsMatch(value);
}
public String GetViolationMessage(String value)
{
return String.Format(RuleMessage.MatchesPatternViolation, _pattern, value);
}
}
And then the rules would be consumed through a variety of extension methods such as:
public static ValidationContext<String> MatchesPattern(this ValidationContext<String> context, String pattern)
{
context.AddRule(new MatchesPatternRule(pattern));
return context;
}
However, I currently have plans for 20+ rules, and MatchesPatternRule class could be easily replaced by writting the extension method as:
public static ValidationContext<String> MatchesPattern(this ValidationContext<String> context, String pattern)
where T : IComparable
{
Verify.IsFalse(String.IsNullOrWhiteSpace(pattern));
Regex regex = new Regex(pattern);
context.AddRule(value => regex.IsMatch(value), value => String.Format(RuleMessage.MatchesPatternViolation, _pattern, value));
return context;
}
Curious as to what approach is better and why? Both are easily testible and ultimately achieve the end result. Any insight would be greatly appreciated!
UPDATE
Based on the feedback from Tom, I think I have opted to go with a middle-ground approach. I am going to create a static class to hold the extension methods for each "rule". This will ensure that any given rule is easy to find, and will keep my code organized but also reduce the amount of code overall.
public static class MatchesPatternRule
{
public static ValidationContext<String> MatchesPattern(this ValidationContext<String> context, String pattern)
{
return MatchesPattern<T>(context, pattern, pattern, RegexOptions.None);
}
public static ValidationContext<String> MatchesPattern(this ValidationContext<String> context, String pattern, String friendlyPattern)
{
return MatchesPattern<T>(context, pattern, friendlyPattern, RegexOptions.None);
}
public static ValidationContext<String> MatchesPattern(this ValidationContext<String> context, String pattern, RegexOptions options)
{
return MatchesPattern<T>(context, pattern, pattern, options);
}
public static ValidationContext<String> MatchesPattern(this ValidationContext<String> context, String pattern, String friendlyPattern, RegexOptions options)
{
Verify.IsFalse(String.IsNullOrWhiteSpace(pattern));
Regex regex = new Regex(pattern);
context.AddRule(value => regex.IsMatch(value), value => String.Format(RuleMessage.MatchesPatternViolation, _pattern, value));
return context;
}
}