Tutorial :Best approach to programming highly complex business/math rules


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?


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.


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


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.


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).


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.


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  


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.


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


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.

Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Next Post »