Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to model exclusive behavior and make it configurable

Tags:

c#

.net

logic

rules

We have to present a list of options with the following 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 )

this is best explained with the following list example of fules for a car

Gas
Diesel
Electric
BioFuel
Ethanol

a car can have multiple fuel types selected such as

biofuel and Gas 
Gas and Electric
Gas, Ethanol and Biofuel

but not 
Diesel and Gas at the same time

Trying to come up with some configurable logic such that the code reads the configuration and applies the rules rather than knowing about specifics esp. since there's another list which has similar constraints

Any help/pointers on how to model this behavior ?

UPDATE -

Thanks everyone for the great ideas, the simplest solution seems to define a list of Incompatible Types in a config file/table for each fuel type as follows

FuelID/FuelName/IncompatibleTypes 1/Gas/2 2/Diesel/1 3/Electric/ 4/BioFuel/ 5/Ethanol/ etc.

Where IncomatibleTypes is say nvarchar(50) and will store the incompatible types like so 2,5,6,... etc.

So the code will read the definitions and if an item is selected, it will read the other selected items and if it finds anything on the incomatible list, it can either display a message or simply auto deselect the other incompatible item etc.

this may be inelegant but seems the most generic solution without the code knowing anything about specific items at all, plus it can be freely extended as in reality * there will likely be a couple dozen max selections at best * if the list of incomatibilities increases the storage can be changed to say nvarchar(100) without any changes to code itself

like image 229
Kumar Avatar asked Nov 15 '13 17:11

Kumar


2 Answers

I would most probably write a property or function, that loads the configuration when first needed (or just load it on application load, depending on the scenario). After that I would assign a bit to every possible value (so they are represented by powers of 2), and store the rules as numbers. When checking if a combination is valid, I would just iterate through the list of items, and check, if there is a compatible number with the checked rule. The simplest case of this is if you list all acceptable combinations, and just run a .Any expression on it, and throw an exception, when nothing is found, or in reverse, you store forbidden combinations, and throw an exception, when something is found.

This approach is way faster, than using a dictionary for example. You can even implement some effective data structure for finding the correct key, if you need performance.

For example:

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

Possible values are:

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

When you need to check for Biofuel + Gas, you check for 9. It is found, everything is all right. When you need to check for diesel and gas, you check for 3. It is not found, you throw an exception.

like image 94
Robert Avatar answered Oct 18 '22 10:10

Robert


I would assign a "compatibility group" number to each of the values, where the number should be viewed digit by digit.

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

FuelType could be a class with a property .CompatibilityGroup of type String or anything that's easy to check character for character.

So for any combination of FuelTypes, just check if they all have a common digit/character. If the CompatibilityGroups have nothing in common, they are not compatible.

E.g.

Biofuel and Gas both have the digit 2 in common, so they are compatible.

Gas and Diesel have no digit in common, so they are incompatible.

Boolean IsCompatible(params FuelType types[])
{
    //Finding an algorithm that finds a common character/digit should be trivial.
    return HasCommonDigit(types.Select(t => t.CompatibilityGroup));
}
like image 25
David S. Avatar answered Oct 18 '22 08:10

David S.