设计需求满足结构
本文关键字:结构 满足 设计需求 | 更新日期: 2023-09-27 17:48:57
我正试图在一个程序中实现一个'Requirements'类型的系统。在我的脑海中,这似乎很简单,但实际上要使它足够抽象而有用,比预期的要困难得多。
这是我想做的…
enum Condition {
Not, Exists, Exceeds
}
abstract class Requirement {
// base class
Condition Condition { get; set; }
}
class ScoreRequirement : Requirement {
// a specific 'score' is required
}
class TraitRequirement : Requirement {
// a specific 'trait' is required
}
class SatisfyingObject {
// this is the class that has to satisfy the requirements
IDictionary<Trait, int> Traits { get; set; }
}
如果我知道代码时必须满足的确切的东西,这是非常简单的。但目标是让人们稍后添加需求。也会有其他类型的衍生自Requirement
。
所以需求可能是这样的…
var obj = new Item {
Requirements = new List<Requirement> {
new ScoreRequirement { Trait = "One", Score = 2, Condition = Condition.Exceeds }
}
}
这个概念看起来很简单。你会调用一个对象…
var satisfying = // get the satisfying object;
if( satisfying.Satisfies( obj.Requirements ) )
return true;
我遇到的问题是如何编写Satisfies
方法-具体而言,我不确定如何将其与Condition
参数联系起来。我希望人们能够设置一些相当通用的"需求",但这背后的逻辑让我非常困惑。由于在设计时不知道需求,因此我无法真正对其中任何一个进行硬编码。
有什么建议吗?
如果这不是一个学习项目,那么我强烈建议您使用已经为此构建的东西:
- EntLib Validation Block <
- 流利的验证/gh>
- 任何构建在System.ComponentModel.DataAnnotations之上的东西
如果你把它作为一个简单的学习项目,那么你有两种基本的方法可以采用:
- 基于反射 基于
- 委托
在这两者中,基于委托的实现要简单得多,但灵活性较差。我已经实现了这个模式好几次,这个概念很简单。以下是你能得到的最基本的概念。
public interface IRuleDefinition
{
String PropertyName { get; }
String Message { get; }
}
public class ValidationRule<T>: IRuleDefinition
{
public String PropertyName { get; private set; }
public String Message { get; private set; }
private Func<T, Boolean> _isValidDelegate;
public ValidationRule(Func<T, Boolean> isValidDelegate, String propertyName, String message)
{
PropertyName = propertyName;
Message = message;
_isValidDelegate = isValidDelegate;
}
public Boolean IsValid(T objToValidate)
{
return _isValidDelegate(objToValidate);
}
}
public class Validator<T>
{
private List<ValidationRule<T>> _validationRules = new List<ValidationRule<T>>();
public void AddRule(Func<T, Boolean> isValidDelegate, String propertyName = null, String message = null)
{
_validationRules.Add(new ValidationRule<T>(isValidDelegate, propertyName, message));
}
public Boolean IsValid(T objToValidate)
{
return _validationRules.Any(vr => vr.IsValid(objToValidate));
}
public IEnumerable<IRuleDefinition> GetViolations(T objToValidate)
{
return _validationRules
.Where(vr => !vr.IsValid(objToValidate))
.Cast<IRuleDefinition>();
}
}
你可以在代码中这样使用它:
var myObj = new MyObject{ Name = "Josh", Age = 29 };
var myObjValidator = new Validator<MyObject>();
myObjValidator.AddRule(
obj => !String.IsNullOrWhitespace(obj.Name),
"Name", "Name is required!");
myObjValidator.AddRule(
obj => obj.Age < 99,
"Age", "Age must be less than 99");
myObjValidator.AddRule(
obj => obj.Name == "Logan" && obj.Age < 29,
message: "RUN!!!");
if(!myObjValidator.IsValid(myObj))
{
foreach(var violation in myObjValidator.GetViolations(myObj))
Console.WriteLine("Property: {0}, Message: {1}",
violation.PropertyName, violation.Message);
}
现在,这些都是来自内存,所以可能有一些可能是编码/编译器错误,但希望你能得到总的想法。
再说一遍,如果这不是一个学习项目,那么就不要重新发明轮子,除非你打算学习更多关于轮子的知识:)
我建议确定指定需求所需的最小范围,并从这个范围开始,看看设计变得有多复杂。如果您发现即使是最小作用域也会导致很多复杂性,或者如果您甚至无法掌握最小作用域是什么,那么您可能需要研究更灵活的方法,例如托管用于表达约束的脚本语言。有很多可用的库来支持这一点,它们将比你自己创建和发展的东西更便宜,以提供一种"简单"的方式设置约束。
尽可能保持简单,但如果确实需要复杂,请尝试找到已经构建并测试过的东西作为基础,以减少复杂性的负担。