Reducing Code Complexity on Switch-blocks






4.82/5 (49 votes)
Using a Dictionary instead of a switch-case construction to reduce complexity and increase testability
Introduction
A switch-block becomes complex very easily. All code is placed in one method and parts of it are used multiple times. In a lot of cases, a switch-block is based on an enum
. Next to that, it's not possible to use a fall-through in C#. The only way to go from case to case is the use of the goto
statement, which isn't a good idea when reducing complexity. Although I've written the examples in C#, it wouldn't be hard to apply the principles to other languages.
A Solution
A nice solution to get rid of large switch constructions is the use of a Dictionary<enum,delegate>
. This way every element of the enum
can be attached to a method. It is possible to call the right method for every enum
possible. Because every enum
has its own method, it's very easy to use this construction in a testdriven environment. Another nice thing is that it's no problem to call one method from another. Let me explain a little further with an example and some pseudo code. Imagine a program that prepares food. This part contains the recipes.
Let us start with the following enum
:
Enum food{
Apple,
ApplePie,
AppleJuice,
Pizza
}
It's not hard to imagine that all of the foods need a specific preparation, but that some actions need to be done for different foods, like peeling an apple or baking in the oven.
To add the preparations to the Dictionary<enum, delegate>
, we first need to define a delegate
method:
delegate bool Preperation();
Now we need to define the actual preparation methods for every item of the enum
, making sure they're declared the same way as the delegate
, thus the same parameters and return value. The method returns a boolean when preparation is successful.
In this example, the methods may look something like this:
bool PeelApple()
{
// code to remove peel
return true;
}
bool BakePie()
{
PreheatOven(180.0);
PeelApple();
CreatePie();
while(!doneBaking())
Bake();
return true;
}
bool MakeAppleJuice()
{
PeelApple();
Juicify();
return true;
}
bool BakePizza()
{
PreheatOven(160.0);
CreatePizza();
while(!doneBaking())
Bake();
return true;
}
Notice that BakePie()
and MakeAppleJuice()
both call the method PeelApple()
. This is not possible in a switch
– case constructor, unless you call the methods from each case.
Now all that's left is to create and initialize the Dictionary
.
Dictionary FoodPreperation;
..
FoodPreperation = new Dictionary<food, Preperation>();
FoodPreperation.add(food.Apple, new Preperation(PeelApple));
FoodPreperation.add(food.ApplePie, new Preperation(BakePie));
FoodPreperation.add(food.AppleJuice, new Preperation(MakeAppleJuice));
FoodPreperation.add(food.Pizza, new Preperation(BakePizza));
Calling the methods is done by:
food FoodOfChoice = food.ApplePie;
FoodPreperation[FoodOfChoice]();
In the last code snippet, the method that goes with food.ApplePie
is executed.
History
- 07 Oct 2008 - Initial upload