Приглашаем посетить
Достоевский (dostoevskiy-lit.ru)

Section 11.6.  Overriding the Methods

Previous
Table of Contents
Next

11.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 . We use overriding to shadow the method in the derived class (Mouse) because we have a specialized version of the routine, instead of calling the more general base class's method (in Animal). In fact, we didn't even need to initialize @Mouse::ISA to say that a Mouse was an Animal, because all the methods needed for speak are defined completely with Mouse.

We'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. Our mice will still say goes, which means the code still has the bug. The problem is that we invoked cut and paste to duplicate code, and in OOP, that's a sin. We should reuse code through inheritance, not by cut and paste.

How can we avoid that? Can we say somehow that a Mouse does everything any other Animal does, but add in the extra comment? Sure!

As our first attempt, let's 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 we've stopped using the method arrow, we have to include the $class parameter (almost surely the value Mouse) as the first parameter to Animal::speak.

Why did we stop using the arrow? Well, if we 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 it 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 we no longer use the method arrow, we get one and only one chance to hit the right method because we're treating it like a regular subroutine with no inheritance magic. We'll look for it in Animal and not find it, and the program aborts.

The Animal class name is now hardwired into the method selection. This is a mess if someone maintains the code, changing @ISA for Mouse, and doesn't notice Animal there in speak. Thus, this is probably not the right way to go.


Previous
Table of Contents
Next