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

发布于 2024-09-07 04:01:29 字数 574 浏览 9 评论 0原文

我必须获取一段数据,并将大量可能的变量应用于它。我真的不喜欢使用大量 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 > 阈值)。

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

I have to take a piece of data, and apply a large number of possible variables to it. I really don't like the idea of using a gigantic set of if statements, so i'm looking for help in an approach to simplify, and make it easier to maintain.

As an example:

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

etc.. etc.. etc.. with possibly in the range of 100-200 different tests and formula variations.

This just seems like a maintenance nightmare. Any suggestions?

EDIT:

To further add to the problem, many variables are only used in certain situations, so it's more than just a fixed set of logic with different values. The logic itself has to change based on conditions, possibly conditions applied from previous variables (if val > threshold, for instance).

So yes, i agree about using lookups for many of the values, but I also have to have variable logic.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

薄荷港 2024-09-14 04:01:29

避免大型交换结构的常见方法是将信息放入数据结构中。创建一个枚举 SportType 和一个包含关联值的 Dictionary。您只需编写 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];
    }
}

A common way to avoid large switching structures is to put the information into data structures. Create an enumeration SportType and a Dictionary<SportType, Int32> containing the associated values. The you can simply write val += sportTypeScoreMap[sportType] and you are done.

Variations of this pattern will help you in many similar situations.

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];
    }
}
赠佳期 2024-09-14 04:01:29

使用 switch 语句或过滤函数。

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

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

然后应用过滤器:

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

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

Use either a switch statement or filter function.

By filter function, I mean something like:

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

Then apply the filter with:

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

Note that the filter works much better using a dictionary, but it is not required.

澉约 2024-09-14 04:01:29

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

MATCH{
    Soccer   soccerBaseVal

    IsMale   5
    !IsMale  1
}

SWITCH{
    Monday   12
    Tuesday  13
}

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

Cribbing from The Pragmatic Programmer, you could use a DSL to encapsulate the rules and write a process engine. For your presented problem, a solution might look like:

MATCH{
    Soccer   soccerBaseVal

    IsMale   5
    !IsMale  1
}

SWITCH{
    Monday   12
    Tuesday  13
}

Then match everything in the first col of MATCH, and the first item in each SWITCH you come to. You can make whatever syntax you feel like, then just write a bit of script to cram that into code (or use Xtext because it looks pretty cool).

睫毛溺水了 2024-09-14 04:01:29

这里有一些想法:

1 使用查找表:

var val = 0;

SportType sportType = GetSportType();

val += sportvalues[sportType];

您可以从数据库加载表。

2 使用工厂模式:

var val = 0;

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

动态工厂模式很有用在代码中频繁添加新(运动)类型的情况下。此模式使用反射来防止工厂类(或任何全局配置)被更改。它允许您简单地向代码中添加一个新类。

当然,在您的情况下,使用动态工厂甚至工厂可能有点过分了。你是唯一能说清楚的人。

Here are a few ideas:

1 Use lookup tables:

var val = 0;

SportType sportType = GetSportType();

val += sportvalues[sportType];

You can load the table from the database.

2 Use the factory pattern:

var val = 0;

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

The Dynamic Factory Pattern is useful in situations were new (sport) types are added frequently to the code. This pattern uses reflection to prevent the factory class (or any global configuration) from being changed. It allows you to simply add a new class to your code.

Of course the use of an dynamic factory, or even a factory can be overkill in your situation. You're the only one who can tell.

平安喜乐 2024-09-14 04:01:29

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

EnforceSportRules
ProcessSportDetails
EnforceGenderRules 

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

GenderRules
GenderContext

As a first step I would probably break up each logical processing area into its own method: (May not be the best names as a first pass)

EnforceSportRules
ProcessSportDetails
EnforceGenderRules 

Next, depending on how complex the rules are, I may break each section into its own class and have them processed by a main class (like a factory).

GenderRules
GenderContext
碍人泪离人颜 2024-09-14 04:01:29

我没有什么特别可以提供给你的,只是首先建议不要把它当作一个大块——把它分成几个部分,在重要部分之间设置注释分隔线。

另一个建议是,如果您要像示例中那样进行许多非常短的测试,请打破惯例,将 val 增量器与求值和缩进放在同一行,以便它们彼此对齐。

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

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

过多的空白会使这数百个内容变成数百行,导致垂直滚动过多并且难以获得事物的整体视图。

I have nothing special to offer you than to first recommend not to just leave it as a big block-- break it into sections, make comment dividers between important parts.

Another suggestion is if you are going to have many very short tests as in your example, break from convention and put the val incrementors on the same line as the evaluatation and indent so they align with eachother.

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

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

Excess whitespace can make those hundred things into several hundred lines, making vertical scrolling excessive and difficult to get an overall view of the thing.

将军与妓 2024-09-14 04:01:29

如果您真的只是以这种方式添加值,我会创建一个具有与数组中存储的值相对应的定义索引的枚举。然后你可以做这样的事情:

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

If you are really just adding values in this sort, I would either create an enumeration with defined indices that correspond to stored values in an array. Then you can do something like this:

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 */;
}
三生殊途 2024-09-14 04:01:29

考虑实现策略模式,它利用继承/多态性来使管理各个函数变得理智。通过将每个函数分离到其自己的专用类中,您可以避免拥有长达数英里的 case 块或 if 语句的噩梦。

不确定 C# 是否支持(或将来),但 VB.NET 集成了 XML Comment CompletionList 指令进入智能感知,当与策略模式结合使用时,它可以为您提供枚举的易用性以及面向对象的开放式可扩展性。

Consider implementing the Strategy Pattern which utilizes inheritance/polymorphism to make managing individual functions sane. By seperating each function into its own dedicated class you can forego the nightmare of having miles-long case blocks or if statements.

Not sure if C# supports it yet (or ever will) but VB.NET integrates XML Comment CompletionList directives into intellisense, which--when combined with the Strategy Pattern--can give you the ease of use of an Enum with the open-ended extensibility of OO.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文