Copyright Notice

This text is copyright by InfoStrada Communications, Inc., and is used with their permission. Further distribution or use is not permitted.

This text has appeared in an edited form in Linux Magazine magazine. However, the version you are reading here is as the author originally submitted the article for publication, not after their editors applied their creativity.

Please read all the information in the table of contents before using this article.
Download this listing!

Linux Magazine Column 84 (Aug 2006)

[Suggested title: ``Pluggable Behavior with a twist'']

My object-oriented programming experience dates all the way back to shortly after the Smalltalk-80 language was developed. I worked for Tektronix at the time, and we were selected as one of the few companies that would get firsthand knowledge outside of Xerox Parc of this new-fangled ``mouse and windows'' interface that so clearly dominates the market today. Although I wasn't in the group that got to interface 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 Tek employee Kent Beck was also playing with Smalltalk, and along with co-worker Ward ``Wiki'' Cunningham would go on to being a driving force in the field of Smalltalk and object oriented design. I still argue that my Smalltalk experience (both at Tek, and at a startup company immediately following that) has made me a better objected-oriented programmer today. (If you want to play with Smalltalk, a wonderful cross-platform free Smalltalk is available from www.squeakland.org: check it out!)

Somewhere in the mid-90's, 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 for condensing a lot of what I had known (and re-learned) from hacking Smalltalk and Perl objects into short, digestible, and yet seemingly comprehensive snippets of what really works when you're doing objects correctly. And to this day, nearly every time I grumble at a 50-line Perl method that I want to subclass but can't, or a hardwired constant buried in the middle of some code that I just want to be able adjust, I'm reminded of the wisdom of this book.

Recently, I was asked to create a full day Object Oriented Programming course for a client. I already have OO materials in the Alpaca course, but they're really 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 the 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 those 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 within a given class is their state. In other words, for a given state, and a given new method call, they behave identically. After all, if the behavior had to be different, we'd simply subclass the class, and capture the distinct behaviors based on extended or overridden methods. For example, if we wanted a file downloader to sometimes ring a bell when finished, but other times simply open a browser window, we'd subclass the FileDownload class as FileDownload::BellRinger and FileDownload::BrowserOpener

However, there are times when the subclassing approach is impractical. For example, suppose the class is the abstract base class for a number of derived classes (download via proxy, download using throttled bandwidth, and so on). If we had to first create subclasses for each bit of the varying behavior, we'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 that will be executed at a particular step, or even passing in some object that will cooperate in completing 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. We 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, we get a different behavior when the download is complete. Of course, we wouldn't want just any method to be used there, but we 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.

Note that when we're pulling up $action, we're also providing a default behavior as well, falling back to ring_bell unless something else was specified.

``But wait, there's more'', 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 we 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.)

As a longer example of a full class taking advantage of this pattern, I present [listing one, below]. The Plugh object has two operations: open a file, and take the reciprocal of a number. If we create a default Plugh, we die if something goes wrong:

  my $basic_plugh = Plugh->new;

Now we try some operations:

  my $half = $basic_plug->reciprocal(2);
  my $secret_handle = $basic_plugh->open_file("/etc/passwd");

But if we 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 warns!

And if we want to be sophisticated, we can even pass in our 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 we could select 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!

LISTING

        =1=     package Plugh;
        =2=     
        =3=     sub new {
        =4=       my $class = shift;
        =5=       bless { @_ }, $class;
        =6=     }
        =7=     
        =8=     sub on_error_ignore {
        =9=       my $self = shift;
        =10=    }
        =11=    
        =12=    sub on_error_warn {
        =13=      my $self = shift;
        =14=      warn $@;
        =15=    }
        =16=    
        =17=    sub on_error_die {
        =18=      my $self = shift;
        =19=      die $@;
        =20=    }
        =21=    
        =22=    sub do_something_dangerous {
        =23=      my $self = shift;
        =24=      my $task = shift;             # coderef
        =25=      eval { $task->(@_) };
        =26=      if ($@) {
        =27=        my $error = $@;
        =28=        my $action = $self->{on_error} || 'on_error_die';
        =29=        $self->$action($@);
        =30=      }
        =31=    }
        =32=    
        =33=    sub reciprocal {
        =34=      my $self = shift;
        =35=    
        =36=      my $somevalue = shift;
        =37=    
        =38=      my $result;
        =39=      $self->do_something_dangerous
        =40=        (sub { $result = 1 / $somevalue });
        =41=      return $result;
        =42=    }
        =43=    
        =44=    sub open_file {
        =45=      my $self = shift;
        =46=    
        =47=      my $file = shift;
        =48=    
        =49=      my $filehandle;
        =50=      $self->do_something_dangerous
        =51=        (sub { open my $h, "<", $file or die "Cannot open $file: $!";
        =52=               $filehandle = $h;
        =53=             });
        =54=      return $filehandle;
        =55=    }
        =56=    
        =57=    1;

Randal L. Schwartz is a renowned expert on the Perl programming language (the lifeblood of the Internet), having contributed to a dozen top-selling books on the subject, and over 200 magazine articles. Schwartz runs a Perl training and consulting company (Stonehenge Consulting Services, Inc of Portland, Oregon), and is a highly sought-after speaker for his masterful stage combination of technical skill, comedic timing, and crowd rapport. And he's a pretty good Karaoke singer, winning contests regularly.

Schwartz can be reached for comment at merlyn@stonehenge.com or +1 503 777-0095, and welcomes questions on Perl and other related topics.