FreeBSD FridayProgramming / DevelopmentWPF Databinding TutorialMy Posts

Welcome to my technology learning website. The goal of this site is to learn and retain as much information as possible. I consider this blog my knowledge base and hope that you both find and share knowledge here as well. Also for those wondering, Rhyous is pronounced ‘rī-əs. As in the words ‘Rye‘ and ‘us‘ compounded into a two syllable word.

Please take a moment to subscribe, or setup an RSS feed so you never miss a post.

Eliminating Cylclomatic Complexity by replacing switch/case with a method or a Dictionary>

Cyclomatic Complexity is a measurement of how many paths your code could traverse. Switch/case statements are often immediate Cyclomatic Complexity concerns.

Cyclomatic Complexity Example

Imagine the following code:

public void Foo(int val)
{
    switch (val)
    {
        case 0:
            // ... code here
            break;
        case 1:
            // ... code here
            break;
        case 2:
            // ... code here
            break;
        case 3:
            // ... code here
            break;
        case 4:
            // ... code here
            break;
        case 5:
            // ... code here
            break;
        case 6:
            // ... code here
            break;
        case 7:
            // ... code here
            break;
    }
}

In the above code, there are 8 paths. The Cyclomatic Complexity is not small. This makes unit tests difficult. It is complexity that is unnecessary. Unnecessary complexity leads to bugs.

Replacing a switch/case statement

Almost invariably, the switch/case statement can be replaced in a way that removes the cyclomatic complexity. There are three replacements methods I am going to dicuss here.

  1. Replace with Methods
  2. Replace with Methods and a dictionary provided parameter.
  3. Replace with Dictionary>

To know which one to choose, you have analyze the code. In the above example, I left out the code. I just put a place holder for it.

// ... code here

After analyzing the code, you should be able to pick one of the following:

Method

So you should use a method if you can.

Example 1
Imagine the following snippet. This is an easy one. You should pick out the replacement without having to be told what it is.

public void Foo(int val)
{
    switch (val)
    {
        case 0:
            Bar.Do(0);
            break;
        case 1:
            Bar.Do(1);
            break;
        case 2:
            Bar.Do(2);
            break;
        case 3:
            Bar.Do(3);
            break;
        // , ... , 
        case 7:
            Bar.Do(7);
            break;
    }
}

As you can see here, each method is easily following a pattern. We can replace the switch statement with a single method call.

public void Foo(int val)
{
    Bar.Do(val);
}

Look, that one was obvious and it was intended to be obvious. It isn’t always going to be obvious.

Example 2
Imagine the following snippet. This is also an easy one, but not quite as easy as above. Hopefully, you pick out the replacement without having to be told what it is.

public void Foo(int val)
{
    switch (val)
    {
        case 0:
            Bar.Do0();
            break;
        case 1:
            Bar.Do1();
            break;
        case 2:
            Bar.Do2();
            break;
        case 3:
            Bar.Do3();
            break;
        // , ... , 
        case 7:
            Bar.Do7();
            break;
    }
}

Notice there is a pattern. We know the method name to call on Bar because we can see the pattern: “Do” + val

We could easily use reflection to eliminate cyclomatic complexity here.

Note: While reflection is often deemed slow and a cause of performance issues, in practice, unless looping through large data sets, any performance loss from reflection is not measurable.

public void Foo(int val)
{
    typeof(Bar).GetMethod("Do" + val).Invoke();
}

We traded Cyclomatic Complexity for Reflection and a possible, but unlikely performance issue. If this code is used in a loop for millions of instances in a data set, you might not want to do this.

Method and a dictionary provided parameter

Example 1
Imagine the code is more like this, in which different case statements call different overloaded values.

public void Foo(int val, ObjA a)
{
    switch (val)
    {
        case 0:
            Bar.Do(a, 3);
            break;
        case 1:
            Bar.Do(a, 7);
            break;
        case 2:
            Bar.Do(a, 5);
            break;
        case 3:
            Bar.Do(a, 100);
            break;
        case 4:
            Bar.Do(a, 9);
            break;
        case 5:
            Bar.Do(a, 12);
            break;
        case 6:
            Bar.Do(a, -1);
            break;
        case 7:
            Bar.Do(a, int.MaxValue);
            break;
    }
}

So every case statement is doing something different. However, notice that what it does different is a static int. We can create a static parameter dictionary of type Dictionary.

internal Dictionary<int, int> ParamMap = new Dictionary<int, int> { {0,3}, {1,7}, {2,5}, {3,100}, {4,9}, {5,12}, {6,-1}, {7, int.MaxValue } };

public void Foo(int val, ObjA a)
{
    Bar.Do(a, ParamMap[val]);
}

This uses a static, prebuilt dictionary that completely eliminates Cyclomatic Complexity.

Notice all the Cyclomatic Complexity is gone. This code never branches. There is very little left to test.

<b>Example 2</b>
Imagine the code is more like this, in which different case statements call different overloaded values.


public void Foo(int val, ObjA a, ObjB b, ObjC c)
{
    switch (val)
    {
        case 0:
            Bar.Do(a);
            break;
        case 1:
            Bar.Do(b);
            break;
        case 2:
            Bar.Do(c);
            break;
        case 3:
            Bar.Do(a, c);
            break;
        case 4:
            Bar.Do(b, c);
            break;
        case 5:
            Bar.Do(b, c, a);
            break;
        case 6:
            Bar.Do(b, c, a * .01);
            break;
        case 7:
            Bar.Do(a, b, c);
            break;
    }
}

This looks harder doesn’t it. The Cyclomatic Complexity can still be simplified. How are we going to do it? We can’t use a

Well, one option is to use a Dictionary.

public void Foo(int val, ObjA a, ObjB b, ObjC c)
{
    var Dictionary<int, object[]> paramMap = new Dictionary<int, object[]>();
    paramMap.Add(0, new []{ a });
    paramMap.Add(1, new []{ b });
    paramMap.Add(2, new []{ c });
    paramMap.Add(3, new []{ a, c });
    paramMap.Add(4, new []{ b, c });
    paramMap.Add(5, new []{ b, c, a });
    paramMap.Add(6, new []{ b, c, a * .01 });
    paramMap.Add(7, new []{ a, b, c });
    typeof(Bar).GetMethod("Do").Invoke(paramMap[val]); // Reflection allows for passing in a dynamically sized list of parameters.
}

The solution is almost exactly the same as above. The differense are:

  1. The dictionary is dynamic, based on the passed in parameters, so we have to build it dynamically.
  2. The parameters are dynamic so we call the method with reflection to allow for dynamic parameters.

The dictionary still completely eliminates Cyclomatic Complexity. Notice all the Cyclomatic Complexity is gone. This code never branches. There is very little to test.

There is the overhead of creating a Dictionary and the overhead of reflection, but again, unless you plan to use this for looping through large data sets, the performance difference is negligible.

Dictionary>

Sometimes there isn’t much common at all. Sometimes, the complexities very greatly.

// ... code here

Imagine the code that goes there is vastly different. Imagine you just can’t find much common ground. In these situations, you can use Dictionary>. The pattern is to put the dictionary in its own class file. Then the object that uses it can have an injectable IDictionary>. Injection options are: Constructor injection, Method injection, property injection. I lean toward a property injection variation called a Lazy Injectable Property.

Question: What generic paramaters should be used for the Dictionary?
Answer: The TKey is clearly the type of the val property, which in the example is an int.

Question: What generic parameters should be used for the Func<>?
Answer: Well, you need to think through to get this answer. First, you need to find the Lowest Common Parameter Set. Second you need to check the return type.

Finding the Lowest Common Parameter Set

If you look at one of the above methods, you can easily get the lowest common parameter set by writing down each and every parameter pass in. Remember this one from above?

public void Foo(int val, ObjA a, ObjB b, ObjC c)
{
    // Switch/case statement here . . .
}

The lowest common parameter set is: a, b, c. If you look at the full implementation further up, you will notice that none of the methods take in val, so val is not included in the parameter set.

So now we can create our Dictionary. We will have three input parameters.

Action<> vs Func<>

This is easy. The only notable difference is that Action<> takes in parameters and returns void. Func<> takes in parameters and returns the type specified in the last generic type.

So as there is no return value in the above example, we can use this code:

public Class FuncDictionary : Dictionary<int, Action<ObjA, ObjB, ObjC>>
{
    public FuncDictionary()
    {
        this.Add(0, (a, b, c) => { Bar.Do(a); } ); // Parameters b, c are ignored. That is ok.
        this.Add(1, (a, b, c) => { Bar.Do(b); } );
        this.Add(2, (a, b, c) => { Bar.Do(c); } );
        this.Add(3, (a, b, c) => { Bar.Do(a, c); } );
        this.Add(4, (a, b, c) => { Bar.Do(b, c); } );
        this.Add(5, (a, b, c) => { Bar.Do(b, c, a); } );
        this.Add(6, (a, b, c) => { Bar.Do(b, c, a * .01); } );
        this.Add(7, (a, b, c) => { Bar.Do(a, b, c); } );
    }
}

Now look at the foo code.

// Lazy injectable property
internal IDictionary<int, Action<ObjA, ObjB, Objc> ActionDictionary
{
    get { return _ActionDictionary ?? (_ActionDictionary = new FuncDictionary()); }
    set { _ActionDictionary = value; }
} private IDictionary<int, Action<ObjA, ObjB, Objc> _ActionDictionary;

public void Foo(int val, ObjA a, ObjB b, ObjC c)
{
    ActionDictionary[val].Invoke(a, b, c);
}

In all the previous methods, we resolved Cyclomatic Complexity by taking a method with 8 branches, and reducing that 1 method to 0 branches. We can also get 100% code coverage with 1 unit test.

1, Methods
0, Cyclomatic Complexity

In this final Dictionary> example, we end up with 8 methods that need testing.

8, Methods
0, Cyclomatic Complexity

The reason is that we still have to test all 8 methods in the FuncDictionary. However, when that work was in the switch/case statement, those were harder to isolate for unit tests. Now, all eight methods are isolated and unit tests are simplified.

Leave a Reply

How to post code in comments?

*