2011-04-05 54 views
0

我试图在程序中实现'需求'类型系统。在我看来,这看起来很简单,但实际上使其足够抽象以提供有用,证明比预期更困难。设计一个需求满意结构

这里就是我试图做...

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

如果这不是一个学习项目,比我会强烈建议你看一下使用已经内置了这样的:

  1. EntLib Validation Block
  2. Fluent Validation
  3. 任何建立在System.ComponentModel.DataAnnotations的顶部

如果你正在做这个作为一个简单的学习项目ECT,比你有2种基本的方法,你可以采取:

  1. 基于反射
  2. 代表基于

两个,委派基础是要简单得多实施,但不灵活。我已经多次实施了这种模式,概念很简单。这里介绍一下你可以得到的最基本的概念。

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

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

同样地,如果这不是一个学习项目,then don't reinvent the wheel unless you are planning to learn more about wheels :)

+0

谢谢!我会看看你的例子,并尝试理解它。我无法使用给定示例的原因是因为规则必须存储在数据库中,因此它们必须是可序列化的 - 并且它们在运行时也不知道。它们被最终用户添加到系统中。 – Ciel 2011-04-05 16:22:49

+0

@Ciel,那么您应该查看EntLib验证块,因为您可以将验证规则存储在代码以外的区域中。它有一种方法可以通过开箱即用的Config文件来实现,但可以通过一些工作扩展到使用数据库。任何更高级的,你需要看看使用完整的业务规则引擎。大多数人的学习曲线也非常陡峭。 – Josh 2011-04-05 16:27:50

+0

此外,您的代码只有一个更改编译。带有名称的'AddRule'的用法不能马上使用。相当不错的代码'从内存'! – Ciel 2011-04-05 16:31:05

0

我建议确定指定要求所需的最小范围,并从此开始,以了解设计变得多么复杂。如果您发现即使最小范围导致了很多复杂性,或者如果您甚至无法掌握最小范围会是什么,那么您可能需要考虑更灵活的方法,例如托管脚本语言来表达你的约束。有许多可用的库可以支持这些功能,并且它们比支持/维护更便宜,而不是您自己创建和发展的某些东西,以便提供一种“简单”设置约束的方法。

如果可以的话,保持简单,但如果确实需要变得复杂,可以尝试找到已经构建和测试的东西作为基础,以减轻复杂性的负担。

+0

我环顾四周,这样的事情,但没有发现任何东西。你知道哪些具体的事情可能是一个很好的起点吗? – Ciel 2011-04-05 15:29:41

+0

下面是一个选项,可让您将C#脚本嵌入/执行到您的应用程序中:http://www.csscript.net/ – 2011-04-05 15:32:09

+0

Hrnm ...承载脚本语言不会让我序列化保存在数据库中的约束,尽管,我将不得不这样做。 – Ciel 2011-04-05 15:38:14