http://qs321.pair.com?node_id=587246


in reply to When should I use a dispatch table?

I think performance optimization should not be the principle determining factor in deciding which technique to use. Unless your application is running unacceptably slowly and you've identified the multi-branch if/else section as a bottleneck, it's ridiculous to change that to something else just for a performance gain.

Maintainability, and design — as in consistency of — those are of primary importance.

I like dispatch tables. They're clever. But at the same time, they raise a red flag for me — the same one I see whenever hashes are used to store a static set of entities in an application: It completely undermines the safety of strict. (There are steps you can take to regain some of this safety, such as using Tie::StrictHash.)

The solution is to make the subs named class methods. I recently did this on a project at work.

I had two dispatch tables, one for the "real" functions and one for a set of stubs, to be used when debugging.

Old way:

my %real_functions = ( wipe_system => sub { system "rm -rf /" }, ); my %debug_stubs = ( wipe_system => sub { warn "wiping system (no, not really)\n" }, ); my $funcs = $debug ? \%debug_stubs : \%real_functions; $funcs->{'wipe_system'}->();

New way:

{ package SystemFunctions::Real; sub wipe_system { shift; # will be the class name system "rm -rf"; } } { package SystemFunctions::Debug; sub wipe_system { shift; # will be the class name warn "wiping system (no, not really)\n"; } } my $funcs = $debug ? 'SystemFunctions::Real' : 'SystemFunctions::Debug'; $funcs->wipe_system();

Now, it sometimes won't be convenient to do this, due to the complication of packages. In my case, it was; in fact, it was a significant improvement, since it gave me a convenient place to encapsulate all my "system functions", which hitherto had lived in the main namespace.

In the very simplest cases, you don't need to worry about packages at all:

my %dispatch = ( incr => sub { $x++ }, decr => sub { $x-- }, ); $dispatch{$op}->();
becomes
sub incr { shift; $x++ } sub decr { shift; $x-- } main->$op();

Note: I only left the shift in as a reminder that it will always be necessary unless the sub takes no arguments (or, as I sometimes do, you pop the args rather than shifting them).

Finally, what about default cases? One possibility is to use AUTOLOAD.

We're building the house of the future together.