编写高度复杂的业务/数学规则的最佳方法

时间:2010-06-29 18:26:43

标签: c# math business-logic

我必须获取一些数据,并将大量可能的变量应用于它。我真的不喜欢使用一组巨大的if语句的想法,所以我正在寻求一种简化方法的帮助,并使其更容易维护。

举个例子:

if (isSoccer)
    val = soccerBaseVal;
else if (isFootball)
    val = footballBaseVal;
.... // 20 different sports

if (isMale)
   val += 1;
else
    val += 5;

switch(dayOfWeek)
{
    case DayOfWeek.Monday:
       val += 12;
    ...
}

等..等等..可能在100-200种不同的测试和配方变化范围内。

这似乎是一场维护噩梦。有什么建议吗?

编辑:

为了进一步增加问题,许多变量仅在某些情况下使用,因此它不仅仅是具有不同值的固定逻辑集。逻辑本身必须基于条件而改变,可能是从先前变量应用的条件(例如,如果val>阈值)。

所以是的,我同意对许多值使用查找,但我也必须有可变逻辑。

8 个答案:

答案 0 :(得分:7)

避免大型交换结构的常用方法是将信息放入数据结构中。创建包含关联值的枚举SportTypeDictionary<SportType, Int32>。您只需编写val += sportTypeScoreMap[sportType]即可完成。

此模式的变化将在许多类似的情况下帮助您。

public enum SportType
{
    Soccer, Football, ...
}

public sealed class Foo
{
    private static readonly IDictionary<SportType, Int32> sportTypeScoreMap =
        new Dictionary<SportType, Int32>
        {
            { Soccer, 30 },
            { Football, 20 },
            ...
        }

    private static readonly IDictionary<DayOfWeek, Int32> dayOfWeekScoreMap =
        new Dictionary<DayOfWeek, Int32>
        {
            { DayOfWeek.Monday, 12 },
            { DayOfWeek.Tuesday, 20 },
            ...
        }

    public Int32 GetScore(SportType sportType, DayOfWeek dayOfWeek)
    {
        return Foo.sportTypeScoreMap[sportType]
             + Foo.dayOfWeekScoreMap[dayOfWeek];
    }
}

答案 1 :(得分:1)

使用switch语句或过滤器功能。

通过过滤功能,我的意思是:

func filter(var object, var value)
{
    if(object == value)
        object = valueDictionary['value'];
}

然后将过滤器应用于:

filter(theObject, soccer)
filter(theObject, football)

请注意,使用字典可以更好地使用过滤器,但这不是必需的。

答案 2 :(得分:1)

来自Pragmatic Programmer,您可以使用DSL来封装规则并编写流程引擎。对于您提出的问题,解决方案可能如下所示:

MATCH{
    Soccer   soccerBaseVal

    IsMale   5
    !IsMale  1
}

SWITCH{
    Monday   12
    Tuesday  13
}

然后匹配MATCH的第一个col中的所有内容,以及每个SWITCH中的第一个项目。您可以制作任何您想要的语法,然后只需编写一些脚本来将其填入代码中(或使用Xtext,因为它看起来很酷)。

答案 3 :(得分:1)

以下是一些想法:

1使用查找表:

var val = 0;

SportType sportType = GetSportType();

val += sportvalues[sportType];

您可以从数据库加载表。

2使用工厂模式:

var val = 0;

val += SportFactory.Create(sportType).CalculateValue();

Dynamic Factory Pattern在新的(运动)类型经常添加到代码中的情况下非常有用。此模式使用反射来防止更改工厂类(或任何全局配置)。它允许您只需在代码中添加一个新类。

当然,在您的情况下使用动态工厂甚至工厂都可能过度。你是唯一能说出来的人。

答案 4 :(得分:0)

作为第一步,我可能会将每个逻辑处理区域分解为自己的方法:(可能不是最佳名称作为第一遍)

EnforceSportRules
ProcessSportDetails
EnforceGenderRules 

接下来,根据规则的复杂程度,我可以将每个部分分解为自己的类,并让它们由主类(如工厂)处理。

GenderRules
GenderContext

答案 5 :(得分:0)

我没有什么特别的东西可以提供给你,而不是首先建议不要把它作为一个大块 - 把它分成几个部分,在重要部分之间做出评论分隔。

另一个建议是,如果您要进行许多非常短的测试,例如,从惯例中断,并将val增量器放在与评估和缩进相同的行上,以便它们彼此对齐。

if (isSoccer)              val = soccerBaseVal;  
if (isMale)                val += 1; 
else                       val += 5; 

switch(dayOfWeek){ 
    case DayOfWeek.Monday: val += 12; 
    ... 
}  

多余的空白可以将这百个事物变成几百行,使得垂直滚动过度,难以全面了解事物。

答案 6 :(得分:0)

如果你真的只是在这种类型中添加值,我会创建一个枚举,其中定义的索引对应于数组中的存储值。然后你可以做这样的事情:

enum Sport
{
    football = 0,
    soccer   = 1,
    //...
}

int sportValues[] = { 
    /* footballValue */,
    /* soccerValue */,
    /* ...Values */
};

int ApplyRules(Sport sport, /* other params */)
{
    int value = startingValue;
    value += sportValues[(int)sport];
    value += /* other rules in same fashion */;
}

答案 7 :(得分:0)

考虑实现利用继承/多态的Strategy Pattern来管理个人功能。通过将每个功能分成自己的专用课程,您可以放弃长达数英里case个句号或if语句的噩梦。​​

不确定C#是否支持(或永远不会)但是VB.NET将XML Comment CompletionList指令集成到intellisense中,当与策略模式结合使用时,它可以让您轻松使用一个具有OO开放式可扩展性的枚举。