A
Anno Siegel
Inheritance in Perl OO has its difficulties when it comes to data
encapsulation. Basically, it can't be done without a peek at the
implementation of the superclass.
A frequent situation is that the inheriting class contains an object
of the superclass (or, more generally, has a method that returns a
superclass object), and you want it to respond to the methods of the
superclass. But let me use a concrete example, the generalization
will be obvious.
Consider a class "Tape", whose objects are conceptually snapshots of
some files or directories at a given time (not necessarily on magnetic
tape, but that's a nice short name). The implementation is a basic
hash-as-a-record:
bless {
name => $name,
...
time => Time:iece( ...),
}, Tape;
sub name { $_[ 0]->{ name} }
sub time { $_[ 0]->{ time} }
Now I want Tape to respond to the methods of Time:iece, that is, I
want to put "Time:iece" on @Tape::ISA. (Because of the overloading
Time:iece does, this means I can compare tapes with each other, or
with other dates (Time:iece objects) to see which is older. Handy
when deciding which tape to overwrite.)
One way of doing this is through Tape::AUTOLOAD. That can be considered
standard, I won't go into the possible variants here.
Instead of AUTOLOAD one can also use overload, which was new to me.
A peek at the implementation of Time:iece shows that the objects
are array-refs. So, if we can make Time:iece think that Tape objects
are the right kind of array ref, we're done. But that is what overloading
"@{}" does. So, in package Tape, saying
use base qw( Time:iece);
use overload ( '@{}' => 'time' );
is all that's needed to make Tape objects inherit from Time:iece.
That's pretty minimal.
The method isn't entirely clean, but some methods that are described
in Perl's OO recommendations are less so. It relies on the fact that
the superclass uses a particular (overloadable) data type for its
implementation, but on none of the further details. Obviously, it also
occupies that particular reference overloading, so if you need that
for something else, you're out of luck.
The implementation also assumes that the inherited data type is different
from the data type of the inheriting class. Otherwise, overloading
your own data type is still do-able, but a little involved. This
restriction can be worked around by yielding and making your data type
something else (wrap a scalar ref around it, for instance), but that
makes the method less attractive.
I'm surely not the first to make the observation that overloading can
help inheritance in this way, but since I didn't know about it, I
conclude that it isn't "well known" and deserves a mention
Anno
encapsulation. Basically, it can't be done without a peek at the
implementation of the superclass.
A frequent situation is that the inheriting class contains an object
of the superclass (or, more generally, has a method that returns a
superclass object), and you want it to respond to the methods of the
superclass. But let me use a concrete example, the generalization
will be obvious.
Consider a class "Tape", whose objects are conceptually snapshots of
some files or directories at a given time (not necessarily on magnetic
tape, but that's a nice short name). The implementation is a basic
hash-as-a-record:
bless {
name => $name,
...
time => Time:iece( ...),
}, Tape;
sub name { $_[ 0]->{ name} }
sub time { $_[ 0]->{ time} }
Now I want Tape to respond to the methods of Time:iece, that is, I
want to put "Time:iece" on @Tape::ISA. (Because of the overloading
Time:iece does, this means I can compare tapes with each other, or
with other dates (Time:iece objects) to see which is older. Handy
when deciding which tape to overwrite.)
One way of doing this is through Tape::AUTOLOAD. That can be considered
standard, I won't go into the possible variants here.
Instead of AUTOLOAD one can also use overload, which was new to me.
A peek at the implementation of Time:iece shows that the objects
are array-refs. So, if we can make Time:iece think that Tape objects
are the right kind of array ref, we're done. But that is what overloading
"@{}" does. So, in package Tape, saying
use base qw( Time:iece);
use overload ( '@{}' => 'time' );
is all that's needed to make Tape objects inherit from Time:iece.
That's pretty minimal.
The method isn't entirely clean, but some methods that are described
in Perl's OO recommendations are less so. It relies on the fact that
the superclass uses a particular (overloadable) data type for its
implementation, but on none of the further details. Obviously, it also
occupies that particular reference overloading, so if you need that
for something else, you're out of luck.
The implementation also assumes that the inherited data type is different
from the data type of the inheriting class. Otherwise, overloading
your own data type is still do-able, but a little involved. This
restriction can be worked around by yielding and making your data type
something else (wrap a scalar ref around it, for instance), but that
makes the method less attractive.
I'm surely not the first to make the observation that overloading can
help inheritance in this way, but since I didn't know about it, I
conclude that it isn't "well known" and deserves a mention
Anno