42 comments

[ 3.4 ms ] story [ 102 ms ] thread
I was hoping to see a good article about inheritance vs. object composition.

Although Ruby's "modules" are indeed preferable to inheritance they are usually as inappropriate as inheritance.

To my experience, object composition solves most of the design issues in a more elegant way. And I wonder why this is still so less understood by most programmers. It isn't even particularly new. Even the good old GoF patterns recommend object composition over inheritance, especially in their Smalltalk examples.

(In their book, they demonstrate each pattern in a static (C++) and a dynamic (Smalltalk) language, so Ruby programmers should have a deeper look at the Smalltalk part of the book, rather than imitating the C++ variants of the patterns.)

I think the reason people prefer inheritance is because it gives them the illusion that their code is simpler. They don't see the huge interfaces implied by inheritance because they are implicit.

Composition, on the other hand, is explicit. When you use it, you notice the complexities. That gives you an incentive to simplify your design, which makes you write actually simpler code.

The other reason as I see is some quite weird perception that OOP == inheritance.
Quite right. We can correct that by showing the classification of main programming paradigms[1]. It shows that, OO just needs records, procedures, named state and closures. (I personally prefer to say the more understandable "OO needs structs, assignment, and closures".)

Now I have a problem, however: what is the difference between OO and stateful functional programming?

[1]: http://www.info.ucl.ac.be/~pvr/paradigms.html

Polymorphism is at the core of most OO language features. Sum types aren't a viable substitute in most large apps because they are closed to extension at runtime. Doing it right functionally means rolling your own protocol system.
OOP is different things to different people. While I agree with you that OOP != inheritance, I believe that polymorphism ∈ OOP and inheritance is a part of polymorphism.
I don't follow you here. Are you saying that inheritance is mandatory to OOP? (I don't agree nor disagree. I'd just like to know what you mean.)
Polymorphism is often implemented as at least inheritance of interface in statically typed languages. Without polymorphism you don't get dynamic dispatch, especially open to extension at runtime.
I am not saying that inheritance is mandatory to OOP. I think "mandatory" is too strong of a word here. As I said, OOP means different things to different people and I am okay with that.

Speaking for myself, OOP is a way to orchestrate programming on a large scale. To grow software without growing the frequency, severity, and difficulty of bugs and without growing costs associated with new features is the desired result. Component-ized architecture takes great steps to achieve this result. For this reason, I view OOP as being some combination of: message passing, encapsulation, and polymorphism. I do not believe this is a complete view of OOP, nor that there is no viable alternative view of OOP.

Ad hoc polymorphism ∈ OOP

Parametric polymorphism ∈ FP

I disagree. Polymorphism and inheritance are separate concepts.

Yes, some statically typed languages like C++ and Java enforce certain inheritance constraints via classes or explicit interfaces before you can use polymorphism.

But almost all dynamically typed languages allow for ad-hoc polymorphism. For instance, in Python this is called "duck typing". Just because two objects provide a "foo()" method doesn't mean they are in any inheritance relation to each other. But polymorphism should still work.

The problem is that lots of different software engineering styles are implemented using the language feature of inheritance. For example in C++ implementing interface X looks the same syntactically as inheriting from some class X to modify it.

Sometimes people refer to the specific problematic inheritance style as "implementation inheritance". I found it very useful just to know that such a distinction could be made.

This is rather well studied subject - but this article does not add anything to the discussion. It talks a lot about names not much about their meaning. The only explanation he gives for the advantages or modules over classes is <blockquote> In Ruby you don't declare your types, so you cannot get ambiguous instance variables: if you have two instance variables with the same name in different modules, then they're the same instance variable. </blockquote> as if this kind of unification of variables would obviously solve anything.

Despite linking to the Wikipedia, he also does provide the right definition of the diamond problem: <blockquote> This is the dreaded "diamond" problem that C++ programmers learn to fear: if your class has two superclasses that both define an instance variable named x, then you can have a hard time specifying which x you're using. </blocquote> Where is the diamond here?

Regarding the diamond problem, I think he means that in the event of multiple inheritance, your class heirarchy would look like this:

  Object
   / \
  A   B
   \ /
    C
Where A and B are parents of the class C. In most OO languages there will be an implied parent of A and B, like Object.
"In Ruby you don't declare your types, so you cannot get ambiguous instance variables: if you have two instance variables with the same name in different modules, then they're the same instance variable."

That line also bugged me. I struggle to see how that's a good thing. That seems like you could have code breaking in seemingly unrelated code due to seemingly common naming issues. That can't be right, can it?

(From the original poster): You're right that I mischaracterized the diamond problem. I edited the post to fix that. My C++ mind model has been slipping away fast in part years. Definitely not like riding a bicycle.
I strongly disagree that "code reuse" is a reason to use inheritance in C++/Java. Composition nicely solves that problem. In fact, I would argue that someone who believes code reuse is an appropriate reason to use inheritance does not understand OO programming as well as they believe.
This. In fact, a friend of mine swears by writing C++ in a style called "snippets" --- meaning, whenever it makes sense to do so, he writes new functionality as a single header and cpp file that has no dependencies whatsoever on other headers, etc, then open-sources it so that anyone can add the same functionality to their own C++ projects by just dropping in the code. A math lib, for example.
(comment deleted)
Aren't those also just called "functions"? In this case one function per file?
No. As soon as your function has a dependency on anything else whatsoever, it can no longer be copy-and-pasted into another project

A snippet consists of functions which accomplish a specific task. The important part is that the entire snippet is a single cpp and a single header. No dependencies.

That's a function with no dependencies. Still a function.
.... Something is obviously being lost in translation here.

There are certain programming problems which can be solved once and used forever. The problem wouldn't necessarily be considered a single function. However, it isn't a massive problem either.

My friend solves these problems by creating a single .cpp and a single .h file. The .cpp is the implementation; the .h file defines the interface. He calls this a "snippet".

Now, the power of the snippet is that you can simply copy the .cpp and .h into any C++ project anywhere and it's good to go. There is no tweaking, include'ing, anything. It just works.

This code can then serve you for the rest of your life, regardless of what project you are currently working on. You can say "I've solved that problem before", and be able to spend literally 30 seconds implementing it into your current project, because all you have to do is drag'n'drop two files.

I get the distinction, and its a fair one. Although, I don't get how snippets could be useful. Especially in C++. It seems like for almost anything interesting I'm going to want a dependency on the standard library or Boost or the CRT.

I could imagine some snippets that don't take any dependencies, but they are pretty few and far between. Do you have pointers to some of the more interesting snippets he's posted?

Yeah, check 'em out: http://www.amillionpixels.us/sourcecode.htm

There are surprisingly many useful snippets. For example, a single header and source file for an entire, complete A* pathfinding implementation! That's a massive timesaver.

In a lot of the ones titled snippets, I do see dependencies. Some to Windows DLLs and some to the CRT. But they're relatively self-contained.

Cool stuff. Thanks for the link. Although I'd still call them functions. :-)

And they're all functions (or function groups), apart from the one or two that are command-line utilities.

At the end of the day, they're libraries. Small libraries? Sure, but still libraries.

Composition solves that problem, but I don't know about "nicely". You end up having to litter each class with a bunch of boilerplate methods which simply call the same method on an instance field. This avoids inheritance problems but you can end up with a lot of extra code.

What I would like to see — at least in Java — is a keyword or annotation for instance fields to mark them as pseudo-superclasses. Then the compiler would generate all of those call-through methods automatically, unless overridden in the current class.

It's called the strategy pattern. Look it up.

If you find yourself writing a lot of boilerplate code, you're doing something wrong, whether you're using inheritance or not.

Yes, I've read that book too. For many of the cases I deal with the strategy pattern is not appropriate, or creates additional complications. Try again.
Should we raise this discussion to the level of concrete examples? Pastebin perhaps?
Here's a pretty good comparison of the two techniques: http://www.artima.com/designtechniques/compoinhP.html

Having worked with a lot of C++ code recently that employs both methods, I personally prefer inheritance for the majority of cases. One of my primary goals while writing code is to write as little code as possible. If I have to write wrapper methods to expose the interface of the back-end class I've composed, then I'm not achieving that goal.

So I should never use the AbstractList, AbstractSet, AbstractMap, or AbstractCollection Java classes for implementing their relative data structures? Instead of reusing the code these classes provide through inheritance, I should instead be using composition?

Code reuse is the only good reason to use concrete inheritance and there are many situations where concrete inheritance is much cleaner than composition. Honestly, this whole conversation feels a little silly to me.

Implementing an abstract class when the concrete implementation conforms to the Liskov Substitution Principle is exactly when inheritance does make sense. You would do this so that calling code wouldn't need to care that the function required a MySpecialMapClass rather then something that conforms to the AbstractMap interface.

However, if you just think that some of the functions in AbstractMap are useful but plan on creating a totally different class that doesn't conform to the interface, then inheritance is inappropriate.

Code reuse is not a reason to use inheritance; it is a byproduct of it.

I think you need to distinguish between interface inheritance and implementation inheritance.

The reason to use interface inheritance is to allow polymorphism in a statically typed language.

The only reason to use implementation inheritance is code reuse. And yes, you should only do that if the classes conform to the LSP.

(From the OP): jknupp, I agree about that. I think that code reuse is one of the two reasons why people use inheritance in Java, but I don't think it's a very valid reason in general. Upcasting, on the other hand, definitely is. My point in the original post is that because Ruby doesn't have upcasting, then inheritance in Ruby becomes pretty marginal.
Neat. This style of code reuse is a lot like perl6 roles (http://search.cpan.org/~rkinyon/Perl6-Roles-0.01/lib/Perl6/R...) or haskell typeclasses: instead of describing how objects fit into a taxonomic hierarchy, objects or typeclasses are described by what behaviors they fulfill. For instance, in haskell a Complex type has an instance of Num in order to define numeric operations like (+) and (-). The emphasis is on what something does rather than on what it is.
>instead of describing how objects fit into a taxonomic hierarchy, objects or typeclasses are described by what behaviors they fulfill.

Can't this be said about Java interfaces too?

Yes and no. Java interfaces are a very degenerate case of roles. They provide no default behavior (a few constants notwithstanding), they are special cases in at least part of the type system (declaring a class does not also declare a role), and I know of no good way to handle conflicts when implementing multiple interfaces with overlapping requirements.
Old joke in my office:

Q. What is the object oriented way of getting rich?

A. Inheritance

(its a Friday :))