创建规则引擎以处理“和/或”逻辑的最佳方法是什么

时间:2012-07-11 18:00:15

标签: .net sql bit-manipulation

例如......

if( (stuCourses.contains("crs101") && stuCourses.contains("crs102") && 
    stuCourses.contains("crs103"))  || (stuCourses.contains("crs201") && 
    stuCourses.contains("crs202")) || stuCourses.contains("crs300") )
{
    //then student can register for crs301;
}

此外,对于类似这样的事情,最好的表格模式是什么,即课程/课程先决条件......如何构建表格字段以处理“和/或”逻辑?

4 个答案:

答案 0 :(得分:1)

我会有以下表格。这有点复杂,所以如果这是一个小实现,只需更新上面的代码就可能更容易了!

Courses(ID, Name, ...)
-- Information about each course

CoursePrerequisiteRules(ID, CourseID, PrerequisiteRuleID, ValidFrom, ValidTo)
-- Assigns a single rule to a particular course, for a particular range of academic years

PrerequisiteRules(ID, RuleType)
-- The header record defining a rule or sub-rule for a specific course

PrerequisiteRuleCourseMembers(ID, PrerequisiteCourseID, PrequisiteRuleID)
-- Each record assigns one course to a prerequisite rule.

PrerequisiteRuleRuleMembers(ID, ParentPrerequisiteRuleID, ChildPrerequisiteRuleID)
-- Each record assigns one prerequisite rule as a child of another rule

PrerequisiteRules.RuleType将为“AND”或“OR”。先决条件规则可以使用PrerequisiteRuleCourseMembers或使用PrerequisiteRuleRuleMembers分配给它的其他规则为其分配课程。每个课程一次只能有一个规则,但如果需要,可以按学年定义(使用CourseFrequisiteRules的ValidFrom和ValidTo)。可以创建复杂的规则树作为此规则的子规则。

e.g。说明CS320需要的要求(CS101或CS102)和MT101:

Courses
ID  Name
1   CS101
2   CS102
3   MT101
4   CS320

CoursePrerequisites
ID  CourseID PrerequisiteRuleID  ValidFrom  ValidTo
5   4        7                   2010       2012
-- The rule for CS320 between 2010 and 2012 is rule number 7

PrerequisiteRules
ID  RuleType
6   "OR"
7   "AND"
-- Rule number 7 performs an AND on all its children
-- Rule number 6 uses an OR instead

PrerequisiteRuleCourseMembers
ID  PrerequisiteCourseID   PrequisiteRuleID
8   1                      6
9   2                      6
10  3                      7
-- This assigns CS101 and CS102 to rule 6, meaning that rule 6 should be evaluated as
-- CS101 OR CS102. It also assigns MT101 to rule 7.

PrerequisiteRuleRuleMembers
ID  ParentPrerequisiteRuleID ChildPrerequisiteRuleID
11  7                        6
-- This assigns Rule 6 to Rule 7, so with the row in the previous table, rule 7 becomes
-- MT101 AND [Rule 6]
-- ...which means MT101 AND (CS101 OR CS102) 

答案 1 :(得分:0)

老实说,我不认为条件本身需要改变......只是它所代表的抽象层次。这看起来很麻烦:

if((stuCourses.contains("crs101") && stuCourses.contains("crs102") && stuCourses.contains("crs103")) || (stuCourses.contains("crs201") && stuCourses.contains("crs202")) || stuCourses.contains("crs300") )
{
    //then student can register for crs301;
}

但不是你想的原因。可以将和/或逻辑格式化为更好看,但更重要的是它应该被抽象为另一个函数。像这样:

public void RegisterStudentForCRS301()
{
    // other stuff
    if (StudentHasTakenNecessaryCourses())
    {
        // etc.
    }
}

private bool StudentHasTakenNecessaryCourses()
{
    return
        StudentHasTakenNecessary100LevelCourses() ||
        StudentHasTakenNecessary200LevelCourses() ||
        StudentHasTakenNecessary300LevelCourses()
    ;
}

private bool StudentHasTakenNecessary100LevelCourses()
{
    return
        stuCourses.contains("crs101") &&
        stuCourses.contains("crs102") &&
        stuCourses.contains("crs103")
    ;
}

private bool StudentHasTakenNecessary200LevelCourses()
{
    return
        stuCourses.contains("crs201") &&
        stuCourses.contains("crs202")
    ;
}

private bool StudentHasTakenNecessary300LevelCourses()
{
    return
        stuCourses.contains("crs300")
    ;
}

每个函数都有自己的抽象级别,只负责一件事,只负责一件事。这些函数聚合成更高级的函数,这些函数检查跨多个抽象级别的逻辑。这一切都汇总到公共功能中,它不关心它只需要知道业务概念的细节(学生是否有资格?)。

每个抽象层次都易于阅读和理解。哎呀,一旦人们对&&||的含义有一个基本的解释,你甚至不需要成为程序员来阅读逻辑。当然,这是更多的代码。但它的表达代码非常清楚地以毫不含糊的方式描述了它正在做什么。如果一个人对逻辑的细节不感兴趣并且只需要知道高级业务功能,那么甚至不需要查看比较逻辑。只需调用适当命名的函数即可。让较低级别的抽象处理较低的细节。

答案 2 :(得分:0)

我会想出一种简单的语言来描述课程要求,并用该语言指定每门课程的要求。然后编写一个非常简单的引擎,该引擎需要课程要求描述并检查学生的课程集是否符合它。

您可以使用&使用简单的课程组合列表将组合中的课程和|分开组合,如下所示:

crs101&crs102&crs103|crs201&crs202|crs300

然后,您将其作为crs301的“先决条件”存储在数据库中。

然后引擎将循环遍历将|标记分开的字符串。然后它将依次在&标记处分隔每个条目,如果课程不匹配则跳到下一个。

答案 3 :(得分:0)

我想建议实现这一目标的最佳方法是将您的“先决条件规则”存储在XML中,可以在数据库中存储,也可以在硬文件中存储,具体取决于您的应用程序架构和需求。 XML非常适合您的需求,因为您可以存储先决条件集,例如......

<Courses>
    <Course>
        <CourseCode>crs301</CourseCode>
        <PrereqSets>
            <PrereqSet>
                <CourseCode>crs101<CourseCode>
                <CourseCode>crs102<CourseCode>
                <CourseCode>crs103<CourseCode>
            </PrereqSet>
            <PrereqSet>
                <CourseCode>crs201<CourseCode>
                <CourseCode>crs202<CourseCode>
            </PrereqSet>
            <PrereqSet>
                <CourseCode>crs300<CourseCode>
            </PrereqSet>
        </PrereqSets>
    </Course>
    <Course> 
    <!--Some other course data goes here-->
    </Course>
</Courses>

然后,您可以使用XPath轻松地查询XML数据。

当然,通过将数据存储在数据库中,可以实现其他方法。一种方法可能是将它存储在一个带有复合键的关联表中,例如下面的......

Course table
    CourseID, Name
    1, crs101
    2, crs102
    3, crs103
    4, crs201
    5, crs202
    6, crs300
    7, crs301


CoursePrereqMap table
    CourseID, PrereqSetID, PrereqCourseID
    7, 1, 1
    7, 1, 2
    7, 1, 3
    7, 2, 4
    7, 2, 5
    7, 3, 6

无论如何,您可以将先决条件集加载到自定义对象(即VO或DTO,如果这些不是过时的术语),或加载到某种列表或数组中,并编写一个方法来处理整个先决条件集,或者甚至是先决条件集的列表或数组,并确保所有课程至少完成一组。

我希望这能解答您关于如何存储此类数据的第二个问题。这不是一个具体的答案,而是关于你可以通过几种方式实施它的建议;当然,你可以采取很多方法。