问题描述
我正在尝试编写代码以检查学生的要求,然后才允许注册课程。我希望此需求检查能够动态进行,以防万一需求发生变化,或者可能需要添加新需求,并且仅在激活此需求时才进行检查(也许并非所有学生都需要该需求)。
我创建了具有需求类型的界面:
public interface IBasicRequirementsEvaluator
{
RequirementResult CheckForFiles();
RequirementResult CheckForFinancialHold();
RequirementResult CheckForRegistrationHold();
RequirementResult CheckForStandingID();
RequirementResult CheckForGraduationDate();
}
及其实现:
public class BasicChecksImplementations : IBasicRequirementsEvaluator
{
private StudentData _data { get; set; }
public BasicChecksImplementations(StudentData data)
{
data = _data;
}
public RequirementResult CheckForFiles()
{
throw new NotImplementedException();
}
public RequirementResult CheckForFinancialHold()
{
throw new NotImplementedException();
}
public RequirementResult CheckForGraduationDate()
{
throw new NotImplementedException();
}
public RequirementResult CheckForRegistrationHold()
{
throw new NotImplementedException();
}
public RequirementResult CheckForStandingID()
{
throw new NotImplementedException();
}
}
然后尝试通过在字符串集合中显示需求检查的名称来尝试仅在激活检查的情况下执行检查:
public RequirementsResult CheckBasicRequirements (StudentData data,List<string> CheckActionNamesList)
{
RequirementsResult result = new RequirementsResult();
BasicChecksImplementations checks = new BasicChecksImplementations(data);
var methods = checks.GetType().GetMethods(BindingFlags.Public);
foreach (string actionName in CheckActionNamesList)
{
var method = methods.Where(x => x.Name == actionName).FirstOrDefault();
method.Invoke(this,null);
}
return result;
}
但是变量方法中什么也没有,所以显然这是错误的。 (还有一种设计模式可以用来实现这一目标吗?)。
解决方法
类似于策略模式的方法可能会起作用。我不知道您需要如何处理结果,因此在示例中将其简化了。
首先,我们创建一个接口,每个评估人员都将使用该接口来执行评估IRequirementEvaluator
。我们还为返回结果创建了另一个接口IRequirementResult
。
public interface IRequirementEvaluator
{
IRequirementResult Evaluate();
}
public interface IRequirementResult
{
bool IsValid { get; }
string Message { get; }
}
接下来,我们创建每个评估器对象,并在每个对象中实现IRequirementEvaluator
接口。注意,由于每个评估器都包含在自己的类中,因此您可以轻松实现新的评估器,而无需触及任何现有的评估器代码。
public class CheckForFiles : IRequirementEvaluator
{
private string inputValue;
public CheckForFiles(string inputValue)
{
// pass params to ctor and use them in evaluate method
this.inputValue = inputValue;
}
public IRequirementResult Evaluate()
{
// used ctor params to evaluate
bool isValid = !string.IsNullOrEmpty(inputValue);
return new RequirementResult(false,$"Evaluation on {this.GetType().Name} {(isValid ? "was Successful" : "has Failed")}.");
}
}
public class CheckForFinancialHold : IRequirementEvaluator
{
private readonly decimal amountDue = 0m;
public CheckForFinancialHold(decimal amountDue)
{
// pass params to ctor and use them in evaluate method
this.amountDue = amountDue;
}
public IRequirementResult Evaluate()
{
// used ctor params to evaluate
bool isValid = amountDue <= 0m;
return new RequirementResult(false,$"Evaluation on {this.GetType().Name} {(isValid ? "was Successful" : "has Failed")}.");
}
}
public class CheckForRegistrationHold : IRequirementEvaluator
{
public CheckForRegistrationHold()
{
// pass params to ctor and use them in evaluate method
}
public IRequirementResult Evaluate()
{
// used ctor params to evaluate
bool isValid = false;
return new RequirementResult(false,$"Evaluation on {this.GetType().Name} {(isValid ? "was Successful" : "has Failed")}.");
}
}
public class CheckForStandingId : IRequirementEvaluator
{
public CheckForStandingId()
{
// pass params to ctor and use them in evaluate method
}
public IRequirementResult Evaluate()
{
// used ctor params to evaluate
bool isValid = false;
return new RequirementResult(false,$"Evaluation on {this.GetType().Name} {(isValid ? "was Successful" : "has Failed")}.");
}
}
public class CheckForGraduationDate : IRequirementEvaluator
{
public CheckForGraduationDate()
{
// pass params to ctor and use them in evaluate method
}
public IRequirementResult Evaluate()
{
// used ctor params to evaluate
bool isValid = false;
return new RequirementResult(false,$"Evaluation on {this.GetType().Name} {(isValid ? "was Successful" : "has Failed")}.");
}
}
最后,我们实现了BasicChecksEvaluator
,它也实现了IRequirementEvaluator
接口。该对象将要求在其构造函数中传递评估者列表。将会迭代此列表,并在调用Evaluate
的{{1}}方法时依次调用每个BasicChecksEvaluator
方法。
Evaluate
要在您的应用程序中使用此功能,请创建public class BasicChecksEvaluator : IRequirementEvaluator
{
private readonly IEnumerable<IRequirementEvaluator> evaluators;
public BasicChecksEvaluator(IList<IRequirementEvaluator> evaluators)
{
if (evaluators == null) throw new ArgumentNullException(nameof(evaluators));
if(!evaluators.Any()) throw new ArgumentException(nameof(evaluators));
this.evaluators = evaluators;
}
public IRequirementResult Evaluate()
{
IList<IRequirementResult> results = new List<IRequirementResult>();
bool isValid = true;
foreach (var e in evaluators)
{
var result = e.Evaluate();
// if one evaluator is invalid then the final result is invalid
isValid = isValid && result.IsValid;
results.Add(result);
}
// return final isValid result along with all messages concatenated
return new RequirementResult(isValid,string.Join(Environment.NewLine,results.Select(r => r.Message).ToArray()));
}
}
的列表并将其传递给IRequirementEvaluator
的构造函数。您可以轻松地选择要包括或排除的评估者。
BasicChecksEvaluator
此外,上面的用法示例可以封装在对象中,并允许您执行以下操作,其中您可以隐藏有关在不同情况下调用评估程序的详细信息。
IList<IRequirementEvaluator> evaluators = new List<IRequirementEvaluator>();
evaluators.Add(new CheckForFiles("some param value that's being passed in"));
evaluators.Add(new CheckForGraduationDate());
evaluators.Add(new CheckForFinancialHold(0m));
// evaluators.Add(new CheckForRegistrationHold()); // <-- commented out to exclude
evaluators.Add(new CheckForStandingId());
BasicChecksEvaluator bce = new BasicChecksEvaluator(evaluators);
var result = bce.Evaluate();
// which outputs the following...
// IsValid is False
// Evaluation on CheckForFiles was Successful.
// Evaluation on CheckForGraduationDate has Failed. <--- caused evaluation to fail
// Evaluation on CheckForFinancialHold was Successful.
// Evaluation on CheckForStandingId has Failed. <--- caused evaluation to fail