PHP: Dispatch tables, an alternative to switch hell
Earlier this week I was putting together a block of code which ended turning into a switch statement with a tangled mess of long case blocks, complicated fall throughs, and ultimately became impossible to follow. Being a stand up guy, I decided to refactor the block using a technique where the case blocks are converted into anonymous functions, indexed into an associative array, and then the correct function is called depending on the value. I haven’t seen this show up too often in PHP code so I thought I’d share.
So what’s the problem?
The life of a switch statement usually starts out relatively benign, you have a few simple conditions and each block is relatively compact:
<?php
switch( $dayOfTheMonth ){
case 1:
// do stuff
break;
case 15:
// pay people
break;
case 30:
case 31:
// do stuff
break;
default:
// no op
break;
}
But then, the switch grows and each case becomes complicated enough that it the entire block becomes mostly unreadable: Ellipsis for effect.
<?php
switch( $dayOfTheMonth ){
case 1:
// do stuff
// more crazy conditional logic
break;
case 2:
// do stuff
break;
....
....
case 14:
// do stuff
// maybe a while loop
break;
case 15:
// pay people
break;
...
...
case 29:
// do stuff
// maybe a while loop
break;
case 30:
case 31:
// do stuff
break;
default:
// no op
break;
}
At this point, its hard to reason about what’s going to happen because each case statement has presumably gotten so large and different conditions are “falling” through so the side effects are difficult to trace through.
An alternative
An alternative to using a normal switch statement is to use a dispatch table, which is basically an array of functions indexed by whatever variable you’d normally be “switching” on. The primary benefit to structuring the code this way is that you can easily reason about side effects since the only variables that can be changed are what captures the return value or anything passed by reference. In addition, since every case is a separate function its a bit easier to edit the code. So what does this look like? It’s actually pretty straightforward:
<?php
$dayFunctions = array(
1 => function(){
// do stuff
return "something";
},
2 => function(){
// do stuff
return "something";
},
...
15 => function(){
// pay people
return "something";
},
30 => function(){
// do stuff
return "something";
},
);
// Make 31 the same as 30
$dayFunctions[31] = $dayFunctions[30];
$res = array_key_exists($dayOfTheMonth, $dayFunctions) ? $dayFunctions[$dayOfTheMonth]() : null;
Extending from there, you could also call the function with arguments, potentially by reference, and even have all the functions be closures which capture the variables to avoid having to call with arguments.
Anyway, questions or comments always welcome.
Questions or comments about this? Email us at contact@setfive.com.