问题
We have a set of about two dozen classes that inherit from a base class which has an abstract Validate method. Of course each class has different validation needs but the rules are needed in different combinations between all of them so, as you can imagine, this resulted in a lot of code duplication, as an example:
Class A needs rules 1, 3, 6, and 9
Class B needs rules 3, 4, and 8
Class C needs rules 1, 8, and 9
....
you get the picture.
So I was thinking of doing a simple refactoring and create a Validation Decorator in which each rule would be encapsulated in a class and a factory that would create the adhoc validation combination for each class.
Then I started thinking that each rule is so simple that instead of creating all the plumbing for the decorator I could probably just have Action delegates stored in the factory which would populate a List(Action(T))
so that each class would just iterate through that list executing each delegate.
The problem is that the number and/or types of parameters vary between each rule, as an example:
Rule 1 needs a nullable DateTime, a DateTime, and an enum.
Rule 2 needs a nullable DateTime, and a DateTime.
Rule 3 needs a string .....
Is this something that can be accomplished or have I not choice but to code the Decorator?
Thanks for your thoughts and ideas.
UPDATE: Some rule examples:
if(EndTime.HasValue && StartTime > EndTime)
Throw new Exception (...);
if(Status == StatusEnum.Pass && !EndTime.HasValue)
Throw new Exception (...);
if(string.IsNullOrEmpty(ProcessName))
Throw new Exception (...);
if(string.IsNullOrEmpty(ProductName))
Throw new Exception (...);
if(string.IsNullOrEmpty(CustomerName))
Throw new Exception (...);
All the involved classes have the properties DateTime? EndTime
, DateTime StartTime
, StatusEnum Status
, and string ProcessName
.
回答1:
Why not make an interface called something like IValidationParameter
Then have each validation rule have an implementation of IValidationParameter. For example
public class Rule1ValidationParameter : IValidationParameter
{
public DateTime? FirstDate { get; set; }
public DateTime SecondDate { get; set; }
pulbic MyEnum MyEnum { get; set; }
}
Then make your list List<Action<IValidationParameter>>
回答2:
First off, you can make a class containing all the parameters with booleans indicating which are relevant. You can also make a a Params delegate but that's a bit more ugly (also it'll require you to convert your parameters into an objects array).
personally, I'd go with a class describing all the parameters.
EDIT:
Here's an example for the ValiationRuleParameters (a class containing the parameters) idea:
public class ValidationRulesParam
{
private readonly Dictionary<string, object> _parameters = new Dictionary<string, object>();
public bool AddParam(string name, object value)
{
if (!_parameters.ContainsKey(name))
return false;
_parameters.Add(name, value);
return true;
}
public bool IsTypedParameterSet(string name, Type valueType)
{
object value;
if (_parameters.TryGetValue(name, out value) == false)
return false;
return (value.GetType() == valueType);
}
public TValue GetParamValue<TValue>(string name)
{
object value;
if (_parameters.TryGetValue(name, out value) == false)
return default(TValue);
if (!(value is TValue))
return default(TValue);
return (TValue)value;
}
public object this[string name]
{
get { return GetParamValue<object>(name); }
set { AddParam(name, value); }
}
}
It's a little like a combination of using a params object[] and using a plain dictionary
You can send instances of this class to each validation rule (action) that you run, and the cations can test the existence of a parameter (and it's type). and get it's value.
It's just an example (and there's much to improve on it), but it should give a nice picture expressing the idea.
来源:https://stackoverflow.com/questions/8086224/how-to-create-list-of-action-delegates-when-different-number-and-types-of-parame