C#中基于可配置规则的系统

时间:2009-12-09 03:51:02

标签: c# f# pattern-matching rules

我有一个算法,它返回一个依赖于算法的两个参数的分类列表(字符串):一个类型变量,以及一个允许将某些特殊分类添加到结果列表的额外类别字符串。

由于规则表达为ifs和switch语句,当前实现是不可读和不可扩展的。规则也是硬编码的。

代码的简化版本:

 private static List<string> DetermineTypes(Type x, object category) {
  List<string> Types = new List<string>();


  if (category is DateTime) {
    types.Add("1");
    types.Add("2");
    types.Add("3");
  } else if (category is string) {
    switch ((string)category) {
      case "A":
        Types.Add("4");
        break;
      case "B":
      case "C":
      case "D":
        Types.Add("5");
        break;
      case "":
        Types = DetermineTypesFromX(Types, x);
        break;
      default:
        Types.Add("6");
        break;
    }
  }
  return graphTypes;
}


private static List<string> DetermineTypesFromX(List<string> Types, Type x) {
  if (x.Equals(typeof(int))) {
    Types.Add("7");
  } else if (x.Equals(typeof(double))) {
    Types.Add("8");

  } else if (x.Equals(typeof(System.DateTime))) {
    Types.Add("9");
    Types.Add("10");
  }
  return Types;
}

我当时认为用xml指定这些可能会很好,因此新类型/规则不需要更改代码,但这对于这种情况来说可能太重了。基本上我试图解决一个问题,即可以随时添加一个新的“类型”:常见的情况是它是上面的“规则”之一,以及一个新的“规则”分支可能不得不的边缘情况加上。

我仍然需要确定使用xml定义的规则(或任何其他方式)使其完全动态的工作是否值得与边缘情况发生的可能性和业务环境(时间表等)相比。

但我的主要问题是你如何优雅地简化上面的嵌套条件代码?可能在设计中加入更多灵活性以提高可扩展性?

我想知道是否使用F#模式匹配的组合可能是一个合适的解决方案? (注意:以前从未使用过F#,最近很好奇,所以这就是我要问的原因)

6 个答案:

答案 0 :(得分:1)

最近在以下两篇博文中讨论了一种称为调度表的模式,您可能会对此感兴趣:

Aaron Feng

K. Scott Allen

答案 1 :(得分:1)

我不会回避基于配置的选项;它通常具有不需要重建的优点。如果您不希望这样,另一个选项可能是通过属性的类型元数据。这将使添加 new 类型(您编写的)的数据变得微不足道,并且您可以(间接地)通过int向现有类型(TypeDescriptor.AddAttributes等)添加属性 - 只要你使用TypeDescriptor.GetAttributes让它们再次退出; -p

这是否是的想法......好吧,反射(和双胞胎,TypeDescriptor)可能很慢,所以如果你想在一个紧密的循环中使用它我先看一下涉及字典的事情。

答案 2 :(得分:1)

您的问题可能会按decision treedecision table

进行编码

另外,Chris Smith的博客中有关于决策树的帖子:

Awesome F# - Decision Trees – Part IAwesome F# - Decision Trees – Part II

答案 3 :(得分:0)

我建议您查看business rules/inference引擎。 NxBRE有一个很好的社区,并且相当成熟。这可能超出了您的直接要求,但如果您希望这些规则随着时间的推移而增加复杂性,BRE将提供一个很好的框架来控制事物。

答案 4 :(得分:0)

由于你提到了F#,这里有一些与C#代码非常相似的F#代码:

open System

let DetermineTypesFromX(x:Type) =
    if x.Equals(typeof<int>) then
        ["7"]
    elif x.Equals(typeof<double>) then
        ["8"]
    elif x.Equals(typeof<DateTime>) then
        ["9"; "10"]
    else
        []

let DetermineTypes(x:Type, category:obj) =
    match category with
    | :? DateTime -> ["1"; "2"; "3"]
    | :? string as s ->
        match s with
        | "A" -> ["4"]
        | "B" | "C" | "D" -> ["5"]
        | "" -> DetermineTypesFromX(x)
        | _ -> ["6"]
    | _ -> []

尽管如此,我建议考虑使用表驱动方法作为硬编码if / switch逻辑的替代方法,无论是将逻辑移出代码还是移入配置文件。

答案 5 :(得分:0)

我遇到了类似的情况,之前我曾就可能对您有类似问题提出过几个问题。

我所做的系统是一个配置驱动,基于规则的动态系统。所有配置和规则都保存在数据库中。决策表是根据从数据库中恢复的值和规则动态构建的。然后转换值并在C#中进行比较。这是我问过dynamic decision table in C#的问题。并且question regarding dyanmically convert and compare values从数据库中重新开始。

所以我最终在配置表方面有一些类似的东西(只是一个例子):

Conditions  IsDecision LHS        Operator  RHS
TTFF        False      PostCode   >         100
TFTF        False      PostCode   <         10000
FTTT        True 

Note: LHS is the property name of the object.

上表简明英文:

Condition 1 PostCode > 100      Yes Yes No  No
Condition 2 PostCode < 10000    Yes No  Yes No
Outcome 1                       Yes
Outcome 2                           Yes
Outcome 3                               Yes
Outcome 4                                   Yes

Then you have other tables/configs to determine the action for each outcome.

实现的核心部分是如何动态构建决策表以及如何动态转换和比较字符串值,所有这些都提供了上一段中特定实现的链接。我相信你可以在你的情况下应用类似的概念,我希望我已经解释了这个概念。

其他资源:

Martin Fowler's decision tree article

Luke's post on decision tree