"It is practically impossible to teach good programming style to students that have had prior exposure to BASIC; as potential programmers they are mentally mutilated beyond hope of regeneration." -- Professor Edsger Dijkstra
Object Oriented Perl
Object oriented programming is hard to sum up in a single sentence. But if you know what it is, you know that it's a very powerful tool. You probably also know that perl is not exactly renowed for providing object oriented programming facilities.
Indeed, if you're thinking of doing some OO perl, your first thought should be: why is perl a good language for this? There are valid reasons: perhaps perl is the language you know best, or you might have access to a big perl codebase that already does most of what you want. Generally speaking, though, if you're able to choose, you should probably choose something else.
That out of the way, let's get stuck in to some object orientation! When I started doing OO perl I didn't find any guides that really worked for me, so I'm going to share the fruits of my labour in the hope that I'll help someone else in a similar situation.
Ground Rules
One of the main areas where object oriented programming helps is in structuring large projects. So, I'm going to assume that at least a few thousand lines of code is involved. A few thousand lines of perl can very quickly become unmanageable; care is certainly needed. It's possible to help structure things with OO technology, but you need to exercise restraint.
Essentially, if you're going to do OO perl, you more or less have to pretend that you're using a stricter language like C++ and Java. The general style guidelines that apply to those languages should apply to your perl code, too. In particular:
- Each class in its own file, each file contains a class
- All data members should be 'private'
- Encapsulate behaviour in classes that hold their own data
This last point is particularly important in perl. It's very easy to write perl code, but in OO programming that becomes as much a disadvantage as an advantage. Using the same regular expression over and over again is bad. Stringing together the same primitives in the same way over and over again is very bad. Perl makes it easy to get what you want by writing what you want, but to write a good OO program you need to get what you want by abstracting the key problem features as classes. Only then do you get the payoff: a better program structure and code that's easy to maintain.
Here's a concrete example. Reading a delimited text file in perl is easy; it's a matter of a few lines of code. In a short script, that's fine, but in a large project, it's disastrous. The same few lines will end up repeated over and over again. If there's a type of file you need to read, there needs to be a class for reading it. If there are several similar types, they need classes with a common interface so you can pick and choose between them.
Here are some guidelines:
Avoid using arrays and hashes to carry data. Hashes are particularly dangerous because they give the illusion of structured data, when really there is no enforced structure at all. Put the structured data in your program into classes. Use classes to keep code and associated data together as much as possible.
If it's appropriate to use an array or hash, always use them by reference. For example, where a C++ program would use a vector, in perl you can use an arrayref. Where a C++ program would use a map or set, in perl you can use a hashref.
Don't use variable length parameter lists. Say exactly what parameters you're expecting, and check at the start of every subroutine that you have the right number. At the same time you can check any preconditions. Note that it is a mistake to check that parameters are of a specific class. If you must check something, check that each object has the methods that will be needed.
Document each subroutine interface to say exactly what you're expecting. Write it like a C++ interface specification, including
publicorprivate, return type, and the type of each parameter. Perl may not care what is passed in, but unless your routine can handle absolutely anything, you need to document what's expected.Regular expressions should be hidden inside classes as much as possible. Complicated regular expressions should definitely be hidden. When you use a regular expression, you are trying to achieve a particular effect. That effect should be made the responsibility of a class, and everyone else can forget about the implementation details altogether.
It may sound like these guidelines rule out some of the best features of perl, and indeed that's correct if you're writing a 100-line script. Once the project gets into the thousands of lines, these features become problematic. They need hiding inside classes or avoiding altogether.
Example Class
With all that out of the way, it's time to touch on how you write an actual class. In perl an object is actually an instance of one of the primitive types that's been 'blessed' into a particular class. That's more or less all there is to it.
Most guides suggest using a hashref as the base type for your classes. Personally I think this is a bad idea, because it encourages people to access the private members of the class. Instead, I always use an arrayref. If I want someone to have access to a data member, I write an accessor function.
Here's a class representing a square:
# The package name will become the class name
package Square;
use strict;
use warnings; # Always a good idea
# public Square new(double size)
# Instantiate a square of a specified size.
sub new
{
my($package, $size) = @_;
die unless scalar @_ == 2;
my $this = [
$size # Length of side of the square
];
bless $this, $package or die;
return $this;
}
# private double _GetSize()
# Get the side length. This method is private, denoted by the underscore.
# This is not enforced, but at least it is clearly indicated.
sub _GetSize
{
my($this) = @_;
die unless scalar @_ == 1;
return $this->[0];
}
# public double Area()
# Get the area of the square.
sub Area
{
my($this) = @_;
die unless scalar @_ == 1;
return $this->_GetSize() * $this->_GetSize();
}
1; # Package needs to return true
This should be placed in a file called Square.pm and can be used as follows:
use Square; my $square = Square->new(7); print "The square area is ", $square->Area(), "\n";
Writing explicit accessor functions in this way takes a little work, but the benefits are huge. Once you start dictating how a class should be used, you can stop worrying about implementation and start concentrating on interfaces. And that's where object orientation really begins to get useful.
Conclusion
Perl's object orientated facilities are not great, but with a bit of discipline you can use them to your advantage. Careful structuring and class design can really pay off when it comes to keeping your code maintained.
So there you have it: object oriented perl. A bizarre concept, a bizarre implementation, but in the end it does actually work.
Copyright (C) 2002-2007 David Morgan -- last modified 19/11/2006 -- 280 hits -- best viewed with any browser -- valid xhtml -- valid css

