The problems that are attributed to MI are more properly put down to its facillitating sloppy factoring; if you use some decent design discipline you'll find that there's zero inherent evil in MI, as well as great economy and flexibility. --PeterMerel
Generally, the reasons advanced for MI have to do with those things that you cannot do in either Java or Smalltalk - primarily use (other than through delegation) implementations of multiple superclasses. While I agree that MI is not 'evil,' I don't see that you have even addressed the issue.
Russell's quite right here that there are other ways to do the refactoring bit, and interfaces in both java and objective-C are a good way to go, with the exception, again as he notes, of mixins. So I'm not saying the other ways of doing refactoring are bad, but since I like mixins I'll give them a little support as well:
I agree. When you've said, "to quote BertrandMeyer," you've said quite enough. :-) ---GlennVanderburg
OK, I'll say a bit more. Firstly, most of the arguments against multiple inheritance are also arguments against single inheritance. Ditto arguments for. Let's recap the pro arguments. What is inheritance for?
''Actually, I've said much the same thing only I phrased it as "If you're going to let people descend interface from any number of classes but implementation from only a set number, why is that set number 'one' and not some other value?" -- KatieLucas
Hmmm ... I think perhaps you should have let things be. In spite of my dig at Bertrand, the quote above does seem sensible to me. Your addition doesn't resonate, however, because I would never have supplied any of the three answers you give for the purpose of inheritance. They may or may not be nice benefits of inheritance, but they aren't what inheritance is for. ---GlennVanderburg
I think we're using different words for the same thing. At least, I read your long piece below without learning what inheritance is for other than the 3 things I list. (Incidently, I see polymorphism as all but orthogonal to inheritance.) -- DaveHarris
It seems to me that using much Multiple Inheritance might be taken as evidence of the presence in one's design of more complexity than one might actually need. It might not be evil, but it is one of the Signs of the Apocalypse. --RonJeffries
It certainly needs to be tempered by OnceAndOnlyOnce ... but how does it otherwise add complexity? --PeterMerel
The following is a personal statement --- not intended to be persuasive of others --- of why I'm glad Java does not support multiple inheritance.
Although I certainly agree that MI is not evil, I do think it is relatively unimportant and rarely useful, and I'm happy to be working in a language (Java) that does not support it. In part, I've come to believe that inheritance is given far too much importance by most OO languages, designs, developers, and pundits.
My thoughts about inheritance have been evolving since I learned Java. The revelations I've had may not seem like much to a Smalltalk programmer, but they represent a complete shift in my thinking.
Nearly every introduction to OO concepts I've ever read or seen has dealt with inheritance very early, and then moved on to a cursory discussion of "polymorphism" as a sort of nice side-effect of inheritance. Partly as a result of this (and partly because of the underlying misunderstanding) the word "inheritance" is usually used to refer to some combination of behavior inheritance and subtyping.
Java is the first language I have used that mostly separates those two concepts. Extending or implementing an interface represents a subtyping relationship, whereas extending a class represents the more traditional combination of subtyping and behavior inheritance. The process of using and designing with interfaces has brought subtyping out of the shadows and into the foreground of my thinking.
I've begun teaching the concepts of inheritance and polymorphism the other way 'round: polymorphism and interfaces come first, as the fundamental issue, and behavior inheritance comes later, as a nice facility (more, but not much more, than a convenience) when two subtypes share significant portions of their behavior or implementation. It seems to work well. Many of the common inappropriate uses of inheritance never occur to programmers who learn it this way. I've found that the method also helps in the explanation of abstract classes and when to use them.
Consider a language that makes the separation even more clear: subtyping uses one mechanism, and behavior inheritance can be specified without also producing a subtype relationship. (I'm speaking hypothetically, but I won't be surprised to learn that such a language actually exists.) Behavior inheritance becomes a kind of specialized composition or delegation facility. Some of the traditional objections and implementation complexities of MI disappear.
My conclusion, then, is that the strongly-typed OO community may have been going down a side path for most of its history, conflating two concepts that are actually separate. Perhaps, once we've corrected that misdirection, the time will come for MI to move back to center stage. For now, though, I'm pleased that Java avoids MI, if only because having to do without it has helped me to understand the fundamental issues more clearly. (From talking to colleagues, I believe it is having a similar effect on others, too.) ---GlennVanderburg
You'd might want to look at SatherLanguage, at http://www.icsi.berkeley.edu/~sather/.
Very well-reasoned and very well put, Glenn. And it has been a bad week for those dimensions.
Of course I'm contaminated now from having worked, for the past N years, in a language that is sublimely ignorant of "type". In Smalltalk, inheritance is about as close as you can get to being a programmer's hack to save code. Which is what it's for, after all.
To me, the whole subtype notion is unnecessary and (it seems to me) doesn't work anyway. When you view inheritance as a technique for implementing OnceAndOnlyOnce, it makes eminent sense to combine things in any way whatsoever, types be damned.
Now please go over and work on that stuff about classes being cyclic dependencies. ;-> --RonJeffries
Agreed that Glenn has put this very well. I'm content that, for almost all purposes, MI as behaviour inheritance isn't necessary. But not having it has consequences; I suspect we can trace the painful complexity of both JFC and MFC class libraries to not being able to do mixins. Among other things. --PeterMerel
Slightly off topic: Glenn has indeed put it very nicely (perhaps we should find a shorthand for that phrase) (trust me, that would not pay off over time. ---GV) but some compilers are not "sublimely ignorant" of types. In fact, they force you to think about inheritance as subtyping. Thus, generic delegation alla #doesNotUnderstand: does not work. With MI, you could do some mix-in stuff. But then, you don't have MI in Java. Which leads me to my question: Has anybody come up with something else like mixins to reduce code duplication in Java? An implementation pattern maybe? PeterMerel's comment right above seems to suggest no. -HaskoHeinecke
Java actually does support multiple inheritance. It just limits that support to interfaces and doesn't provide it for inheritance of implementation. It's a way of allowing polymorphism strictly by adhering to a shared interface "contract" without having to share any implementation of structure or behavior. It's still MI, it's just for subtyping instead of subclassing. The problem of name-clashes and redefinition still exists.
Note that there are really two facets of inheriting or sharing "implementation": one is inheriting structure or state (data) and the other is inheriting behavior (method implementation). There are some languages that provide inheritance of behavior but not of structure (Perl and Python spring to mind). This is kind of interesting because it forces superclass and subclass to communicate only via methods and you are forced to pay more attention to the interface between superclass and subclass (which more people need to do IMHO).
Ralph Johnson once said that C++ programmers suffer from the disease of thinking that inheritance causes polymorphism :-). Like Java, the Sather programming language (an interesting variant of Eiffel that preceded Java) explicitly separates subtyping from subclassing. In fact I believe it even does what Glenn mused about above: allowing subclasses to share more than just interface but without forcing a subtype relationship.
Inheritance isn't really a "pure" concept in OOPL theory. But Polymorphism is. The orthogonal concept is that of "Delegation." Inheritance of behavior is nothing more than a particular combination of Polymorphism and Delegation. Having separate support for both polymorphism and delegation and being able to combine them is actually much more powerful (meaning you can do more with it; including get yourself into more trouble ;-) --BradAppleton
I enjoy working in a typeful language, and find that it helps my thinking (and I have worked some in Smalltalk and some other typeless ones, so I do know a little bit, at least, about the other side of the fence). One of the things I'm excited about is the idea that, with proper separation of the concepts, inheritance can still be "a technique for implementing OnceAndOnlyOnce" without interfering with proper subtype relationships.
One thing to note is that if your design is expressed in terms of interfaces rather than classes, you do not need MI to do mixins in Java; delegation works just fine. The fact that delegation is manual and tedious can be ameliorated by tools (or possibly by the eventual addition of AutomaticDelegationForJava). Not that MI wouldn't be nice for doing mixins, but it's not necessary for the best Java designs I've seen.
Unfortunately, Java developers (me included) are still learning how to use interfaces well, how to express designs and relationships in terms of pure interfaces rather than these odd mixes of interface and behavior that we call classes. Not having MI is forcing us to learn. I think over the long run this will be useful. ---GlennVanderburg
Do you agree that it's led to unnecessary complexity in the libraries?
Yeah, but I don't think the complexity is necessarily the result of not having MI; I think the libraries would have been simpler if interfaces had been put to better use. I *think* that; I don't know that I can really back it up yet.
Um ... yeah, I knew that. :-) I really did, and just forgot. But I see that (my forgetting, that is) as part of the problem, which I'll get to in a moment.
In a way, C++ offers the complete separation in the other way, because you can introduce a subtype relationship without inheriting behavior, by using pure abstract classes.
So C++ technically matches what I speculated about above. But while the concepts of inheritance and polymorphism can be separarated in a C++ program, they are not conceptually separated in the language. And that conceptual separation is what I think we need. ---GlennVanderburg
C++ has much more polymorphism that you think (or remember) it has, thanks to overloading and templates. And polymorphism in STL is quite separated from the inheritance concept. --NikitaBelenki
Of course they do. I think that's called polymorphism. How do different methods of the same name lead us to want multiple inheritance? Is the idea to build an abstract class for the interface, so they can all inherit #implementedBySubclass?
This doesn't necessarily mean we need full MI, but it does mean we need more than Java provides.
I would think that anyone who has endured the pain and suffering of implementing and supporting either class in either Smalltalk or Java (don't forget about the differences between the various flavors of numbers and the "zoo" of iterator/collection classes) would be well aware of the compelling advantage offered by MultipleInheritance in these two cases.
Whenever I go near SI languages, I find myself spending a lot of time writing a lot of code over and over again in order to get polymorphic behaviour to inherit so I can re-use stuff, which seems like a waste of time to me...
And you end up with arbitrary decisions about where to slice the inheritance. Is a PoliceCar? mostly a Car (has four wheels, engine, drives on roads) or an EmergencyVehicle? (has a radio, blue lights and a siren). Now the correct answer in an SI environment is that NEITHER is more important, and so people should interface-inherit from both and re-write both behaviours. In the real world I find that actually people pick one. Deem it "more important" and implementation-inherit that. Meanwhile, a partition away, someone else is making a different importance decision and by the wall at the end of the office buried in nine tons of other paper is a "policy" on how to pick them written by a committee...
I think that multiple inheritance of implementation tends to cause a very confusing model of the system - increasing maintenance costs. On the other hand, re-use by delegation in C++ tends to involve repeated descriptions of the same elements - increasing maintenance costs by violating DontRepeatYourself.
I tend towards using delegation rather than inheritance because I tend to work on systems that will be maintained by low-skill developers who will not be dedicated to a single project and thus need to understand the model very swiftly.
This sounds a little extreme. Instead of removing as much inheritance as possible, I would focus on code clarity and add or remove inheritance to help understanding of the code.
AnswerMe Maybe I'm missing something, but isn't AspectOrientedProgramming just another, cleaner, way to do something equivalent to MultipleInheritance?
Can you explain why you think it might be?
This page mirrored in WikiPagesAboutRefactoring as of July 17, 2004