Search code examples
c#business-logic

Is there a better way to write business logic to validate mutually exclusive codes?


I have two properties. I need to validate these properties against a rather large matrix showing compatible values.

Sample properties:

public string firstValue { get; set; }
public string lastValue { get; set; }

Sample matrix:

    A   B   C
A   X   O   O
B   O   X   X
C   X   O   X

Let the x axis represents the "firstValue" and the y axis represents the "lastValue". The program must validate that invalid combinations denoted by an "X" throw an error to the user.

Sample validation:

if (firstValue == "A")
{
    if (lastValue == "A" || lastValue =="C)
        Console.WriteLine("Invalid Combination");
}

This could obviously be refactored several ways using if\else and switch logic, but when you add many hundreds of values to the matrix it starts to get scary. I decided to create a new class containing one function per row in the matrix.

public class MutuallyExclusiveValidation
{
    public void CheckA(string lastValue)
    {
        if (lastValue == "A" || lastValue == "C")
        {
            Console.WriteLine("Invalid Combination");
        }
    }

    public void CheckB(string lastValue)
    {
        if (lastValue == "B")
        {
            Console.WriteLine("Invalid Combination");
        }
    }

    public void CheckC(string lastValue)
    {
        if (lastValue == "B" || lastValue == "C")
        {
            Console.WriteLine("Invalid Combination");
        }
    }
}

Then I devised something like this to make sure I'm not hitting more code than I need to. The predicate will return a boolean selecting and invoking the validation method that needs to be run. (just a sample, not production code)

    public static void CheckForMutuallyExclusiveValues(string firstValue, string lastvalue)
    {
        var allValidators = GetMutuallyExclusiveValidators(firstValue, lastvalue);
        var requiredValidators = allValidators.First(x => x.Key(firstValue));
        var validationClassInstance = new MutuallyExclusiveValidation();
        requiredValidators.Value(validationClassInstance);
    }

    public static Dictionary<Predicate<string>, Action<MutuallyExclusiveValidation>> GetMutuallyExclusiveValidators(string firstValue, string lastvalue)
    {
        var returnDictionary = new Dictionary<Predicate<string>, Action<MutuallyExclusiveValidation>>();
        Action<MutuallyExclusiveValidation> validation;
        Predicate<string> condition;

        validation = x => x.CheckA(lastvalue);
        condition = x => x == "A";
        returnDictionary.Add(condition, validation);

        validation = x => x.CheckB(lastvalue);
        condition = (x => x == "B");
        returnDictionary.Add(condition, validation);

        validation = x => x.CheckC(lastvalue);
        condition = (x => x == "C");
        returnDictionary.Add(condition, validation);

        return returnDictionary;
    }

This seems to work great, but I can't help thinking there's a better way. Any thoughts?

EDIT: Fabjan's answer is the one I'm going with for this particular problem. Thanks also to Tim Schmelter.


Solution

  • Create a 2d array and validate records with it using some method, something like:

    class MyClass
    {
        bool[,] matrix = new bool[,]
        {            
            {false, true, false},  // x1 y123
            {true, false, true},   // x2 y123
            {true, false, false},  // x3 y123
        };
    
        string[] xValues = { "A", "B", "C" };
        string[] yValues = { "A", "B", "C" };
    
        public bool IsValid(string value1, string value2)
        {
            return matrix[Array.IndexOf(xValues, value1), Array.IndexOf(yValues, value2)];
        }
    }
    
    
    class Program
    {
        static void Main()
        {
            MyClass c = new MyClass();
    
            Console.WriteLine(c.IsValid("A", "A"));
    
            Console.WriteLine(c.IsValid("B", "C"));
    
            Console.WriteLine(c.IsValid("A", "C"));
    
            Console.ReadKey();
        }
    }
    

    Output:

    Output