Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation

inheritance turns back and bites (for real this time)

by dada (Chaplain)
on Mar 02, 2004 at 15:53 UTC ( #333287=note: print w/replies, xml ) Need Help??

in reply to inheritance turns back and bites

time to clarify things up :-)

I would like to thank everyone that answered to this rather silly post, and took part in a sort of bad joke of mine.

I had prepared a very different kind of posting, talking about inheritance, interfaces and code maintenance, but in the end I decided to try to be thought-provoking and so posed the question as a sort of quiz. and I'm sorry if this caused more harm than good :-).

almost everybody spotted the error in redefining the interface (or the signature, if you prefer to think in terms of it) for a method in a derived class.

the rule I wanted to point out, which in Perl (due to its typeless and prototypeless nature, that we all sometimes love) may easily go overlooked (I, for one, didn't had it so clear in mind before stepping on it):

Don't change the interface of methods you overload from a base class (that is, you can do things differently internally, but both input and output should be consistent with the original method) or inheritance will turn back and bite you.

and that's my exposition of point c. regarding point b (a way to fix this), many (here and in other forums) suggested checking the number, and possibly the nature, of parameters in MyNumber::add, as per broquaint's:

@_ > 2 && return $_[0]->SUPER::add(@_[1..$#_]);
this has the disadvantage that one can still write: $answer->add(string => 'xxx'), and this may very well be what the author of MyNumber wanted to avoid.

one could check the value of the second parameter, and die unless it is number, but this seems rather kludgy.

a slightly less inelegant way to fix this problem could be for example:

sub add { # check if the Big Boss is calling us... if( (caller(1))[3] =~ /^SomeData::/) { # we feel guilty, so let's hide return shift->SUPER::add(@_); } # ...back to our scheduled job my($self, $value) = @_; die "not a number" unless $value =~ /^\d+$/; $$self += $value; }
but still broquaint's hint on Class::Multimethods wins this thread's elegance award.

as a final note, I will also unveil where really I encountered the problem (so that you can finally forget about my broken example :-). I was taking a look at Net::ICal for a project of mine, and I found that Net::ICal::Time, which is intended to be just a wrapper class for Date::ICal, defines a method:

but Date::ICal has an add method, which has this synopsis (as well as others):
add(duration => $duration)
and that's exactly our point a. the code in Net::ICal::Time::add ends with:
return $self->SUPER::add(duration=>$duration);
but as we saw, this is not enough to be safe.

to further underline how sneaky this bug can be, I will also add this: the code in Date::ICal::new, which Net::ICal::Time delegates to, calls offset, which in turns calls add (which is really Net::ICal::Time::add) only when it has to do some adjustment to account for timezones.

and that's all for today. I hope you found all this somewhat entertaining :-)


King of Laziness, Wizard of Impatience, Lord of Hubris

Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://333287]
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others romping around the Monastery: (5)
As of 2022-01-25 13:38 GMT
Find Nodes?
    Voting Booth?
    In 2022, my preferred method to securely store passwords is:

    Results (66 votes). Check out past polls.