fREWdiculous!
24 Aug
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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.
4 Responses for "Refactoring Dispatch Tables into Objects"
Maybe I’m missing something, but I don’t get what is so great about replacing a dispatch table with an object. Or rather, yes, I agree that calling methods on objects uses Perl’s namespaces as dispatch tables (the way all objects do) and you get all the benefits and disadvantages therein, including subclassing.
In this example, your storage variable ($x) becomes global, as do your subroutines. In the original, they are lexical. As you note, checking can() and calling as a method slows things down. (This may or may not matter for any given application.)
In Moose-land, you could use roles instead of subclasses for better composability of methods. E.g. have a dispatcher role that requires ‘GET’, ‘PUT’, etc. methods and then have other roles that provide those methods with whatever customization you want. Compose them into a class (or into an object) and you get similar benefits without requiring inheritance.
@dagolden: I was unclear in the post I guess. Dispatch tables aren’t wrong and I don’t think they should all be converted to objects; I just think objects allow another level of flexibility. And as for using roles instead of inheritance to create the objects, yes, it’s totally awesome, I just didn’t want to have people think they needed roles to use this technique. Or maybe I just didn’t want to explain roles in a post about something other than roles
Interesting. I’m just reading to keep my Perl fluent, but shouldn’t it be
sub PUT { $_[0]->{x} = $_[«1»] } # (used «» for highlighting only)
in package Table?
@Bernhard: Woops! Good call, fixing now
Leave a reply