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

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

4

4 回答 4

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 分配给它的其他规则。每门课程一次都会有一个活动规则,但如果需要,可以在每个学年的基础上定义这些规则(使用 CoursePrequisiteRules 的 ValidFrom 和 ValidTo)。可以将复杂的规则树创建为这一规则的子规则。

例如说明 CS320 需要 (CS101 OR CS102) AND 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) 
于 2012-07-11T19:42:57.700 回答
0

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

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

crs101&crs102&crs103|crs201&crs202|crs300

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

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

于 2012-07-11T18:15:04.283 回答
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,如果它们不是过时的术语),或者加载到某种列表或数组中,然后编写一个处理整个先决条件集的方法,甚至可能是先决条件集的列表或数组,并确保所有课程至少完成一组。

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

于 2012-07-12T00:50:49.773 回答
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")
    ;
}

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

每个抽象级别都易于阅读和理解。哎呀,你甚至不需要成为一名程序员来阅读逻辑,一旦一个人对什么&&||意思有了基本的解释。当然,这是更多的代码。但它是富有表现力的代码,非常清楚地描述了它在做什么。如果对逻辑细节不感兴趣,只需要了解高级业务功能,甚至不需要看比较逻辑。只需调用适当命名的函数。让较低的抽象层处理较低的细节。

于 2012-07-11T18:11:47.490 回答