Listen Print
Perl Design Patterns

Perl Design Patterns

by Phil Crow
June 13, 2003

Introduction

In 1995, Design Patterns was published, and during the intervening years, it has had a great influence on how many developers write software. In this series of articles, I present my take on how the Design Patterns book (the so-called Gang of Four book, which I will call GoF) and its philosophy applies to Perl. While Perl is an OO language -- you could code the examples from GoF directly in Perl -- many of the problems the GoF is trying to solve are better solved in Perl-specific ways, using techniques not open to Java developers or those C++ developers who insist on using only objects. Even if developers in other languages are willing to consider procedural approaches, they can't, for instance, use Perl's immensely powerful built-in pattern support.

Though these articles are self-contained, you will get more out of them if you are familiar with the GoF book (or better yet have it open on your desk while you read). If you don't have the book, then try searching the Web - many people talk about these patterns. Since the Web and the book have diagrams of the object versions of the patterns, I will not reproduce those here, but can direct you to this fine site.

I will show you how to implement the highest value patterns in Perl, most often by using Perl's rich core language. I even include some objects.

For the object-oriented implementations, I need you to understand the basics of Perl objects. You can learn that from printed sources like the Perl Cookbook by Tom Christiansen and Nat Torkington or Objected Oriented Perl by Damian Conway. But the simplest way to learn the basics is from perldoc perltoot.

As motivation for my approach, let me start with a little object-oriented philosophy. Here are my two principles of objects:

  1. Objects are good when data and methods are tightly bound.
  2. In most other cases, objects are overkill.

Let me elaborate briefly on these principles.

Objects are good when data and methods are tightly bound.

When you are working for a company that rents cars (as I do), an object to represent a rental agreement makes sense. The data on the agreement is tightly bound to the methods you need to perform. To calculate the amount owed, you take the various rates and add them together, etc. This is a good use of an object (or actually several aggregated objects).

In most other cases, objects are overkill.

Consider a few examples from other languages. Java has the java.lang.Math class. It provides things such as sine and cosine. It only provides class methods and a couple of class constants. This should not be forced into an object-oriented framework, since there are no Math objects. Rather the functions should be put in the core, left out completely, or made into non-object-oriented functions. The last option is not even available in Java.

Or think of the C++ standard template library. The whole templating framework is needed to make C++ backward compatible with C and to handle strong static-type checking. This makes for awkward object-oriented constructs for things that should be simple parts of the core language. To be specific, why shouldn't the language just have a better array type at the outset? Then a few well-named built-in operations take care of stacks, queues, dequeues and many other structures we learned in school.

So, in particular, I take exception to one consistent GoF trick: turning an idea into a full-blown class of objects. I prefer the Perl way of incorporating the most-important concepts into the core of the language. Since I prefer this Perl way, I won't be showing how to objectify things that could more easily be a simple hash with no methods or a simple function with no class. I will invert the GoF trick: implement full-blown pattern classes with simpler Perl concepts.

The patterns in this first article rely primarily on built-in features of Perl. Later articles will address other groups of patterns. Now that I've told you what I'm about to do, let's start.

Iterator

There are many structures that you need to walk one element at a time. These include simple things such as arrays, moderate things such as the keys of a hash, and complex things such as the nodes of a tree.

The Gang of Four suggest solving this problem with the above mentioned trick: turn a concept into an object. Here that means you should make an iterator object. Each class of objects that can reasonably be walked should have a method that returns an iterator object. The object itself always behaves in a uniform way. For example, consider the following code, which uses an iterator to walk the keys of a hash in Java.


    for (Iterator iter = hash.keySet().iterator(); iter.hasNext();) {
        Object key   = iter.next();
        Object value = hash.get(key);
        System.out.println(key + "\t" + value);
    }

The HashMap object has something that can be walked: its keys. You can ask it for this keySet. That Set will give you an Iterator on request to its iterator method. The Iterator responds to hasNext with a true value if there are more things to be walked, and false otherwise. Its next method delivers the next object in whatever sequence the Iterator is managing. With that key, the HashMap delivers the next value in response to get(key). This is neat and tidy in the completely OO framework of a language with limited operators and built-in types. It also perfectly exhibits the GoF iterator pattern.

In Perl any built-in or user defined object which can be walked has a method which returns an ordered list of the items to be walked. To walk the list, simply place it inside the parentheses of a foreach loop. So the Perl version of the above hash key walker is:


    foreach my $key (keys %hash) {
        print "$key\t$hash{$key}\n";
    }

I could implement the pattern exactly as it is diagrammed in GoF, but Perl provides a better way. In Perl 6, it will even be possible to return a list that expands lazily, so the above will be more efficient than it is now. In Perl 5, the keys list is built completely when I call keys. In the future, the keys list will be built on demand, saving memory in most cases, and time in cases where the loop ends early.

The inclusion of iteration as a core concept represents Perl design at its finest. Instead of providing a clumsy mechanism in non-core code, as Java and C++ (through its standard template library) do, Perl incorporates this pattern into the core of the language. As I alluded to in the introduction, there is a Perl principle here:

If a pattern is really valuable, then it should be part of the core language.

The above example is from the core of the language. To see that foreach fully implements the iterator pattern, even for user-defined modules, consider an example from CPAN: XML::DOM. The DOM for XML was specified by Java programmers. One of the methods you can call on a DOM Document is getElementsByTagName. In the DOM specification this returns a NodeList, which is a Java Collection. Thus, the NodeList works like the Set in the Java code above. You must ask it for an Iterator, then walk the Iterator.

When Perl people implemented the DOM, they decided that getElementsByTagName would return a proper Perl list. To walk the list one says something like:


    foreach my $element ($doc->getElementsByTagName("tag")) {
        # ... process the element ...
    }

This stands in stark contrast to the overly verbose Java version:


    NodeList elements = doc.getElementsByTagName("tag");
    for (Iterator iter = elements.iterator(); iter.hasNext();) {
        Element element = (Element)iter.next();
        // ... process the element ...
    }

One beauty of Perl is its ability to combine procedural, object-oriented, and core concepts in such powerful ways. The facts that GoF suggests implementing a pattern with objects and that object only languages like Java require it do not mean that Perl programmers should ignore the non-object features of Perl.

Perl succeeds largely by excellent use of the principle of promotion. Essential patterns are integrated into the core of the language. Useful things are implemented in modules. Useless things are usually missing.

So the iterator pattern from GoF is a core part of Perl we hardly think about. The next pattern might actually require us to do some work.

Decorator

In normal operation, a decorator wraps an object, responding to the same API as the wrapped object. For example, suppose I add a compressing decorator to a file writing object. The caller passes a file writer to the decorator's constructor, and calls write on the decorator. The decorator's write method first compresses the data, then calls the write method of the file writer it wraps. Any other type of writer could be wrapped with the same decorator, so long as all writers respond to the same API. Other decorators can also be used in a chain. The text could be converted from ASCII to unicode by one decorator and compressed by another. The order of the decorators is important.

In Perl, I can do this with objects, but I can also use a couple of language features to obtain most of the decorations I need, sometimes relying solely on built-in syntax.

I/O is the most common use of decoration. Perl provides I/O decoration directly. Consider the above example: compressing while writing. Here are two ways to do this.

Use the Shell and Its Tools

When I open a file for writing in Perl, I can decorate via shell tools. Here is the above example in code:


    open FILE, "| gzip > output.gz"
        or die "Couldn't open gzip and/or output.gz: $!\n";

Now everything I write is passed through gzip on its way to output.gz. This works great so long as (1) you are willing to use the shell, which sometimes raises security issues; and (2) the shell has a tool to do what you need done. There is also an efficiency concern here. The operating system will spawn a new process for the gzip step. Process creation is about the slowest thing the OS can do without performing I/O.

Tying

If you need more control over what happens to your data, then you can decorate it yourself with Perl's tie mechanism. It will be even faster, easier to use, and more powerful in Perl 6, but it works in Perl 5. It does work within Perl's OO framework; see perltie for more information.

Suppose I want to preface each line of output on a handle with a time stamp. Here's a tied class to do it.


    package AddStamp;
    use strict; use warnings;

    sub TIEHANDLE {
        my $class  = shift;
        my $handle = shift;
        return bless \$handle, $class;
    }

    sub PRINT {
        my $handle = shift;
        my $stamp  = localtime();

        print $handle "$stamp ", @_;
    }

    sub CLOSE {
        my $self = shift;
        close $self;
    }

    1;

This class is minimal, in real life you need more code to make the decorator more robust and complete. For example, the above code does not check to make sure the handle is writable nor does it provide PRINTF, so calls to printf will fail. Feel free to fill in the details. (Again, see perldoc perltie for more information.)

Here's what these pieces do. The constructor for a tied file handle class is called TIEHANDLE. Its name is fixed and uppercase, because Perl calls this for you. This is a class method, so the first argument is the class name. The other argument is an open output handle. The constructor merely blesses a reference to this handle and returns that reference.

The PRINT method receives the object constructed in TIEHANDLE plus all the arguments supplied to print. It calculates the time stamp and sends that together with the original arguments to the handle using the real print function. This is typical decoration at work. The decorating object responds to print just like a regular handle would. It does a little work, then calls the same method on the wrapped object.

The CLOSE method closes the handle. I could have inherited from Tie::StdHandle to gain this method and many more like it.

Once I put AddTimeStamp.pm in my lib path, I can use it like this:


    #!/usr/bin/perl
    use strict; use warnings;

    use AddStamp;

    open LOG, ">output.tmp" or die "Couldn't write output.tmp: $!\n";
    tie *STAMPED_LOG, "AddStamp", *LOG;

    while (<>) {
        print STAMPED_LOG;
    }

    close STAMPED_LOG;

After opening the file for writing as usual, I use the built-in tie function to bind the LOG handle to the AddStamp class under the name STAMPED_LOG. After that, I refer exclusively to STAMPED_LOG.

If there are other tied decorators, then I can pass the tied handle to them. The only downside is that Perl 5 ties are slower than normal operations. Yet, in my experience, disks and networks are my bottlenecks so in memory inefficiency like this tends not to matter. Even if I make the script code execute 90 percent faster, I don't save a noticeable amount of time, because it wasn't using much time in the first place.

This technique works for many of the built-in types: scalars, arrays, hashes, as well as file handles. perltie explains how to tie each of those.

Ties are great since they don't require the caller to understand the magic you are employing behind their back. That is also true of GoF decorators with one clear exception: In Perl, you can change the behavior of built-in types.

Pages: 1, 2

Next Pagearrow





Contact Us | Advertise with Us | Privacy Policy | Press Center | Jobs | Submissions Guidelines

Copyright © 2000-2008 O’Reilly Media, Inc. All Rights Reserved. | (707) 827-7000 / (800) 998-9938
All trademarks and registered trademarks appearing on the O'Reilly Network are the property of their respective owners.

For problems or assistance with this site, email