如何建模独占行为并使其可配置

时间:2013-11-15 17:06:09

标签: c# .net logic rules

我们必须提供具有以下规则的选项列表

* you can select multiple items, as many as you want
* however, some of the items are mutually exclusive, i.e. selecting itemA should automatically deselects itemC ( if itemC is selected prviously )

最好用以下列出汽车燃油的列表示例来解释

Gas
Diesel
Electric
BioFuel
Ethanol

汽车可以选择多种燃料类型,例如

biofuel and Gas 
Gas and Electric
Gas, Ethanol and Biofuel

but not 
Diesel and Gas at the same time

尝试提出一些可配置的逻辑,以便代码读取配置并应用规则而不是了解具体的esp。因为有另一个列表有类似的约束

有关如何建模此行为的任何帮助/指示?

更新 -

感谢大家的好主意,最简单的解决方案似乎在配置文件/表中为每种燃料类型定义了一个不兼容类型列表,如下所示

FuelID / FuelName / IncompatibleTypes 1 /气体/ 2 2 /柴油/ 1 3 /电器/ 4 /生物燃料/ 5 /乙醇/ 等

其中IncomatibleTypes是nvarchar(50),它将存储不兼容的类型,如2,5,6,......等。

因此代码将读取定义,如果选择了一个项目,它将读取其他所选项目,如果它在不可用列表中找到任何内容,它可以显示消息或只是自动取消选择其他不兼容项目等。

这可能是不优雅的,但似乎是最通用的解决方案,如果代码根本不了解具体项目的任何内容,而且它可以像现实一样自由扩展 *最多可能会有十几个最大选择 *如果不可用性列表增加,则可以将存储更改为nvarchar(100)而不对代码本身进行任何更改

4 个答案:

答案 0 :(得分:4)

我会为每个值分配一个“兼容性组”编号,其中应该逐位查看该编号。

FuelType | CompatibilityGroup
---------|-------------------
Gas      | 2
Diesel   | 3
Electric | 123
BioFuel  | 123
Ethanol  | 123

FuelType可以是类型为.CompatibilityGroup的属性String的类,也可以是容易检查角色字符的任何内容。

因此,对于FuelType的任何组合,只需检查它们是否都有一个共同的数字/字符。如果CompatibilityGroup没有任何共同点,则它们不兼容。

E.g。

BiofuelGas都有共同的数字2,因此它们是兼容的。

GasDiesel没有共同的数字,因此它们不兼容。

Boolean IsCompatible(params FuelType types[])
{
    //Finding an algorithm that finds a common character/digit should be trivial.
    return HasCommonDigit(types.Select(t => t.CompatibilityGroup));
}

答案 1 :(得分:4)

我很可能会写一个属性或函数,它在第一次需要时加载配置(或者只是在应用程序加载时加载它,具体取决于场景)。之后,我会为每个可能的值分配一个位(因此它们由2的幂表示),并将规则存储为数字。检查组合是否有效时,我只是遍历项目列表,并检查是否存在与已检查规则的兼容号码。最简单的情况是,如果列出所有可接受的组合,并在其上运行.Any表达式,并在未找到任何内容时抛出异常,或者相反,则存储禁止的组合,并在出现异常时抛出异常找到。

这种方法比使用字典更快。如果需要性能,您甚至可以实现一些有效的数据结构来查找正确的密钥。

例如:

Name     | Value    
----------------------    
Gas      | 00001 = 1    
Diesel   | 00010 = 2    
Electric | 00100 = 4    
BioFuel  | 01000 = 8    
Ethanol  | 10000 = 16

可能的值是:

Combination   | Value
----------------------
Biofuel + Gas | 01001=9
Gas + Electric| 00101=5
etc.

当您需要检查生物燃料+气体时,检查9.发现,一切都很好。 当你需要检查柴油和汽油时,你检查3.没有找到,你抛出一个例外。

答案 2 :(得分:3)

我会使用Dictionary和每个键的值列表。示例代码:

enum fuel { Diesel, Biofuel, Gas, Electric, Ethanol };
private void Form1_Load(object sender, EventArgs e)
{ 
    Dictionary<fuel, List<fuel>> dict = new Dictionary<fuel,List<fuel>>(); 

    dict.Add(fuel.Biofuel, new List<fuel>{fuel.Gas, fuel.Ethanol});
    dict.Add(fuel.Gas, new List<fuel>{fuel.Biofuel, fuel.Electric, fuel.Ethanol});
    //etc.

    fuel fuel1 = fuel.Biofuel;
    fuel fuel2 = fuel.Gas;

    if (dict[fuel1].Contains(fuel2) || dict[fuel2].Contains(fuel1))
    {
        //Both fuels can be put together
    }
    else
    {
        //Please, don't mix.
    }
}

这种方法非常灵活,可以进行优化,因此您不需要将所有燃料添加为关键字(并非每个关键字的所有潜在值)。您也不需要使用Enum。这个答案的重点是提出一个解决方案,该解决方案基于由键和值列表定义的字典,这些值仅列出&#34;有效组合&#34;。

答案 3 :(得分:2)

到目前为止,所有答案都是解决问题的不错方法,但我不会认为他们中的任何一个都会在选择新项目时给你哪些项目排除,而不是全部迭代类型(与选定的一对创建)。理想情况下,我想要像

这样的东西
List<FuelType> excluded = fuel.GetExcludeTypes();

FuelType excl = fuel.GetExcludeTypes();

如果FuelType是Flags枚举。

因此我的解决方案是这样的:

enum FuelType
{
    Gas = 0x0001, etc...
}
class Fuel
{
    readonly FuelType TypeId;
    readonly FuelType Exclude;
    public Fuel(FuelType type, FuelType exclude)
    {
        TypeId = type;
        Exclude = exclude;
    }
}

您可以创建类似的类型:

var gas = new Fuel(FuelType.Gas, FuelType.Diesel);
var diesel = new Fuel(FuelType.Diesel, FuelType.Gas | FuelType.AlsoExclude);

通过使用标志,并存储每种燃料的可排除燃料类型的OR,您可以避免列出每种燃料的类型列表,或者您必须以一对FuelTypes提供的单独的兼容性列表一个博尔。这样,您可以根据燃料类型排除所有类型,并且不了解其他类型。

我仍然不喜欢这个解决方案,因为它是在运行时确定燃料类型,但它是获得语义功能的好方法。可能有更好的方法来设置代码以实现这些相同的结果。我欢迎提出建议。