Pulling out the Switch: It’s Time for a Whooping

In my previous post I talked about how if-else and switch statements are very similar in that they both ignore the problem of combining data with code.

Today I am going to show you how to refactor switch statements to alleviate that problem.

There are some varied opinions on how to refactor switch statements which I believe derive from trying to treat all switch statements as the same.  I want to look at the kinds of switch statements that exist and why I recommend to refactor each one in a particular way.

Data to data Switch Statement

The first, most obvious kind of switch statement is one that maps one form of data to another form of data.

[sourcecode language=”csharp”] switch (state)
{
case "Florida": return "Tallahassee";
case "Idaho": return "Boise";
case "Arizona": return "Phoenix";
case "South Carolina": return "Columbia";
}
[/sourcecode]

This example is clearly mapping one piece of data to another.  The best refactor for this situation is to use a map.  In C# it is a dictionary, in Java a map.

[sourcecode language=”csharp”] var stateToCapitolMap = new Dictionary<string, string>()
{
{"Florida", "Tallahassee"},
{"Idaho", "Boise"},
{"Arizona", "Phoenix"},
{"South Carolina", "Columbia"}
};

return stateToCapitolMap[state];
[/sourcecode]

I cannot believe how many people argue against this refactoring.  It doesn’t look like much, but we have greatly separated logic from data and increased the maintainability of the code.

Before our refactoring, consider, how you would be able to read all the states and capitols from a file and insert them into the switch statement?  The only possible way would be through code generation.  Clearly this indicates a coupling of code and data.  The switch statement is formatted in such a way that it almost looks like data, but don’t let that fool you, it is code.

Consider the refactored example.  If we want to read the values from a file, it is simple.  So simple, that I’ll even show the code right here.

[sourcecode language=”csharp”] var stateToCapitolMap = new Dictionary<string, string>();
foreach (string line in File.ReadAllLines("StatesAndCapitols.txt"))
{
string[] stateCapitol = line.Split(‘,’);
stateToCapitolMap.Add(stateCapitol[0], stateCapitol[1]);
}
return stateToCapitolMap[state];
[/sourcecode]

Data to action Switch Statement

This kind of switch statement appears different than data to data, but it is actually very similar.  In this case we are mapping some data to a direct action that should be performed given that data.

Often this form of logic can be disguised by multiple actions happening in a case statement.

[sourcecode language=”csharp”] switch (move)
{
case "Up": MoveUp();
break;
case "Down": MoveDown();
break;
case "Left": MoveLeft();
break;
case "Right": MoveRight();
break;
case "Combo":
MoveUp();
MoveUp();
MoveDown();
MoveDown();
break;
}
[/sourcecode]

We can take the same approach here because really this is a form of mapping data to data.  The second data item is essentially the name of a method to call to perform an action.  We can illustrate this intent easily in C#.  (In Java, you will need to wrap the action into a set of classes with a common interface.)

[sourcecode language=”csharp”] var moveMap = new Dictionary<string, Action>()
{
{"Up", MoveUp},
{"Down", MoveDown},
{"Left", MoveLeft},
{"Right", MoveRight},
{"Combo", () => { MoveUp(); MoveUp(); MoveDown(); MoveDown(); }}
};

moveMap[move]();
[/sourcecode]

What we are essentially doing now is a dynamic look-up of the method to call based on the data.  We could even make this example data driven from a text file that specified how to map a move to a method name or list of method names, but that is far beyond on the scope of this post, and I don’t think I would recommend it unless you have a really good reason.

Data to multiple actions Switch Statement

If you are familiar with the techniques of refactoring switch statements, you make be shaking your head by now saying, “that guy is wrong, he needs to use a factory.”  Okay, well now we are going to do it.

In the data-to-action refactoring, I opted for the simplest solution that can work, instead of trying to over solve the problem by adding complexity in the form of a factory.

But, what happens when you have multiple switch statements in your code that operate on the same set of data?

[sourcecode language=”csharp”] switch (move)
{
case "Up": MoveUp();
break;
case "Down": MoveDown();
break;
case "Left": MoveLeft();
break;
case "Right": MoveRight();
break;
case "Combo":
MoveUp();
MoveUp();
MoveDown();
MoveDown();
break;
}

// … somewhere else

switch (move)
{
case "Up":
moveName = "Basic Up Move";
break;
case "Down":
moveName = "Basic Down Move";
break;
case "Left":
moveName = "Basic Left Move";
break;
case "Right":
moveName = "Basic Right Move";
break;
case "Combo":
moveName = "Up Up Down Down Combo Move";
break;
}
[/sourcecode]

Sure, we could refactor these both into maps or dictionaries.  But what will happen when we try and add a new move?  We’ll have to remember to add logic in both places or we’ll have a problem.  In the prior example we recognized that data was being mapped to an action, so we represented that as succinctly in code as possible.

In this example, the same data is being mapped to some data that describes a move and actions to perform.  We need some way to house these attributes that belong to the data we are switching on together, and we would like to have this all in one place, so we don’t have to change the code in multiple places.

Our best solution here is to use a factory that gives us the right kind of object that implements the behavior that should be tied to the data we are currently switching on.

[sourcecode language=”csharp”] public interface IMove
{
void DoMove();
string GetFriendlyName();
}

public class UpMove : IMove
{
public void DoMove()
{
// Do the move
}

public string GetFriendlyName()
{
return "Basic Up Move";
}
}

public static class MoveFactory
{
private static Dictionary<string, Func<IMove>> moveMap = new Dictionary<string, Func<IMove>>()
{
{"Up", () => { return new UpMove(); }},
{"Down", () => { return new DownMove(); }},
{"Left", () => { return new LeftMove(); }}
// …
};

public static IMove CreateMoveFromName(string name)
{
return moveMap[name]();
}
}
[/sourcecode]

You can see here that we are creating a factory which contains a dictionary which maps a move name to what kind of object to create.  Each move implements a common IMove interface.  (I only show some of the implementation here.)

Now in our code we can replace those switch statements with polymorphic behavior from our object returned from the factory.

[sourcecode language=”csharp”] IMove move = MoveFactory.CreateMoveFromName(name);
move.DoMove();
String friendlyName = move.GetFriendlyName();
[/sourcecode]

The nice thing about this implementation is that if we try to add a new move the IMove interface will require us to implement all the proper methods.  We make a change in one place and the compiler reminds us what we need to do.

Don’t jump straight to the factory

You may have heard the argument between using a factory or a dictionary to refactor switch statements before.  What I am trying to show in this blog post is that it depends on your situation.

The simplest solution is a dictionary or map.  Once you have a second place you are mapping the same data, you should move to a factory.  The factory then contains the mapping between a piece of data and a class.

I also wanted to note here that I didn’t use enumerations.  In real code you should.  I avoided them here to prevent adding one more layer of abstraction so that my example would not require as much explanation.

  • configurator

    In Java, the simplest solution is often an enum.

    enum Move {
    Up(“Basic Up Move”) {
    @Override
    public void DoMove() {
    MoveUp();
    }
    },
    Down(“Basic Down Move”) { … },
    (Left, right, combo …);

    private final String friendlyName;
    private Move(String friendlyName) {
    this.friendlyName = friendlyName;
    }
    public String getFriendlyName() {
    return friendlyName;
    }
    }

    Yes, this makes it impossible to read from a file to know what your actions are, but if that’s not a requirement, Java enums are generally pretty good at one-way mappings.

    • http://simpleprogrammer.com jsonmez

      Excellent point! Thanks for the comment, I didn’t put a Java solution here, but that is one that I frequently use, although I didn’t consider using an anonymous override int he constructor. It is certainly most appropriate in my last example.

  • Pingback: Switch is Just a Fancy If Else « Making the Complex Simple()

  • Pingback: The Morning Brew - Chris Alcock » The Morning Brew #667()

  • KevDog

    What I can’t believe is that I just figured out the “It’s time for a whooping” joke. I need more caffeine this morning.

    On the plus side, I did implement the map function in some code I’m writing, a very elegant solution. Many thanks!

    • http://simpleprogrammer.com jsonmez

      Heh, if you’ve never been whooped by a switch, the reference might be a little obscure. :P
      Glad I could be of help.

  • Pingback: Explaining What Action<> And Func<> Are « Making the Complex Simple()

  • KevDog

    I’ve been using this technique a lot lately and was curious as to how you would recommend refactoring this little bit of fun:

    public static DataFileParser GetParser(DataFile dataFile)
    {
    switch ((FileType)dataFile.ValidationFormat.FileType)
    {
    case FileType.PDF:
    return new PdfParser(dataFile);
    case FileType.Image:
    return new ImageParser(dataFile);
    case FileType.CsvWithHeaderRow:
    return new CsvParser(dataFile, true);
    case FileType.Csv:
    return new CsvParser(dataFile, false);
    default:
    break;
    }

    throw new NotImplementedException(“There is no parser for ” + dataFile.ValidationFormat.FileType.ToString());
    }

    • http://codeitalready.com TJB

      I see 2 options.
      1) a map of FileType to DataFileParser
      2) add a static FileType property to the DataFileParser class and query against the DataFileParser implementations to find who supports the particular type.
      Yeah, I guess #1 is easier ; )
      Hope this helps, T.J.

  • Pingback: Refactoring Switches Advanced « Making the Complex Simple()

  • http://gravatar.com/sureshvr sureshvr

    this post is awesome. I may sound silly. switch statement has default supported. its kind of fallback. Where as Dictonary doesn’t has default element. so how do we support that?

    var stateToCapitolMap = new Dictionary()
    {
    {“Florida”, “Tallahassee”},
    {“Idaho”, “Boise”},
    {“Arizona”, “Phoenix”},
    {“South Carolina”, “Columbia”}
    };

    return stateToCapitolMap[nostate];

    • http://simpleprogrammer.com jsonmez

      Thanks, great observation:

      http://simpleprogrammer.com/2011/08/14/making-switch-refactorings-better-defaultable-dictionary/

      I actually created a defaultable dictionary that solves this problem.

    • http://gravatar.com/sureshvr sureshvr

      rather than how do we support. my question should have been how we handle it?

      • http://gravatar.com/sureshvr sureshvr

        you are fast. thanks for your post. your post specially these kinds are addicting for me. Looking forward for more great post.

  • Pingback: Refactoring Switches to Classes « Making the Complex Simple()

  • http://twitter.com/ppetrovdotnet pip010 (@ppetrovdotnet)

    it is amazing how many switch-like statements you can do, yet you can’t do this:

    switch(typeof(arg0))
    {

    }

  • http://twitter.com/ppetrovdotnet pip010 (@ppetrovdotnet)

    it is amazing how many switch-like statements you can do, yet you can’t do this:

    switch(typeof(arg0))
    {
    case MyClass: break;
    }

  • Andy Davis

    moveMap can just be Dictionary instead of Dictionary<string, Func> .

    But I’m guessing you had a func because of the Combo which you left out when doing the factory refactor. Maybe you would just have

    ComboMove : IMove too.

    Either way, I don’t think you need the Func.