DekGenius.com
[ Team LiB ] Previous Section Next Section

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.

    [ Team LiB ] Previous Section Next Section