Adopting good Perl OOP habits

Yes, Perl is object-oriented! Most Perl developers take advantage of these features without realizing it. This article provides a list of good habits leading to effective object-oriented Perl development.

If you’ve been developing in Perl for a while, you’ve no doubt used a lot of the better modules out there. And you’ve probably used many of those packages' object-oriented interfaces, possibly on a daily basis. But if you are like a great number of everyday Perl programmers, you haven't ever tried to use object-oriented techniques when developing your own code. This article provides some suggested habits that can help you become an effective object-oriented Perl developer.

Instant OOP primer
The basic idea of object-oriented programming (OOP) is so simple that you probably already do it in a limited way. An object is nothing more than a collection of data that is all related to a single thing. Think of a hash loaded with data about a ball. You might have hash keys called color, diameter, and material. OOP methods are just subroutines that trust each ball’s hash to have the fields defined when they operate on them.

What is a hash?
Small hashes are basically free in terms of performance and memory, so don’t be afraid to use them liberally to keep your data together. If you aren’t aware of hash arrays (also called associative arrays), you're missing one of the great powers of Perl. In a normal array, you name individual items by their position; in a hash array, you can give items names (called keys). Think of a hash like a little database. Given a name, it can look up a record. Running “perldoc perldata” on the command line will get you the documentation on Perl’s data types and structures.

OOP goes beyond simply organizing your data and subroutines into related sets by ensuring at compile or runtime that the object and method actually are related. That way, you don’t attempt to call the sub ball_volume on a hash of hairstyles. OOP languages offer further benefits by simplifying syntax and providing a host of other features for declaring complex object-to-object relationships.

In simplest terms, every object tells Perl what kind of object it is so that only subroutines that know how to play with it safely are called. That lets you use simple names for your methods without worry. If you call volume on a book object, it will tell you how far into the series it is, not how much space it takes up. And if you call volume on a hairstyle, it will return poofy or limp.

Habit #1: Use hashes
Collect related data into little hashes. This one sounds simple, and it is. You have probably made a set of variables like $red, $green, and $blue before. Usually, just a second later, you realize that you need two or three sets and thus wind up with $a_red, $b_red, $c_red, and likewise for green and blue. Change those nine scalars to three hashes called %a_color, %b_color, and %c_color with r, g, and b parameters.

The guts of just about every module on CPAN are wrapped around a hash. Just getting used to grouping related data in a hash will have you well on your way to OOP. You'll have fewer variables to declare, and you'll keep your data organized. You also get the added bonus of improved readability, since a variable like $boy{height} is practically self documenting. You’ll be happy you started doing this even if you never try OOP coding for real. Listing A provides an example.

Habit #2: Use subroutines
The next good habit is to use subroutines to organize your code around those data collections. Try grouping chunks of code that work on a hash collection into subroutines. Make sure you pass in the collection rather than individual values. Calling invert_new(\%color); beats invert_old(\$reda, \$greena, \$bluea); hands down. At the very least, you won’t need to remember the argument order.

The big win with subroutines is organizing your code logic; you can keep your program flow at the top of the file and push the grunt work out of sight. If you pass all your variables into the subs, you’ll never get bitten when you go to reuse the code and find that the new program uses different global variables—or worse, when the same global variable is used for something completely different. If nothing else convinces you, think how much easier those small subroutines are to debug, when all the code you need to understand fits on the screen at once. (See Listing B.)

Habit #3: Reuse
The third good OOP habit is to collect those subs, pull them into a separate file, and reuse them. Refine them; make them work on general cases. Use them in more than one script and discover the pleasure of having to debug them only once. If you pull out all the subroutines that work with one particular hash chunk into one file, you are way ahead of the game.

You don’t have to get fancy. Just pull the subroutines into a file and “require” it. Don’t try and get ahead of yourself and start writing subroutines you don’t already use; you’ll only waste your time. Move them out and then check to make sure that they all work. As a bonus, you’ll likely find bugs you missed. Listing C provides a good example.

Habit #4: Protect the hash
The final good habit is the big step: defend your hash. Rather than create the hash directly, add a library subroutine that returns a hash reference after checking the data used to create it. Then, add a few tiny subs that return individual values out of the hash and let you change them safely. The code for these can be as simple as Listing D.

With these little subs in place, you ensure that if you do need to change the rules, you'll at least have errors thrown when you break something. Stop writing code that changes the hash in your programs; move that code to the library.

The payoff
You can sum up these suggestions in one sentence: Hash related data, write subs that use the whole hash, collect subs related to one hash into a simple library, and protect your fancy library hash. Each of these tips adds almost nothing to your workload and pays off in extra organization right away. Together, they add up to 90 percent or more of what you’d do to create an object-oriented module.

If you ensure that the hash reference is sent first to every subroutine and that you touch the hash’s interior only with library routines, you basically just need to pick a name for your module. Well, that and let Perl know that it should be treating your hash as an object. Your new library of code most likely won’t need even 20 lines to become a module!

Editor's Picks