I once had a heated argument with Rob Pike over lunch about Java when I was a naive grad student; suffice it to say that I thoroughly got my butt whooped on inheritance. My argument was, I think, that inheritance basically is composition, just with some self recursion thrown in. Keep in mind that programming is always about composition, and we are just arguing about different styles of such.
OOP has recently been thrashed in the mud in the academic community, where it was never completely accepted. Now, industry always loved OOP, not because it was new, but it provided some stronger guidance on what they were already doing (composing software out of stateful parts), and was much more pragmatic than its older more aloof sibling (FP). People were already thinking in terms of objects probably even without using Simula, Beta, Smalltalk, C++, etc...vtables were even already being crafted like crazy in C.
I agree that object thinking is just another tool in your bag, sometimes you really need lambdas and should use those. Sometimes you want raw tables. Any program worth its salt is going to incorporate many different styles, and avoiding one style on ideological grounds is ridiculous.
I think inheritance basically _is_ composition plus self-recursion.
You get into trouble because of the self-recursion. Any time a base class method calls another base class method, that call is part of the class's interface, because when you extend the class, the call will be redirected to the subclass' method.
But how many base classes have documentation for every self-call that can be redirected in such a manner?
> You get into trouble because of the self-recursion.
Which is why (savvy) object-oriented programmers have largely moved on to interface polymorphism. Inheritance polymorphism has its uses, which is why there hasn't been a whole lot of effort to rally behind languages that lack it, but the general consensus is that you should really only use inheritance when inheritance is what you really want to use.
Unfortunately it's true that there's a whole lot of OO code out there that was written before people learned this lesson. And OO's popularity with pragmatists combined with its relative lack of popularity with academics means that a lot of code doesn't reflect lessons related to the pitfalls of inheritance polymorphism that academics figured out a very long time ago, such as the Liskov Substitution Principle.
My understanding is that composition can be changed dynamically whereas inheritance cannot. It has always seemed to me like composition is more flexible.
To give concrete examples: using composition, if instance A has a B, then at runtime you can replace the pointer to the B with a pointer to a C such that now A has a C. You basically change the type of A by changing where messages get sent / delegated.
With inheritance, you'd have B is an A and C is an A, and the relationships here are static unless you start messing around with reflection and dynamic class loading and stuff.
The tradeoff for the flexibility of composition is more verbose code, I think.
Dynamic inheritance is not unthinkable, I've used it in my languages before (or see research languages like Cecil). Of course, you can do this easily in dynamic languages like ruby.
Actually, can you really do dynamic inheritance in ruby? I don't _think_ so. There are ways to apply inheritance dynamically at runtime of course (including with module mix-ins, which are basically just inheritance even though ruby pretends it isn't), but I don't think you can _undo_ inheritance at runtime.
You can easily simulate dynamic inheritance in ruby.... with composition, using delegate-like patterns.
but I don't think you can _undo_ inheritance at runtime
I'd be surprised if you couldn't do it in Ruby. You certainly can do it in Perl because it uses a package (class) variable called @ISA for it's inheritance lookup.
And because package variables are dynamically scoped you can do this:
{
# remove everything except father from inheritance
local @Some::Class:ISA = $Some::Class::ISA[-1];
$some_object->foo; # finds father foo() only
}
$some_object->foo; # runs first foo() found in inheritance
I think OOP really took off in industry because it was easy to sell third-party modules. You could "plug in" this module that you purchased and it was easy to hook up. Markets have a way of doing that: the solution that wins isn't necessarily the "best" solution but the one that's easiest to sell.
I don't think the component revolution has happened. We got frameworks to be sure, we even got...gasp...libraries with our languages. Maybe for that reason, OO languages (Java/Python/Smalltalk) were more likely to come without their batteries included. I'm guessing inheritance helped out a bit with that.
But I don't think objects are really especially about third-party reuse or even any reuse at all, but they are more about enabling easy problem decomposition (i.e. break up your problem into a bunch of interacting objects).
OOP definitely has something about it that favors reuse of code.
I think you got it backwards, it's decomposition that it has a problem with - it's not easy to point fingers for exactly why that is (it's probably because of all the mutable state, which leads to entanglement, where components only seem independent of each other, when in fact they aren't), but you can find anecdotal evidence of this happening in the wild ... look at frameworks like Django and Rails, with tons of reusable plugins available and yet a humongous effort went into Rails for making it modular (e.g. such that you can import parts of it, like ActiveRecord, in other non-Rails projects, or for easily replacing ActiveRecord with something else), while Django never achieved it.
>Maybe for that reason, OO languages (Java/Python/Smalltalk) were more likely to come without their batteries included.
Surely you mean "WITH their batteries included"?
For this is the very situation in Python (and it's slogan in fact), and of course Java has the most extensive "included batteries" in the form of the JDK API than any other language.
OOP has recently been thrashed in the mud in the academic community, where it was never completely accepted. Now, industry always loved OOP, not because it was new, but it provided some stronger guidance on what they were already doing (composing software out of stateful parts), and was much more pragmatic than its older more aloof sibling (FP). People were already thinking in terms of objects probably even without using Simula, Beta, Smalltalk, C++, etc...vtables were even already being crafted like crazy in C.
I agree that object thinking is just another tool in your bag, sometimes you really need lambdas and should use those. Sometimes you want raw tables. Any program worth its salt is going to incorporate many different styles, and avoiding one style on ideological grounds is ridiculous.