Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

As a rule of thumb, generalisation is used only in specific circumstances. For example, when we can say that X is literally a subclass of Y. So, we can happily say that a Horse is a subclass of Mammal. I have always been lead to believe that we should use generalisation and inheritence here. If we do not have this strict correspondence between two objects then we should not.

A Horse is a Mammal. However, it is also literally a mode of transportation. So what happens in a world of single inheritance where horse cannot inherit from both? Do we then subjugate the fact that a horse is literally a mode of transportation to realisational, i.e., an interface such as ITransportable?

share|improve this question
6  
Avoid -able abuse: the horse isn't tranport*able*, it's a transport*er*. –  NickC Apr 9 '11 at 14:04
4  
I'm concerned about extending OO concepts to physical-world metaphors. These metaphors seem to only confuse the issue, IMO. Is it not enough to say that "Interfaces describe responsibilities and concrete classes perform responsibilities?" –  greyfade Apr 9 '11 at 15:30
    
An Arabian is-a Horse. So are an Appaloosa and a Clydesdale. Those are relationships where the full subclass relationship makes sense. But they way you'd normally use them varies (for example, the Arabian participates in racing, whereas the Clydesdale is better for hauling loads) so they implement different interfaces. –  Donal Fellows Apr 9 '11 at 15:59
1  
@Renesis, you can transport a horse (but that was most likely not what was intended here) –  user1249 Apr 9 '11 at 20:07
    
@Renesis: I probably would have wrote ITransport if I had not been actively attempting to escape extra childcare duties. I could see the sunlight and the prospect of all one can eat buffet with friends. –  Carnotaurus Apr 10 '11 at 6:59

9 Answers 9

up vote 9 down vote accepted

The key here is that (single) inheritance is a scarce resource. You only have one superclass. Considering your domain will help you make the decision as to how to best allocate it. Are you writing a taxonomy program? Then you'll probably want to subclass Mammal. Are you writing a transportation program? Then you'll probably want to subclass Vehicle (or something).

Just because a horse is both a type of mammal and a type of transportation device does not mean both concepts are equally valuable in your domain.

Also consider the "favor composition over inheritance" maxim from the GoF book. Probably the best thing to come out of that book.

share|improve this answer
    
Yes, it's all about usefulness, assuming that the domain is well-known - I'll upvote this post. –  Carnotaurus Apr 10 '11 at 7:48
    
+1 "Probably the best thing to come out of that book.". So true. –  Oliver Weiler Jul 22 '11 at 9:41

Think about what "Is a mammal" actually means. From google:

A warm-blooded vertebrate animal of a class that is distinguished by the possession of hair or fur, the secretion of milk by females, and (typically) the birth of live young

So in the context of programming, it might have a GrowHair method (let's forget the reproductive system for now, that's way too complex). But there is very little in the implementation of GrowHair that is common across different types of Mammal.

The Universe class might call GrowHair on each Mammal on a timer loop, but it doesn't need to worry about the implementation, the different ways in which Humans and Dogs grow hair. Let the Human and Dog classes deal with that. It just wants to know that each Mammal has the property of hair growth, which can be activated through the GrowHair method.

Suddenly, this is an interface (a contract between the Mammal and the Universe that the Mammal will perform certain functions, regardless of the implementation) rather than a base class per se.

This is the direction in which OO has moved. Multiple inheritance caused a lot of problems and, in nearly all cases, all but one of the base classes could have existed without implementation (especially where we follow the "prefer composition over inheritance" approach). And so interfaces were born as a replacement.

share|improve this answer
    
+1 for pointing out just how hard it is to actually have two classes of object in the real world who would share any significant part of their implementation. –  jprete Apr 9 '11 at 17:50
3  
I never had many problems with multiple inheritance. I agree that realisation has been used to fill the gap for single inheritance languages. The problem with single inheritance is that it is suggestive of a possibly unified and universal Aristotlean style ontology. This ontology has been demonstrated to be flawed. Some might say that all class-based programming is flawed in this way: carnotaurus.tumblr.com/post/3248631891/… –  Carnotaurus Apr 10 '11 at 7:26
    
@Carnotaurus - Interesting post. Agree with some of it, not with some but this isn't a good forum for debate in that sense. What I will say is that the problems defined there are with class-based OO vs prototype-based OO, not with single inheritance vs multiple inheritance. I think realisation and prototyping are different, and equally valid, solutions to the same problem. You could argue that dynamic languages are another solution to it. –  pdr Apr 10 '11 at 11:43
    
I am aware that they are different but all class-based OO shares the same ontological flaw, with single inheritance having it to a much greater degree. Multiple inheritance has enough workarounds to circumvent this flaw. Prototype OO does not suffer from this. –  Carnotaurus Apr 10 '11 at 11:51
1  
@Carnotaurus - Multiple inheritance is all well and good until you have contradictory base classes. And you and Taivalsaari are right, if you were modelling something as complex as the whole world then prototypical languages may be more suited. Fortunately, most business models are simpler and classes seem to be much easier for most people to conceptualise. –  pdr Apr 10 '11 at 12:36

Simply put: Interfaces are for contracts, classes for implementations.
Interface inheritance is for type hierarchies, class inheritance (and composition) is for code reuse.

In general, you should use interfaces to express type relationships.

share|improve this answer
    
IDisagree, interfaces are not extensible, if you change it you break all implementations. You can always add members to classes. –  Max Toro Apr 9 '11 at 16:14
1  
@Max Toro: 1. If you add an abstract member to a class you'll have exactly the same problem. 2. Adhering to the ISP helps in keeping the effect manageable –  back2dos Apr 9 '11 at 16:50
1  
@Max Toro: Having all of the implementations break until you fix them is a feature static typing. –  Rodrick Chapman Apr 9 '11 at 22:17
    
@back2dos: 1. Virtual members, interfaces don't have those. –  Max Toro Apr 9 '11 at 23:32
1  
@Max Toro: The code will still break, because the client to the type in question can run into a pure virtual function call. Add a method to an interface, and you will see at compile time, which classes need to be adjusted. Add a virtual method to a class, you will have crashes at runtime instead. Add an abstract method to a class (as in Java), you will have exactly the same effect as with an interface (compiler complaining your don't implement it). I prefer to have my errors at compile time. –  back2dos Apr 10 '11 at 10:09

Multiple is-a relationships do cause issues with single inheritance, but in many cases the relationship isn't that hard to work out. For example, I would use an ITransportation interface, because different forms of transportation do not behave in anything like the same way. I would do this even if multiple inheritance was available; there is no "form of transportation" class in the real world, it's naturally an interface.

Much of the time I would say that multiple inheritance is a sign that your objects are doing too much, but it's something that needs to be thought about case-by-case. What's vital is to avoid using multiple inheritance to drag a lot of behavior into one class that is then responsible for too much in the design.

If you really want a class to do multiple inheritance in the single-inheritance world, then you still have the option of having the class implement interfaces and delegate the implementation to an internal object.

share|improve this answer
    
Just because something is intangible doesn't mean it should be an interface. A bank account is intangible but we see the Account class everywhere. –  Carnotaurus Apr 10 '11 at 7:37
    
You cannot strictly use "multiple inheritance in the single-inheritance world". As you say, composition and realisation are options. –  Carnotaurus Apr 10 '11 at 7:40
    
@Carnotaurus: I don't make "form of transportation" an interface because it's intangible. (It's not intangible, and it wouldn't matter if it were.) But if I had cars, walking, bicycles, and horses all in the same app, and if I were trying to represent multiple aspects of their existence, the transportation part would likely be an interface, because there's very little naturally shared behavior among the four. –  jprete Apr 10 '11 at 13:30

Let's talk about a more general concept: similarity.

To say that B is a subclass of A means that B is similar to A (but not that A is similar to B - thus similarity in the context of subtyping is not symmetric).

If B is similar to A then everywhere I can use an A I can use a B.

Multiple inheritance means that I can say B is similar to A and B is similar to C. This isn't a problem as long as A and C are not dissimilar to each other. If they are dissimilar then I'm basically saying that B is near to two things that aren't near each other, a contradiction.

So, for multiple inheritance to work, A and C either need to be similar to each other or they need to be mutually orthogonal (i.e. they live in different spaces have nothing to do with each other).

Well, we know that they can't be mutually similar because subtyping is not symmetric (A and C cannot both inherit from each other).

That means they must be mutually orthogonal which means they don't have any space in common. But this is brittle because even if they start off as orthogonal, this will break if they ever each get a method with the same name and signature (i.e they end up in the same nominal space - i.e. space of names). It can also break if they each, at some point, end up inheriting from the same base class (the diamond problem).

So it is, in fact, single-inheritance which holds in the general case.

share|improve this answer
    
This is not "similarity" so much as the LSP. –  Rein Henrichs Apr 9 '11 at 20:59
1  
@Rein Henrichs True, and the answer could be reduced to "MI makes it too easy to violate the LSP", but that may not be very helpful. –  Rodrick Chapman Apr 9 '11 at 22:05
    
This is an aside but I'll response. You are aware that there are other forms of inheritance that are not class-based which address this issue? Here is an entry point to the details: carnotaurus.tumblr.com/post/3248631891/… –  Carnotaurus Apr 10 '11 at 8:01
    
@Carnotaurus, I am indeed aware of prototype inheritance. Couching things in terms of similarity lets you talk about classical inheritance, structural vs nominal type systems, prototypes, type classes, etc in a general setting. –  Rodrick Chapman Apr 10 '11 at 21:56

I'll throw in my contribution even though the question has already been answered well enough. Thing is, programmers tend to think of architecture in a taxonomic as opposed to architecturally. That is to say, programmers will tend to look for classifications of Objects rather than structures of Objects. This can probably be attributed to real life: when you meet a person, you think of them as

  1. A woman
  2. A white woman
  3. A pretty white woman
  4. A pretty white woman in a nice dress.

This is triggered within about a second when our brain processes our view of the woman. You would end up with a class hierarchy:

class Woman {}
class WhiteWoman extends Woman {}
class PrettyWhiteWoman extends WhiteWoman {}
class NicelyDressedPrettyWhiteWoman extends PrettyWhiteWoman {}

Now, it is fairly obvious that this architecture is very naive, but we probably make these design mistakes where the hierarchy is not so easily defined.

In order to write more reusable code, we have to fight our brain's desire to classify and instead attempt to compose. "Composition over inheritance" as they say.

In your example, you are thinking that a horse "is a" mammal, and a horse "is a" method of transportation (i.e. extension). In fact, it is more correct to think that a horse "has a" mammalian biology and a horse "has a"n ability to act as transport. Thus, a Horse should implement the Mammalable and Transporter interface contracts.

Now we come to a point of contention about how some hierarchies should be structured. For example, say that on our farm we have a lot of FourLeggedAnimals, each of which exhibits some behavior that is identical to their four-legged brethren. On our farm, we also have Omnivores. Omnivores also also exhibit similar behavior. Problem is that some animals are no more Omnivores than they are four-legged. It does not make sense for FourLeggedAnimal to extend from Omnivore or vice versa. An example of this contention is if we have both humans and pigs on our farm.

Instead, we have to rethink our architecture. We should think about our objects from the inside out rather than the top down. Instead of thinking "what defines a horse," we should think "what composes a horse." In the context of our farm, this definition of horse may make the most sense (php-based pseudo-code):

interface Mammal {
   function growHair();
}
interface Transporter {
   function loadUp(bulk);
   function moveOut(destination);
}
class FourLeggedAnimal {
   public function walk() {
      echo "Going for a walk";
   }
}
class Omnivore {
   public function eat(Food food) {
      echo "Enjoying some food";
   }
}
class Herbivore {
   public function eat(Meat food) {
      echo "Blech!";
   }
   public function eat(Veggies food) {
      echo "Nom nom nom";
   }
}

class Horse implements Mammal, Transporter {
   private FourLeggedAnimal;
   private Herbivore;
   //Mammal/Transporter methods omitted
   public function __construct(FourLeggedAnimal fla, Herbivore h) {
      this.FourLeggedAnimal = fla;
      this.Herbivore = h;
   }
   public function eat(Food food) {
      this.Herbivore.eat(food);
   }
   public function walk() {
      this.FourLeggedAnimal.walk();
   }
}

This example is very naive and could probably use some improvement, but I hope the concept is there. Since we have moved away from the "is a" mentality, our architecture is more SOLID. Horse dependencies are inverted, we don't have to concern ourselves with Liskov substitution violations, etc.

The only downside to this architecture, and it is a big one, is the duplicated code. Do we really have to have an entire walk() method for Horse just to have it exhibit its FourLeggedAnimal behavior? If we can find a way around this inherent problem, there'd be no stopping us.

share|improve this answer
    
So any woman from PrettyWhiteWoman and up in your hierarchy is effectively ..dressless?! –  mlvljr Jul 21 '11 at 19:17
2  
@mlvljr why do you think they're so pretty? –  Explosion Pills Jul 21 '11 at 19:36

Putting aside issues with inheritance design (which applies to both interface inheritance and 'full' MI).

The answer is 'no', what you should be able to do with MI, you can do with SI and Interface composition. The difference is that SI+II requires you to write a lot more code to do the same thing and handle the management of this code duplication.

If you wanted to have a horse that was both an 'ITransporter' and an 'IStockKeepingUnit' then MI would allow you to just inherit and be done with it, SI requires you to create a class that implements the other interface and hook it up to the interface methods that are implemented by your horse class. As long as you don't go crazy with inheriting many layers deep and have several classes that inherit off the same class, then MI is the better choice.

If you have to implement your own specific methods for an interface and cannot simply re-implement the base class methods, then II is better (as its simpler).

I worked with some serious analysts in the past writing very lareg scale systems. They used MI a lot as they decomposed the system designs, so I can see the benefits in multiple reused interfaces. I also see the need to keep those interfaces 1 level deep or you can start tp get too complex.

share|improve this answer

So what happens in a world of single inheritance where horse cannot inherit from both?

You end up with a lot of noisy code to work around the lack of support for multiple inheritance. Instead of

 class A { private: *data* ; 
           public: void doA() { ... } }
 class B { private: *data* ; 
           public: void doB1() { ... } 
                   void doB2() { ... };
         }

 public class MI: public A, public B { ... }

you need: public class A { ...; } public interface B { ...; } public class BImpl implements B { ...; }

 public class SI extends A implements B {
    private BImpl bImpl;
    public void doB1() { bImpl.doB1(); }
    public void doB2() { bImpl.doB2(); }
 }

All that trouble to work around a possible conflict in method names between the two base classes.

BTW, it is a mistake to attempt to create OO classes to mimic categories in the domain. Create OO classes only when needed to implement use cases, and use inheritance to improve the program structure. I wouldn't worry about Animal and Transporter until I also had a Cow and a Car, and a use case that required breeding the Horses and the Cows, and traveling by Horse or Car. At that point I might wish for a language that supported multiple inheritance.

share|improve this answer

Yes and no.

On the one hand, you don't need classes at all - if your language is Turing complete and has all the I/O capabilities you need, you have all the tools you need. But that's an extreme interpretation of "need".

On the other hand, having support for multiple inheritance built into the language can simplify and clarify some tasks. You could still achieve the same requirements with only single inheritance, but you'd need to structure things differently, adding a fair bit more clutter.

A similar issue happens with single dispatch vs. multiple dispatch. I used to claim that multiple dispatch makes the visitor pattern redundant. I think that's probably wrong now because multiple dispatch in itself doesn't separate out the responsibilities for the visitor and visitee roles, but it does lead to a much simpler and probably more flexible variant of the visitor pattern. It's compelling enough that for languages that don't support multiple dispatch (most of the popular OOP languages), quite a few people have written domain-specific languages. Myself included ;-)

I'd guess that there are some design patterns that could be simplified or eliminated with the use of multiple inheritance, but I can't say for sure.

As is often the case, it's a balance issue. It might seem easiest to have the perfect tool for every job, but then you need an infinite number of tools. A minimalist toolkit means you have more manual work to do - but more experience with the particular tools you have available. Programmers will probably be shooting and bombing each other until the end of time over how big the toolkit should be, and precisely which tools should be included.

One point worth noting - multiple inheritance brings more complexity along with it than may be immediately obvious. The "diamond inheritance pattern" often gets a mention in this context. Consider this pattern...

    A
   / \
  /   \
 B     C
  \   /
   \ /
    D

Does D inherit a single copy of A, or two independent copies of A? This question can only occur because D inherits both B and C. In C++, it's a choice - and an indirectly expressed one. If both B and C inherit A using virtual inheritance, then D inherits a single shared copy of A - otherwise there are two independent copies of A.

But what about this case...

    A
   /|\
  / | \
 B  C  D
  \ | /
   \|/
    E

And what if B and C both uses virtual inheritance, but D does not?

I think the correct answer is that E will inherit two copies of A - one shared by B and C, and a separate copy for D. Not to hard in principle, but in code, the relevant information is scattered around.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.