8.6 Overriding the Methods
Let's
add a mouse that can barely be heard:
{ package Animal;
sub speak {
my $class = shift;
print "a $class goes ", $class->sound, "!\n";
}
}
{ package Mouse;
@ISA = qw(Animal);
sub sound { "squeak" }
sub speak {
my $class = shift;
print "a $class goes ", $class->sound, "!\n";
print "[but you can barely hear it!]\n";
}
}
Mouse->speak;
which results in:
a Mouse goes squeak!
[but you can barely hear it!]
Here, Mouse has its own speaking routine, so
Mouse->speak doesn't
immediately invoke Animal->speak. This is known
as overriding. You use overriding to shadow
the method in the derived class (Mouse) because
you have a specialized version of the routine, instead of calling the
more general base class's method (in
Animal). In fact, you didn't even
need to initialize @Mouse::ISA to say that a
Mouse was an Animal at all
because all the methods needed for speak are
defined completely with Mouse.
You've
now duplicated some of the code from
Animal->speak; this can be a maintenance
headache. For example, suppose someone decides that the word
goes in the output of the
Animal class is a bug. Now the maintainer of that
class changes goes to says.
Your mice will still say goes, which means the
code still has the bug. The problem is that you invoked cut and paste
to duplicate code, and in OOP, that is a sin. You should reuse code
through inheritance, not by cut and paste.
How can you avoid that? Can you say somehow that a
Mouse does everything any other
Animal does, but add in the extra comment? Sure!
As your first attempt, you can invoke the
Animal::speak method directly:
{ package Animal;
sub speak {
my $class = shift;
print "a $class goes ", $class->sound, "!\n";
}
}
{ package Mouse;
@ISA = qw(Animal);
sub sound { "squeak" }
sub speak {
my $class = shift;
Animal::speak($class);
print "[but you can barely hear it!]\n";
}
}
Note that because you've stopped using the method
arrow, you have to include the $class parameter
(almost surely the value of "Mouse") as the first
parameter to Animal::speak,.
Why did you stop using the arrow? Well, if you invoke
Animal->speak there, the first parameter to the
method is "Animal," not
"Mouse", and when the time comes for it to call
for the sound, it won't have the
right class to select the proper methods for this object.
Invoking Animal::speak directly is a mess,
however. What if Animal::speak
didn't exist before and was inherited from a class
mentioned in @Animal::ISA? For example, suppose
the code was:
{ package LivingCreature;
sub speak { ... }
...
}
{ package Animal;
@ISA = qw(LivingCreature);
# no definition for speak( )
...
}
{ package Mouse;
@ISA = qw(Animal);
sub speak {
...
Animal::speak( ... );
}
...
}
Because you no longer use the method arrow, you get one and only one
chance to hit the right subroutine. You'll look for
it in Animal, and not find it, and the program
aborts.
The Animal classname is now hardwired into the
subroutine selection. This is a mess if someone maintains the code,
changing @ISA for Mouse, and
didn't notice Animal there in
speak. Thus, this is probably not the right way to
go.
|