Mis Using Multiple Inheritance

[from ClassicOoAntiPatterns]

I want to say something about dependence on MultipleInheritance. I'll clarify first that my bias is that any dependence on multiple inheritance is too much, but for the sake of argument I'll accept that multiple inheritance is desirable in some circumstances. Nevertheless, it is an antipattern, when designing programs in languages that permit multiple inheritance, to use it excessively, to the exclusion of other good OO practices. I'll call it naive multiple inheritance

[contrived example]

Shape abstract class, abstract (pure virtual, for C++ folks) method volume(), with subclasses Sphere, Cube, Pyramid. Obviously for each one the volume() method does something slightly different.

Texture abstract class, abstract method bounce(), with subclasses Rubbery, Sticky, Fuzzy, again, clearly different implementations of bounce().

So now I want each kind of shape with each kind of texture:

 RubberySphere
 StickySphere
 FuzzySphere

RubberyCube StickyCube FuzzyCube

RubberyPyramid StickyPyramid FuzzyPyramid
I'm sure you can see where this is going....

-- StevenNewton

Name clash, and discussion thereof, removed as it was apparently a RedHerring.

OK I guess it wasn't obvious where I was going ;). Suppose you want to add another texture, or another shape, or both? 3 textures x 3 shapes = 9 classes. 4 textures x 4 shapes = 16 classes. (The remainder of the multiplication is left as an exercise). To put some concreteness in this, suppose you change Shape to BrowserVersion (IE (3, 4, 5, Mac vs. Windows), Netscape (3, 4 & 6), Mozilla, Opera, Lynx) and Texture to DesignTheme (the sky's the limit). Naive MultipleInheritance is not just the renaming issue, which is bad, but the consequences of poorly designed hierarchies which genuine novices either don't foresee or don't know how to avoid.

-- StevenNewton

Yes, here you have to create a great many classes, which is bad because... why? See also FearOfAddingClasses, GenerativeProgramming.

To answer my own question... Inheritance is a static, compile-time relationship. Sometimes a dynamic relationship, such as delegation, is better. Notice, however, that this objection applies to single inheritance as much as multiple inheritance. [Inheritance is not constrained to be a static, compile-time relationship. If your OO system works that way, then it is a language/implementation issue, but that doesn't make it a fundamental OO issue.]

Also, with multiple inheritance, the new classes are not adding any new concepts to the system, only giving names to combinations of existing concepts. As the number of combinations of concepts grows, you have to create more names, which become increasingly hard to invent or remember.

Just because FearOfAddingClasses is bad, doesn't mean you should fall victim to BlindlyAddingClasses?, which is another trap that newbies to ObjectOrientedDesign are very prone to fall into.

So what is the alternative? If you need 16 (4 x 4) derived classes, isn't easier to maintain 8 (4 + 4) lower level classes than 16 discrete flat classes? If you don't need all 16, nothing forces you to create unnecessary classes.


But MultipleInheritanceIsNotEvil! ;->

Nobody said it was. Just that it could be misused.

Aside from the ExponentialGrowthDueToUntamedMixins? that occurs in a flat inheritance hierarchy, consider also what happens if we want to start inheriting from one of the existing concrete classes. The Shape example makes it too hard to think about it in concrete terms, but if we had a FooPyramid, which might be Rubbery, then would that be a RubberyFooPyramid?, or a FooRubberyPyramid? One way, our RubberyFooPyramid is not a RubberyPyramid; the other way, there is no common base between all the different FooPyramids, other than Pyramid itself. What you end up with is an InheritanceGraphThatTearsItselfApart?.

-- BruceDodson?


This example is almost precisely like the before example given in Fowler's excellent Refactoring book for the TeaseApartInheritance? refactoring. The problem is that the language the Refactoring example was written in uses single inheritance, not multiple. In other words, at least one problem you're having here is caused by something which has *nothing* to do with multiple inheritance - it's fundamental to inheritance.

In fact, the problem is precisely that inheritance is being used when it shouldn't. It appears to me to be impossible to argue that any shape is-a texture; some shapes may have-a texture. So because the initial design was incorrect, you get bad results.

Please provide an example which isn't polluted by so many other problems.

I'm going to jump to a conclusion, which I happen to have reached a long time ago. There's nothing wrong with multiple inheritance which isn't also wrong with single inheritance. Inheritance, all inheritance, fundamentally misses the point, and MI just happens to make the lack obvious. I appreciate how much work Smalltalk does for me, and I appreciate how C++ tries to take that a step further; but the way people use classes is fundamentally different. You just don't really get burned by the difference until you have to work in a strictly statically typed language. There are times when I want to borrow the interface of an existing class (inherit); there are other times when I want to borrow the implementation (or in more standard terms, delegate). All existing OO languages make it easy to inherit, but then they force you to delegate at the same time. Smalltalk doesn't enforce interfaces, so many programmers use its inheritance support to implement exposed delegation (I suspect that this is one reason it's easy to program in Smalltalk). C++ does enforce interfaces (often at all the wrong points - argh!), but the only way to delegate is to inherit, which causes many programmers to use evils such as private inheritance. Compare Sather, which separates the two issues: you can inherit without delegating, and delegate without inheriting. With that arrangement, there can be no question about multiple or non-multiple inheritance: it's obvious.

-- Billy (never used this wiki before, please clean me up if I mess anything up)


I like the point about the new subclasses merely giving names to combinations of existing concepts. Perhaps we sometimes need anonymous subclasses, or subclass expressions. In C++ we can replace RubberySphere with SubClass<Rubbery,Sphere>, where SubClass is defined like:

  template <typename Left, typename Right>
  class SubClass : public Left, public Right {
  };
which is close (it falls short for constructor arguments). -- DaveHarris

What about this for the ctor args?

  template <typename Left, typename Right>
  class SubClass : public Left, public Right {
SubClass ( const Left& l, const Right& r ) : Left(l), Right(r) {}
  };
-- BillWeston


My 2c: the ObjectOriented approach is backwards, which is why there is such pressure to use MultipleInheritance. See AlexanderStepanov's thoughts on the subject. To broadly summarize his argument: types are attributive rather than constructive. The "type" of an object is often an accidental property. For example, one part of the system may be interested in whether or not an object is Comparable, whereas another might want to know whether it is Serializable. While Comparable may be an essential property, Serializable is clearly accidental. The substance of the object might be that it represents a string. So what is the correct "type" of this object?

This is why InterfacesAreGood, and GenericProgrammingIsBetter. In C++, a templated method which uses the < operator on a pair of objects is implicitly posing a question: do these objects have a strict-weak ordering? This is akin to a Java class implementing a StrictWeakOrdering interface, for example.

-- DavidKTurner

But isn't that more an artifact of trying to shoehorn static typing onto OOP than a problem with OOP itself? In SmallTalk, all that matters is what messages an object responds to, not its heritage or the limits of a static type system.

When I first encountered SmallTalk (coming from C++), this seemed so unworkable to me. Since any object could be sent any message, you'd ideally have a global standard for all messages. (You want to avoid two objects having a message with the same name but with a radically different response to that message.) But it gives exactly what you describe.

Another reason why I tend to think that AllProblemsWithObjectOrientedProgrammingStemFromTheDifferencesBetweenTheLanguageBeingUsedAndSmallTalk?. (^_~)

-- RobertFisher


Valid Uses Of Multiple Inheritance

I believe the context presented at the top of the page, i.e., that multiple inheritance can be misused and misused multiple inheritance is bad; seems to be a circular argument ("misuse" implies "bad" and vice-versa) and of little interest. A more interesting discussion would be to look at cases where multiple inheritance provides value.

I find little need to use multiple inheritance in practice, but I am not convinced that it is never valuable. The result is that this discussion is primarily an intellectual exercise for me, but I would be interested in hearing from those who actually employ it.

I would suggest that the appropriate evaluation criteria would be whether use of multiple inheritance improves the source code over alternative approaches. This criteria assumes two things. One, multiple inheritance does not solve unique problems; there are alternative solutions (I believe this is true of any software construct). Hence, use of multiple inheritance is not refuted by the existence of an alternative. Two, the evaluation is subjective. Multiple people may look at a multiple inheritance solution and an alternative and have different opinions as to whether the source code is improved.

-- WayneMack

Examples:


EditText of this page (last edited February 19, 2006) or FindPage with title or text search