dcsimg

Learn to Program with Objects in Perl

Last month's column was a brief tutorial introducing the concept of objects in Perl. We covered some of the basic concepts of object-oriented programming, including class methods and inheritance. We learned how to factor out and reuse common code with variations. This month we'll learn how to create instance data, which is information associated with one particular object.

Last month’s column was a brief tutorial introducing the concept of objects in Perl. We covered some of the basic concepts of object-oriented programming, including class methods and inheritance. We learned how to factor out and reuse common code with variations. This month we’ll learn how to create instance data, which is information associated with one particular object.

As you may recall, I introduced the method-arrow syntax Class->method(@args); or the equivalent:


$a = “Class”;
$a->method(@args);

which constructs an argument list of (“Class”, @args)and attempts to invoke:


Class::method(“Class”, @args);

However, if Class::method is not found, then @Class::ISA is examined (recursively) to locate a package that does indeed contain method, and that subroutine is invoked instead.

Using this simple syntax, we have class methods, (multiple) inheritance, overriding, and extending. Using just what we’ve seen so far, we’ve been able to factor out common code and provide a nice way to reuse implementations with variations. This is at the core of what objects provide, but objects also provide instance data, which we will discuss now.

A Horse Is a Horse, of Course of Course — Or Is It?

Let’s start with the code from last month for the Animal class and the Horse class:


{ package Animal;
sub speak {
my $class = shift;
print “a $class goes “, $class->
sound, “!\n”
}
}
{ package Horse;
@ISA = qw(Animal);
sub sound { “neigh” }
}

This lets us invoke Horse->speak to ripple upward to Animal::speak, calling back to Horse::sound to get the specific sound, and the output of:


a Horse goes neigh!

But all of our Horse objects would have to be absolutely identical. If I add a subroutine, all horses automatically share it. That’s great for making horses the same, but how do we capture the distinctions about an individual horse? For example, suppose I want to give my first horse a name. There’s got to be a way to keep its name separate from the other horses.

We can do that by drawing a new distinction, called an “instance.” An “instance” is generally created by a class. In Perl, any reference can be an instance, so let’s start with the simplest reference that can hold a horse’s name: a scalar reference.


my $name = “Mr. Ed”;
my $talking = \$name;

So now $talking is a reference to what will be the instance-specific data (the name). The final step in turning this into a real instance is with a special operator called bless:


bless $talking, Horse;

This operator stores information about the package named Horse into the thing pointed at by the reference. At this point, we say $talking is an instance of Horse. That is, it’s a specific horse. The reference is otherwise unchanged and can still be used with traditional dereferencing operators.

Invoking an Instance Method

The method arrow can be used on instances as well as names of packages (classes). So, let’s get the sound that $talking makes:


my $noise = $talking->sound;

To invoke sound, Perl first notes that $talking is a blessed reference (and thus an instance). It then constructs an argument list, in this case from just ($talking). (Later we’ll see that arguments will take their place following the instance variable, just as with classes.)

Now for the fun part: Perl takes the class in which the
instance was blessed, in this case Horse, and uses that to locate the subroutine to invoke the method. In this case, Horse::sound is found directly (without using inheritance), yielding the final subroutine invocation:


Horse::sound($talking)

Note that the first parameter here is still the instance, not the name of the class. We’ll get neigh as the return value, and that’ll end up as the $noise variable above.

If Horse::sound had not been found, we’d be wandering up the @Horse::ISA list to find the method in one of the superclasses, just as for a class method. The only difference between a class method and an instance method is that the first parameter will be a class name (a string) or an instance (a blessed reference) respectively.

Accessing the Instance Data

Because we get the instance as the first parameter, we can now access the instance-specific data. In this case, let’s add a way to get at the name:


{ package Horse;
@ISA = qw(Animal);
sub sound { “neigh” }
sub name {
my $self = shift;
$$self;
}
}

Now we call for the name:


print $talking->name, ” says “,
$talking->sound, “\n”;

Inside Horse::name, the @_ array contains just $talking, which the shift stores into $self. (It’s customary to shift the first parameter off into a variable named $self for instance methods.) Then, $self gets dereferenced as a scalar ref, yielding Mr.Ed, and we’re done with that. The result is:


Mr. Ed says neigh.

How to Build a Horse

Of course, if we constructed all of our horses by hand, we’d most likely make mistakes from time to time. We’re also violating one of the properties of object-oriented programming, in that the “inside guts” of a Horse are visible. That’s good if you’re a veterinarian, but not if you just like to own horses. So, let’s let the Horse class build a new horse:


{ package Horse;
@ISA = qw(Animal);
sub sound { “neigh” }
sub name {
my $self = shift;
$$self;
}
sub named {
my $class = shift;
my $name = shift;
bless \$name, $class;
}
}

Now with the new named method, we can build a horse:


my $talking = Horse->named(“Mr. Ed”);

Notice we’re back to a class method, so the two arguments to Horse::named are Horse and Mr. Ed. The bless operator not only blesses $name, it also returns the reference to $name, so that’s fine as a return value. And that’s how to build a horse.

Inheriting the Constructor

But was there anything specific to Horse in that method? No. Therefore, it’s also the same recipe for building anything else that inherited from Animal, so let’s put it there:


{ package Animal;
sub speak {
my $class = shift;
print “a $class goes “, $class->
sound, “!\n”
}
sub name {
my $self = shift;
$$self;
}
sub named {
my $class = shift;
my $name = shift;
bless \$name, $class;
}
}
{ package Horse;
@ISA = qw(Animal);
sub sound { “neigh” }
}

Ahh, but what happens if we invoke speak on an instance?


my $talking = Horse->named(“Mr. Ed”);
$talking->speak;

We get a debugging value:


a Horse=SCALAR(0xaca42ac) goes neigh!

Why do we get a debugging value? That’s because the Animal::speak routine is expecting a classname as its first parameter, not an instance. When the instance is passed in, we’ll end up using a blessed scalar reference as a string, and that shows up as we saw it just now.

Making a Method Work with
Either Classes or Instances

All that we really need is for a method to be able to recognize if it is being called on a class or called on an instance. The most straightforward way is with the ref operator. This returns a string (the classname) when used on a blessed reference, and undef when used on a string (like a classname). Let’s modify the name method first to notice the change:


sub name {
my $either = shift;
ref $either
? $$either # it’s an instance, return name
: “an unnamed $either”; # it’s a class, return generic
}

Here, the ?: operator comes in handy to select either the dereference or a derived string. Now we can use this with either an instance or a class. Note that I’ve changed the first parameter holder to $either to show that this is intended:


my $talking = Horse->named(“Mr. Ed”);
print Horse->name, “\n”; # prints “an unnamed Horse\n”
print $talking->name, “\n”; # prints “Mr Ed.\n”

and now we’ll fix speak to use this:


sub speak {
my $either = shift;
print $either->name, ” goes “, $either->
sound, “\n”;
}

And since sound already worked with either a class or an instance, we’re done!


Adding Parameters to a Method

Let’s train our animals to eat:


{ package Animal;
sub named {
my $class = shift;
my $name = shift;
bless \$name, $class;
}
sub name {
my $either = shift;
ref $either
? $$either # it’s an instance, return name
: “an unnamed $either”; # it’s a class,return generic
}
sub speak {
my $either = shift;
print $either->name, ” goes “,
$either->sound, “\n”;
}
sub eat {
my $either = shift;
my $food = shift;
print $either->name, ” eats $food.\n”;
}
}
{ package Horse;
@ISA = qw(Animal);
sub sound { “neigh” }
}
{ package Sheep;
@ISA = qw(Animal);
sub sound { “baaaah” }
}

And now try it out:


my $talking = Horse->named(“Mr. Ed”);
$talking->eat(“hay”);
Sheep->eat(“grass”);

which prints:


Mr. Ed eats hay.
an unnamed Sheep eats grass.

Note that an instance method with parameters gets invoked with the instance first, and then the list of parameters. This means that that first invocation is synonymous with the following:


Animal::eat($talking, “hay”);

More Interesting Instances

What if an instance needs more data? Most interesting instances are made of many items, each of which can in turn be a reference or even another object. The easiest way to store these is often in a hash.

The keys of the hash serve as the names of parts of the object (often called “instance variables” or “member variables”), and the corresponding values are commonly known as, well, the values.

But how do we turn the horse into a hash? Recall that an object was any blessed reference. We can just as easily make it a blessed hash reference as a blessed scalar reference, as long as everything that looks at the reference is changed accordingly.

Let’s make a sheep that has a name and a color:


my $bad = bless { Name => “Evil”, Color =>
“black” }, Sheep;

so $bad->{Name} has Evil, and $bad->{Color} has black. But we want to make $bad->name access the name, and that’s now messed up because it’s expecting a scalar reference. Not to worry, because that’s pretty easy to fix up:


## in Animal
sub name {
my $either = shift;
ref $either ?
$either->{Name} :
“an unnamed $either”;
}

And of course named still builds a scalar sheep, so let’s fix that as well:


## in Animal
sub named {
my $class = shift;
my $name = shift;
my $self = { Name => $name, Color =>
$class->default_color };
bless $self, $class;
}

What’s this default_color? Well, if named has only the name, we still need to set a color, so we’ll have a class-specific initial color. For a sheep, we might choose to define it as white:


## in Sheep
sub default_color { “white” }

And then to keep from having to define one for each additional class, we’ll define a “backstop” method that serves as the “default default,” directly in Animal:


## in Animal
sub default_color { “brown” }

Now, because name and namedwere the only methods that referenced the “structure” of the object, the rest of the methods can remain the same, so speak still works as before.

A Horse of a Different Color

But having all our horses be brown would be boring. So let’s add a method or two to get and set the color.


## in Animal
sub color {
$_[0]->{Color}
}
sub set_color {
$_[0]->{Color} = $_[1];
}

Note the alternate way of accessing the arguments: $_[0] is used in-place, rather than with a shift. (This saves us a bit of time for something that may be invoked frequently.) And now we can fix that color for Mr. Ed:


my $talking = Horse->named(“Mr. Ed”);
$talking->set_color(“black-and-white”);
print $talking->name, ” is colored “,
$talking->color, “\n”;

which results in:


Mr. Ed is colored black-and-white

So, now we have class methods, constructors, instance methods, instance data, and even accessors. But that’s still just the beginning of what Perl has to offer. We haven’t even begun to talk about accessors that double as getters and setters, destructors, indirect object notation, subclasses that add instance data, per-class data, overloading, isa and can tests, UNIVERSAL class, and so on. Perhaps in a future column, eh?

For more information, see the Perl documentation for perlobj (the reference information about objects), perltoot (the tutorial for those who already know objects), perlbot (the Bag of Object Tricks teaches good style with instance data and methods), and books such as Damian Conway’s excellent Object Oriented Perl. And as always, enjoy!





Randal L. Schwartz is the chief Perl guru at Stonehenge Consulting and co-author of Learning Perl and Programming Perl. He can be reached at merlyn@stonehenge.com.

Comments are closed.