You know the old saying "where there's a will, there's a relative"?
Oh, wrong inheritance. Let me start again.
Let's say you know a little about black holes. You know that they're so massive that even light can't escape their gravity and once they have you, that's it. You also know that there's a massive black hole at the center of our galaxy and that very tiny black holes can explode. (Pedant alert: I know this is an oversimplification. That's the point)
Now imagine that you run into Paris Hilton at a party and she tells you that she's worried about the black hole in the center of our galaxy exploding. Naturally, you laugh at her. It's a big black hole. It's the small ones that explode! Then you run into Stephen Hawking and he tells you the same thing. Now you might be worried. Why? Because Hawking's a recognized expert on black holes and Hilton isn't. But maybe she had previously run into Hawking and he expressed the same concern to her? You don't know. All you know is that you're more likely to trust an expert on this topic than a debutante.
So why is it, when experts in OO programming tell us things like "avoid inheritance", "use delegation" or "especially avoid multiple inheritance", we so often ignore them? Is there something perverse about programmers which make us think that we can ignore thousands of years of collective experience and research in OO programming and substitute our own? It's not that there's nothing new to contribute! I wouldn't dream of saying that. It's that if we contribute something new, it should be very carefully thought over and investigated before inflicting it on others. The rules of thumb of OO are good rules indeed. In fact, for Perl, they're often easier to follow and more dangerous to ignore.
On smaller projects, with more accessible code bases, these issues are less problematic. As a project grows, however, the code is harder to manage and the temptation to "break the rules" (assuming you knew them in the first place) also grows.
Multiple Inheritance
So let's say you want to be a diligent OO programmer, but hey, inheritance is what OO is all about, right? So you can use inheritance with impunity. You're writing a game, so you need a Physical class which holds things like "weight", "description", "location", and so on. Your first class hierarchy looks like this:
+---------------+ +---------------+Well, that seems fine. Physical is an abstract base class and you have two items which clearly are "Physical". That looks fine.
Now you've promised that you'll deliver the game by a particular date and you realize you haven't created a particular trap you were looking for. It's a box which, when picked up, shoots the player. Hmm, it's a box. It's a gun. I know! I'll just inherit from both. Sure, people say it's bad, but I'm in a hurry and it works!
+---------------+ +----------------+Of course, you need to make sure it looks like a box and not a gun and being a good programmer, you don't want to duplicate code, so you inherit the description method from the box. But now we have to make sure that our inheritance order is correct. One way to do this in Perl:
package Physical::Trap; use base qw(Physical::Box Physical::Gun)
Great! When the player looks at the trap, they'll see a box instead of a gun.
Now you just need to make the gun shoot the player when they try to open the box:
$self->shoot($player);
Oh, that doesn't work. You didn't load the gun.
$self->insert($bullet); $self->shoot($player);
Now you're cackling with glee. When the poor sod opens the trap, they shoot themselves!
Delegation
Except you didn't insert the bullet into the gun. The box also has an "insert" method and you just inserted the bullet into the box because the box comes first in the inheritance tree. Either you hard-code the class name in there and start futzing around with all of the other places you may have this issue or you get smart and delegate.
package Physical::Trap; use base qw(Physical::Box); use Physical::Gun; use Physical::Bullet; sub _initialize { my $self = shift; $self->gun(Physical::Gun->new); } sub open { my ( $self, $player ) = @_; $self->gun->insert(Physical::Bullet->new); $self->shoot($player); }
This is correct because your trap is-a box and it contains a gun. When you have a contains relationship (also known as a has-a relationship), delegate. In fact, if you're not sure if is-a holds, delegate. Not only does delegation avoid the inheritance issues outlined here, since it only provides the methods you explicitly delegate to, you never have to worry about accidentally overriding a private method in the delegate.
To explain this in more detail, you can read jk2addict's grief at inheriting from Class::DBI or DBIx::Class.
Or you can read about my class with 255 methods (how perfect!) spread across 27 classes. I didn't design it that way, but that's what it is. What if you want to subclass that? You should only have to worry about the public interface, but in Perl, we traditionally write private methods with a leading underscore:
sub _dont_touch_me { ... }
Many of those 255 methods were "private", but now I have to know about these internal methods if I want to avoid accidentally overriding them! Even single inheritance has the problem. The more levels of inheritance you have, the more likely you'll accidentally override something you shouldn't. What's worse, it's quite possible that you won't know you've overridden it until that 3:00 AM emergency phone call from your boss. And trust me on this, debugging accidental overriding can be very difficult.
Another look at multiple inheritance
Imagine the following hierarchy:
+-----+ +-----+With this, Baz calls a convert method which is inherited. What happens if both Foo and Bar have this method? Presumably convert will change the internal state of the object. Whose method should be called? Both? Does the order matter? Are they both required? If they're both required, do they conflict with one another? What if sometimes you need one and sometimes you need the other? What if it's an 'equals' method and one or the other should match, but not both. How do you know which to call? Multiple inheritance is so problematic that many languages, such as Ruby, C# and Java, don't even allow it.
One things which sometimes help is intelligent method dispatching. If Foo::munge requires a Cat object and Bar::Munge requires a Dog object, calling with a Cat object is not ambiguous. Alas, Perl doesn't support this, so this somewhat dubious help is generally not available to us. Even if it did, if you pass along an Animal object, you're back to square one.
Conclusion
When you have a small project and you're the only person working on it, breaking the rules is not only tempting, it's often easy. After all, you have the entire model in your head (presumably) and you know what you're doing. Return to that project after a while or watch it grow and that model is harder to maintain.
Software is largely about complexity management. We don't want to duplicate code. We don't want conflicting functionality. We want our code to be as small as possible. Don't use inheritance when delegation will do and avoid multiple inheritance like the plague. You'll gain the benefits listed and you'll save yourself a lot of headache in the long run.
So why is it, when experts in OO programming tell us things like “avoid inheritance”, “use delegation” or “especially avoid multiple inheritance”, we so often ignore them?
The way OO is taught and conceptualised way overemphasises inheritance. Also, almost all languages have special syntax for inheritance but none whatsoever for delegation – so one has a tool to use it and one is a ritual.
Oh yeah, I forgot. These days I don’t do
sub _dont_touch_me {
... }
Instead I do
my $dont_touch_me = sub {
... };
If you believe more in “stay out because it’s polite not because I have a shotgun” you can substitute our
for my
there.