Re^2: Understanding 'Multiple Inheritance'
by Tanktalus (Canon) on Mar 07, 2005 at 16:09 UTC
|
The heart of the big benefits from OO is encapsulation, not inheritance.
The heart of the big benefits from OO is polymorphism. Polymorphism encourages encapsulation, but absolutely requires inheritance.
Polymorphism allows you to derive (inherit) from a class and change its behaviour for certain methods. And that is the basic advantage of OO.
I can do encapsulation in non-OO programming. For example, I can create a private package-level cache of data, create random keys, and require that key to access the data for that "object" (abstract data type), rather than passing around a hash ref. I've created encapsulation.
What I can't do without OO is polymorphism. I cannot create a new ADT that has all of the functionality of the old ADT, except overriding some function to extend it. That requires inheritance.
As to MI - I don't avoid it. I embrace it as much as possible. The only thing is, that I've only found 1 time in about 10 years of OO programming where MI was the right answer. And that was 9 years ago. It's enough to convince me that languages without MI (such as Java) are seriously crippled for certain paradigms, although Java's MI (extends one class, implements many others) is sufficient for a lot of other paradigms.
Perl is somewhere between C++ and Java on MI. Many of perl's classes don't really have any useful constructor. Object types which don't need any construction, but can mix in code fragments (which is impossible in Java, but not C++) are often called "plugins" in Perl. This type of MI is incredibly useful in those problem spaces. And you may not have even realised you were using MI - many plugins "use base" on themselves for you (or equivalent). It's kind of an abuse on Exporter... but it works. And you can still choose to override these functions to extend them - giving you the polymorphism you need.
| [reply] |
|
|
Polymorphism encourages encapsulation, but absolutely requires inheritance.
Completely false.
package RubberDuck;
sub new { bless {}, shift }
sub quack { print "Squeak!\n" }
sub DESTROY { print "RubberDuck goes back in the toybox.\n" }
package Mallard;
sub new { bless {}, shift }
sub quack { print "Quack!\n" }
sub DESTROY { print "Mallard flies away.\n" }
package main;
for my $duck_class (qw( RubberDuck Mallard ))
{
my $duck = $duck_class->new();
$duck->quack();
}
Duck typing, without inheritance.
Polymorphism has ever so much more to do with method dispatch and type equivalence (in languages that care about such things and in situations where it matters) than it does about inheritance, which is merely an implementation detail of how to reuse code and to mark type equivalence in some cases. | [reply] [d/l] |
|
|
Polymorphism encourages encapsulation, but absolutely requires inheritance.
Completely false.
? Not just "false" but "completely false"? So polymorphism neither encourages encapsulation nor requires inheritance?
This is not polymorphism, this is overloaded functions, as in multimethods of perl 6. Your methods are, more or less:
sub quack(RubberDuck $self) { ... }
sub quack(Mallard $self) { ... }
This is not polymorphism. Overloadable functions (same name, different contents) are usually found only in OO languages, but I don't think that this is causal. I think C may have overloadable functions in the not-too-distant future, if it doesn't already in the latest spec.
package RubberDuck;
sub new { bless {}, shift }
sub make_noise { print "Plug yer ears!\n"; $_[0]->quack(); }
sub quack { print "Squeak!\n" }
sub DESTROY { print "RubberDuck goes back in the toybox.\n" }
package Mallard;
our @ISA = qw(RubberDuck);
sub new { bless {}, shift }
sub quack { print "Quack!\n" }
sub DESTROY { print "Mallard flies away.\n" }
package main;
for my $duck_class (qw( RubberDuck Mallard ))
{
my $duck = $duck_class->new();
$duck->make_noise();
}
This is polymorphism. The parent class calls a function in the parent class, but the derived class gets to intercept it and do stuff with it. That stuff may include calling the parent class, or, as in this example, it may not. | [reply] [d/l] [select] |
|
|
|
|
|
|
The heart of the big benefits from OO is encapsulation, not inheritance.
The heart of the big benefits from OO is polymorphism.
As you two demonstrated, this is obviously up for debate. Any authoritative statement either way is bound to be disagreeable to one person or another. In this case, though, I think you're both right.
A large part of good programming practice is finding the right abstractions for certain tasks. Polymorphism and encapsulation are both ways to abstract one thing or another, and I think objects are pretty good at doing both of those.
Incidentally, I usually use object oriented techniques more for encapsulation, as Tilly suggests, than polymorphism, but I think they are two sides of the same coin. You can have one without the other, but they're not mutually exclusive.
| [reply] |
|
|
I disagree. More specifically while I agree that encapsulation is possible without OO, I believe that most of the advantages that most programmers saw when they moved to OO was that they began to use more encapsulation because it became simpler to do so.
I also disagree that you can't do polymorphism without OO. There are plenty of ways to write code such that you'll get polymorphism. One obvious approach is to emulate how OO works by hand (this turns out to be fairly simple).
About multiple inheritance, it sounds to me like you think that MI is a great idea, but it just keeps on not coming up as a good solution for you. However I'm going to guess that if you ran across a problem today that looked like what you used it for 9 years ago, you'd probably find that there was a perfectly good single inheritance solution available that you now have the programming maturity to find.
Also note that I differentiate multiple inheritance from attempts to create mixins. For one thing mixins have the distinct advantage that you avoid the complications around how to decide which method to dispatch to when unrelated parents implement different versions. Since most of my complaints about MI have to do with exactly that problem, avoiding it does not seem to me to be a small change in semantics.
That said, I generally don't like mixins in Perl, though I do in Ruby. The difference, as I've said many times, is how many uses I can amortize the effort of learning the interface over.
| [reply] |
Re^2: Understanding 'Multiple Inheritance'
by punkish (Priest) on Mar 07, 2005 at 14:25 UTC
|
The heart of the big benefits from OO is encapsulation, not inheritance.
Interesting observation, open to debate. I am new to OO, and intuitively, it is inheritance that I see as a bigger benefit than any other OO properties. In fact, intuitively, inheritance is what I understand better than encapsulation or polymorphism, etc.
Yes, multiple inheritance (MI) can cause a lot of problems, especially if using modules that were built by others, and hence, were not designed from ground up to be inherited from in conjunction with other modules. But life is complicated, and more often than not involves MI. After all, we all inherited from a dad and a mom, with hopefully no clashes and dire consequences.
That said, should not my approach, stated in my second post above, avoid the problems of same-named methods clashing and overriding each other? Except, it doesn't work all the way. Here is the real code I am trying out. I want to use a config file and connect to an email server. For this, I use Config::Simple and Mail::IMAPClient. Thus far I was using it normally and it works. Lately I decided to OO-ize it thusly --
package Mypackage;
use Config::Simple;
use Mail::IMAPClient;
use strict;
sub new {
my ($class) = @_;
bless {}, $class;
}
sub load_config {
my ($self, %a) = @_;
return new Config::Simple("$a{cfg}");
}
sub connect_to_imaphost {
my ($self, %a) = @_;
return Mail::IMAPClient->new(
Server => $a{IMAP_HOST},
User => $a{EM_UNAME},
Password => $a{EM_PWD},
Uid => 0,
Debug => $a{DEBUGGING},
);
}
sub other_methods... {}
### and then, in my script...
use Mypackage;
my $a = Mypackage->new();
# $a has all the methods of Mypackage
$f->load_config(cfg => "my.conf")->import_names();
# this works and imports all the config vars
my $imap = $a->connect_to_imaphost(
server => $IMAP_HOST,
user => $EM_UNAME,
password => $EM_PWD,
debug => $DEBUGGING,
);
my $msg_count = $imap->message_count("$INBOX");
# the vars above have been imported via Config::Simple
# the above, however, fails with the following
##Not connected at ./myscript.pl line 48
##Error sending '1 STATUS inbox (MESSAGES)' to IMAP: at ./myscript.pl
+ line 48
In the above code, instances of B and C are made only when called for. In a way, I have achieved encapsulation, and made A inherit the properties of both B and C, but only when desired. Thereby, hopefully, avoiding any problems.
Question is -- is the above code ok? If yes, why is it not working. If no, what am I doing wrong?
Many thanks.
--
when small people start casting long shadows, it is time to go to bed
| [reply] [d/l] |
|
|
Your problem is that connect_to_imaphost is returning the new imap object but maintains no connection between that and the object that you created. They know nothing about each other.
About inheritance, the problem with inheritance is that it is "action at a distance". The behaviour of your code depends on code somewhere over there which you're not looking at right now. Tracking down cause and effect therefore becomes more tricky, and you've opened up the possibility of negative interactions between the functions that you write and the ones that the parent class depends on. That is, you've introduced tight coupling between two pieces of code that are maintained separately.
The strength of inheritance is, of course, that you can avoid writing the same code multiple times in multiple places.
Whether the advantages of inheritance outweigh the disadvantages is situation dependent, and sometimes a matter of opinion. But it is a matter of fact that many people have managed to dig themselves into deep holes using inheritance when it wasn't really appropriate.
By contrast encapsulation and information hiding has much more clear-cut benefits and more minor risks.
I'd suggest reading Code Complete 2 for more detail on this theme. (The first edition didn't cover OO, but did explain enough about encapsulation and information hiding to make it clear what the advantages are.)
| [reply] |
|
|
| [reply] |
|
|
|
|
| [reply] |