设计需求满足结构

本文关键字:结构 满足 设计需求 | 更新日期: 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参数联系起来。我希望人们能够设置一些相当通用的"需求",但这背后的逻辑让我非常困惑。由于在设计时不知道需求,因此我无法真正对其中任何一个进行硬编码。

有什么建议吗?

设计需求满足结构

如果这不是一个学习项目,那么我强烈建议您使用已经为此构建的东西:

  1. EntLib Validation Block
  2. <
  3. 流利的验证/gh>
  4. 任何构建在System.ComponentModel.DataAnnotations之上的东西

如果你把它作为一个简单的学习项目,那么你有两种基本的方法可以采用:

  1. 基于反射
  2. 基于
  3. 委托

在这两者中,基于委托的实现要简单得多,但灵活性较差。我已经实现了这个模式好几次,这个概念很简单。以下是你能得到的最基本的概念。

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);
}

现在,这些都是来自内存,所以可能有一些可能是编码/编译器错误,但希望你能得到总的想法。

再说一遍,如果这不是一个学习项目,那么就不要重新发明轮子,除非你打算学习更多关于轮子的知识:)

我建议确定指定需求所需的最小范围,并从这个范围开始,看看设计变得有多复杂。如果您发现即使是最小作用域也会导致很多复杂性,或者如果您甚至无法掌握最小作用域是什么,那么您可能需要研究更灵活的方法,例如托管用于表达约束的脚本语言。有很多可用的库来支持这一点,它们将比你自己创建和发展的东西更便宜,以提供一种"简单"的方式设置约束。

尽可能保持简单,但如果确实需要复杂,请尝试找到已经构建并测试过的东西作为基础,以减少复杂性的负担。