dcsimg

Pluggable Behavior With a Twist

To many, Smalltalk remains the canonical object-oriented programming language. But Perl can leverage the best practices of Smalltalk and do more. Randal Schwartz reminisces and shares his usual pearl of wisdoms.
My object-oriented programming experience dates all the way back to shortly after the introduction of Smalltalk-80. I worked for Tektronix at the time, and the company was selected as one of the few organization to get firsthand experience (at least outside of Xerox PARC) with a new-fangled, “mouse and windows” interface that has since become ubiquitous. Although I wasn’t in the group that worked in liaison with PARC directly, my work group managed to obtain one of those 68K- based Magnolia machines that ran the Smalltalk-80 “image,” and I got to play with it in all of my spare hours in the early part of 1983.
Although I don’t recall meeting him at the time, my fellow Tektronix employee Kent Beck was also playing with Smalltalk, and along with co-worker Ward “Wiki” Cunningham, would go on to become a driving force in the field of Smalltalk and object-oriented design. I still argue that my Smalltalk experience (both at Tektronix and at a startup company immediately following that) has made me a better objected-oriented programmer. (If you want to play with Smalltalk today, Squeak is a wonderful cross-platform, free Smalltalk, available from http://www.squeakland.org/).
Somewhere in the mid-’90s, I stumbled across Kent Beck’s Smalltalk Best Practice Patterns at Powell’s Technical Bookstore in downtown Portland. I immediately fell in love with the book, because it condensed a lot of what I had learned (and re-learned) from hacking Smalltalk and Perl objects into short, digestible, and yet seemingly comprehensive snippets of what really works when you’re creating objects correctly. To this day, nearly every time I grumble at a 50-line Perl method that I want to subclass but can’t, or discover yet another a hardwired constant buried in the middle of some code that I want to adjust, I’m reminded of the wisdom of his book.

Better Than Smalltalk?

Recently, I was asked to create a full-day object oriented (OO) programming course for a client. I already have OO materials in the “Alpaca” course, but those examples are intended more for people who already have some of the OO theory down, and not so much for someone learning OO from scratch. But starting with that, I realized that what I wanted to impress upon my students was some of the wisdom that made average OO programmers into great OO programmers. I realized that I just needed to go dig up my dusty, dog-eared copy of Beck’s book, and figure out how many of his patterns translate directly into Perl. Surprisingly, the majority were directly usable.
But when I was typing up the descriptions for the Pluggable Selector and Pluggable Block patterns, I realized that Perl actually had an advantage over Smalltalk for implementing variable behavior in an object.
Let me explain.
Ideally, the only difference between various objects of a given class is their state. In other words, for a given state and a given new method call, all of the objects in a class behave identically. After all, if the behavior had to be different, one would simply subclass the class and capture the distinct behaviors based on extended or overridden methods. For example, if you wanted a file downloader to sometimes ring a bell when finished, but other times simply open a browser window, you’d subclass the FileDownload class as FileDownload::BellRinger and FileDownload::BrowserOpener
However, there are times when the subclassing approach is impractical. Consider, for instance, a class that serves as the abstract base class for a number of derived classes (download via proxy, download using throttled bandwidth, and so on). If you had to first create subclasses for each bit of the varying behavior,you’d get an explosion of child classes (download via proxy and ring a bell, download throttled opening a browser, and so on). Or suppose the behavior is really two or three independent parts; again, the number of subclasses would be impractical to maintain.
And thus, we typically resort to some sort of pluggable behavior, such as passing in a flag telling what to do, or passing in some sort of coderef to execute at a particular step, or even passing in some object that cooperates in the completion of a stage of the task.
In Beck’s book, the pattern of Pluggable Selector provides a selection between various methods based on passing the name of the method in as a parameter. In Perl, we’d implement this style as an indirect method call in which the name of the method is held in a simple scalar:
my $method_to_call = ’do_this’;
$self->$method_to_call($arg1, $arg2, @arg3);
In this case, Perl looks up do_this just as if I had entered:
$self->do_this($arg1, $arg2, @arg3);
Amazingly enough, even though you might consider this a symbolic reference, such a call is permitted even under use strict. You could use this for the file downloader by passing a selection in to the constructor…
my $dl = FileDownload->new(when_complete => ’ring_bell’);

$dl->download("http://example.com/foobar");
… and the constructor would squirrel away the value for when_complete into some instance (member) variable, then use it at the end of the download:
sub new {
my $class = shift;
bless { @_ }, $class;
}

sub download {
my $self = shift;

# download complete, time to do whatever
my $action = $self->{when_complete} || "ring_bell";
$self->$action($url, $localfile);
}

sub ring_bell {
my $self = shift;
my $url = shift;
my $localfile = shift;
print "\a\a\aDownload of $url complete into $localfile!\a\a\a";
}

sub open_url {
my $self = shift;
my $url = shift;
my $localfile = shift;
system "open", $localfile; # OSX open command
}
So, based on the method name stored in the when_complete instance variable, you get a different behavior when the download is complete. Of course, you wouldn’t want just any method to be used there, but you should allow for a child class to have additional methods. (One possible solution is to ensure the method begins or ends with a fixed pattern of characters, and reserve that area of the namespace for things that might be used in this manner.)
And that’s one of the cool things about this pattern. Suppose I subclass FileDownloader. I can provide additional “what to do when the download is complete” methods in my derived class, and then just pick a totally new selector in my when_complete value, and it’ll call my routine instead. Had this all instead been coded at the tail of download() with a multiway if-elsif-else, the class would have been less reusable and extensible. When you pull up $action, you’re also providing a default behavior as well, falling back to ring_bell() unless something else was specified.
“But wait… There’s more! ”, as the old late-night TV ads would go. And there is. The syntax that invokes an indirect method call can also call an arbitrary code reference as if it were a method, passing the class or instance as the first parameter.
So, you could also write:
my $action = sub {
my $self = shift;
my $url = shift;
my $filename = shift;
print "\a\a\aDownload of $url complete into $filename!\a\a\a";
};

$self->$action($url, $localfile);
Even though $action is now a coderef, we’ll still invoke the subroutine, passing the $self parameter ahead of $url and $localfile. But this coderef could have been provided at the new time:
my $bell_ringer = FileDownloader->
new(
when_complete => sub {
my $self = shift;
my $url = shift;
my $filename = shift;
print "\a\a\aDownload of $url
complete into $filename!\a\a\a";
},
);

$bell_ringer->download("…");
And this is Beck’s Pluggable Block pattern: pass a coderef to indicate some action to be performed at certain times in the life of the object.
Yes, with one syntax, we get both patterns of Pluggable Selector and Pluggable Block. Even Smalltalk would have a bit of a hard time with that! (Actually, you’d just need to extend Object#perform: to dispatch differently for a block versus a symbol, but that’s not the default behavior.)
Listing One is a longer example of a full class taking advantage of these patterns. The Plugh object has two operations: open a file, and take the reciprocal of a number.
LISTING ONE: An example of the Pluggable Selector and Pluggable Block patterns

package Plugh;

sub new {
my $class = shift;
bless { @_ }, $class;
}

sub on_error_ignore {
my $self = shift;
}

sub on_error_warn {
my $self = shift;
warn $@;
}

sub on_error_die {
my $self = shift;
die $@;
}

sub do_something_dangerous {
my $self = shift;
my $task = shift; # coderef
eval { $task->(@_) };
if ($@) {
my $error = $@;
my $action = $self->{on_error} || ’on_error_die’;
$self->$action($@);
}
}

sub reciprocal {
my $self = shift;

my $somevalue = shift;

my $result;
$self->do_something_dangerous
(sub { $result = 1 / $somevalue });
return $result;
}

sub open_file {
my $self = shift;

my $file = shift;

my $filehandle;
$self->do_something_dangerous
(sub { open my $h, “<”, $file or die “Cannot open $file: $!”;
$filehandle = $h;
});
return $filehandle;
}

1;

If you create a default Plugh, the code calls die if something goes wrong:
my $basic_plugh = Plugh->new;
Now, try some operations:
my $half = 
$basic_plug->reciprocal(2);
my $secret_handle =
$basic_plugh->open_file("/etc/passwd");
But if you try anything that would throw an exeception, that exception is thrown:
my $zero = $basic_plugh->reciprocal(0); # dies
my $missing = $basic_plug->open_file("/nonesuch"); # dies
Now, let’s modify the behavior:
my $warning_plugh = Plugh->new(on_error => ’on_error_warn’);
The last two exceptions are now simply turned into warn s! And if you want to be sophisticated, you can even pass in your own decision logic:
my $xyzzy = Plugh->new
(on_error =>
sub {
my $self = shift;
for (shift) { # alias error to $_
die $@ unless /Cannot open/;
warn $@;
}
}
);
Now, a bad file open is simply warned, but any other error is fatal. Imagine combining this with Exception::Class to create a sophisticated framework where you could choose if errors are thrown in basic ways, or select a more complicated algorithm, all by passing the right thing in as a parameter. Yes!
The classic patterns can be useful even in modern languages like Perl. I’d highly recommend picking up a copy of Beck’s book, and grabbing a Smalltalk system to play with. Or just apply the techniques to your Perl hacking. Until next time, enjoy!

Randal Schwartz is the Chief Perl Guru at Stonehenge Consulting. You can reach Randal at class="emailaddress">merlyn@stonehenge.com. You can download the code used in this column at http://www.linux-mag.com/downloads/2006-08/perl/plugh.pl.txt. Kent Beck’s new book, Implementation Patterns discusses many of the concepts of his earlier book, Smalltalk Best Practice Patterns, using Java. According to Beck, his new book will be available in the third quarter of 2006.

Comments are closed.