Refactoring Dispatch Tables into Objects
One of the cool ways of doing things in Perl is to use a dispatch table. The most obvious dispatch table is a hash of subroutines:
my $x;
my $table = {
GET => sub { return $x },
PUT => sub { $x = $_[0] },
};
sub dispatch {
my ($method, $data) = @_;
if (my $fn = $table->{$method}) {
$fn->($data)
} else {
die 'METHOD NOT ALLOWED!'
}
}
This is a pretty cool thing to be able to do easily. But what’s even cooler is that we can refactor the dispatch table into a package, which allows us to make objects that can override bits of the dispatch table:
package Table {
sub new { bless {}, $_[0] }
sub GET { return $_[0]->{x} }
sub PUT { $_[0]->{x} = $_[1] }
}
package SubTable {
use parent 'Table';
sub DELETE { delete $_[0]->{x} }
}
my $table = SubTable->new;
sub dispatch {
my ($method, $data) = @_;
if (my $fn = $table->can($method)) {
$table->$method($data)
# the following would also work and would be
# marginally faster
# $table->$fn($data)
} else {
die 'METHOD NOT ALLOWED!'
}
}
Note that one thing you might consider is prefixing the methods with “public_” or something like that; just in case your dispatcher object as private methods you don’t want web browsers executing. Generally though I’d just not put such methods in my dispatcher, but I haven’t yet made anything super complex using this pattern. I am using the pattern for a pluggable dashboard system at work, but the methods there are all called GET_foo or POST_bar, so users can’t run methods I didn’t specifically make for HTTP.
Posted Thu, Aug 25, 2011If you're interested in being notified when new posts are published, you can subscribe here; you'll get an email once a week at the most.