Advertisements
Posted by: jsonmez | August 14, 2011

Making Switch Refactorings Better – Defaultable Dictionary

I’ve written before on the idea of refactoring a switch to a Map or Dictionary.

There is one major problem that I have been running into though.  Switch statements and dictionaries are not functionally equivalent for one major reason…

Switches allow for default

I kept struggling with this when I would implement a dictionary to replace a switch.  How can I deal with the default case?

There are of course many ways to deal with the default case in a dictionary or map, but I didn’t really like any of the solutions because they either required me to remember to check to see if my entry was in the dictionary before looking it up, or to rely on catching an exception.

Let me give you an example:

switch(vegetables)
{
	case Vegetables.Carrot:
		DoCarrotStuff();
		break;

	case Vegetables.Spinach:
		EatIt();
		break;

	case Vegetables.Peas:
		FeedToDog();
		break;

	default:
		Butter();
		Salt();
		SprinkleCheese();
		Eat();
}

Converting this to a dictionary we get something like:

var vegetableActionMap = new Dictionary<Vegetable, Action>
{
	{ Vegetable.Carrot, () => DoCarrotStuff() },
	{ Vegetable.Spinach, () => EatIt() },
	{ Vegetable.Peas, () => FeedToDog() }
}

Action result;
if(!vegetableActionMap.TryGetValue(vegetable, out result)
{
     Butter();
	  Salt();
     SprinkleCheese();
     Eat();
}
else
   result();

So clunky, just to handle the default case.

Would be much better do something like this:

var vegetableActionMap = new Dictionary<Vegetable, Action>
{
	{ Vegetable.Carrot, () => DoCarrotStuff() },
	{ Vegetable.Spinach, () => EatIt() },
	{ Vegetable.Peas, () => FeedToDog() }
}.WithDefaultValue( () => { Butter(); Salt(); SprinkleCheese(); Eat(); });

vegetableActionMap[vegetable]();

Well now you can!

Enter DefaultableDictionary!

Also the first thing I ever put on GitHub!

The idea is pretty simple, I am just creating a decorator for IDictionary.

The DefaultableDictionary has a constructor that takes an IDictionary and a default value.

It then delegates all of the methods to the passed in IDictionary reference.  For the methods that look up a value, it handles returning the default if the key doesn’t exist in the dictionary.

I created an extension method that lets you just put a .WithDefaultValue() on the end of your dictionary declaration in order to auto-magically give you a DefaultableDictionary back with that default value.

Sleep well my friend

Knowing that you can not create a dictionary that has a default value which is returned instead of throwing an exception if the key passed in is not found.

I have no doubt that in 3rd world countries children are still starving, but in 1st world countries children with VS Express hacking away at iPhone applications using MonoTouch will not have to catch exceptions from dictionaries that do not know how to just return a default value.

So now there is no excuse!  Refactor those switches!

As always, you can subscribe to this RSS feed to follow my posts on Making the Complex Simple.  Feel free to check out ElegantCode.com where I post about the topic of writing elegant code about once a week.  Also, you can follow me on twitter here.
Advertisements

Responses

  1. […] Making Switch Refactorings Better – Defaultable Dictionary – John Sonmez shares an implementation of a dictionary class which provides for returning a default value when no match is found, making it ideally suited to refactoring switch statements into a map / dictionary approach and handling the default case of the switch in an elegant way. […]

  2. […] Making Switch Refactorings Better – Defaultable Dictionary (John Sonmez) […]

  3. Nice work. I like the “this” method that allows me to issue Vegetable.Pees() => SomeMethod(). It makes the code really expressive. WithDefaultValue is just icing on top of the cake.

  4. Thanks. This is a great way to replace switch statements. I have been cringing lately every time I used a switch statement. This just seems so much cleaner and more expressive. Would be great to see this available as a NuGet package.

    • Hmm that is an interesting idea, I didn’t consider that, but perhaps I’ll make a NuGet package for it.

  5. I did a session at NetDug (Boise based user group) a couple of years ago on this same thing. I got all sorts of strange looks during that presentation.

    I also had a few people look at that, thinking it was the greatest thing ever, and using instead of simple if/else situations.

    It is a great technique, but it has its limits.

  6. […] Making Switch Refactorings Better – Defaultable Dictionary […]

  7. Why are we refactoring our switches? Does this really read better? At least when it was a switch the compiler would warn me if I added a new vegetable and no corresponding case.

    • Because we can put the dictionary in a single place and it can be changed at runtime. A switch statement tends to get repeated in various places in the code where it must be maintained.

      • Seems to me that you’ve taken something compile-time-checked and moved it to a place where it can explode at run-time. Added a bit of complexity as well – funny on a site titled “making the complex simple” 😉

        Switch statements can often be made to go away entirely by stepping back and looking at your object model. Replacing one trivially with a dictionary is not making anything bad go away.

        • To me the dictionary is a much cleaner implementation and much more flexible. I agree that you aren’t changing too much by using a dictionary instead of a switch, but sometimes a full blown inheritance hierarchy or polymorphism us overkill.

          Take the concept of “commands.” In many programs it doesn’t make sense to use a full blown command pattern or something similar, but a switch is very messy.

          Take a look at this post: http://simpleprogrammer.com/2010/08/12/switch-is-just-a-fancy-if-else/
          And the following one. That better explains my point.

  8. […] even created a defaultable dictionary for refactoring a switch statement into a dictionary of […]

  9. If you do a lot of reading from positions where there are no elements yet (i.e. getting defaultValue), it’s better to check if there is a value and then read it or return defaultValue. Exception handling is very slow and in my application it pretty much ruined performance 🙂

    • Good point, I agree. It should be more of a catch-all rather than a planned logic path. Would like to see the base dictionary implemented this way though.

      Getting rid of the exception handling and just checking for the value existing in the implementation would this the performance.

  10. And by that I meant implementing that in the DefaultableDictionary, not doing it from the outside. Thanks for a great piece of code btw!

  11. Thanks for this excellent article for replacing switches. I have got a problem here.
    I am using dictionary in a method which returns a custom type. How can I define an action which when retrieved (in this example -vegetableActionMap[vegetable]()) return the type returned by

    DoCarrotStuff().Thanks in advance.
    Hemanth

    • Try this post: http://simpleprogrammer.com/2010/09/24/explaining-what-action-and-func-are/

      I think that should help you.

      • Thanks. That mean you can return a value from the delegate method when using Action as your dictionary’s value?

        • Yes, you would use a Func in that case. So, say you have a method the returns an int and takes an int.
          You would use Func with two generic params of int and int. (Comments for my blog strips out the greater and less than signs.)

  12. Thanks again. I think i wrote “you can’t (cannot) return” in my previous reply.
    How about refactoring default of switch statement in dictionaries while using Func.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

%d bloggers like this: