136

I always read that composition is to be preferred over inheritance. A blog post on unlike kinds, for example, advocates using composition over inheritance, but I can't see how polymorphism is achieved.

But I have a feeling that when people say prefer composition, they really mean prefer a combination of composition and interface implementation. How are you going to get polymorphism without inheritance?

Here is a concrete example where I use inheritance. How would this be changed to use composition, and what would I gain?

Class Shape
{
    string name;
  public:
    void getName();
    virtual void draw()=0;
}

Class Circle: public Shape
{
    void draw(/*draw circle*/);
}
whalabi
  • 103
  • 3
MustafaM
  • 1,958
  • 3
  • 15
  • 13
  • 67
    No, when people say prefer composition they really mean **prefer** composition, **not** *never ever ever use inheritance*. Your whole question is based on a faulty premise. Use inheritance when it's appropriate. – Bill the Lizard Feb 09 '12 at 02:59
  • 2
    You might also want to take a look at http://programmers.stackexchange.com/questions/65179/where-does-this-concept-of-favor-composition-over-inheritance-come-from/65183#65183 – vjones Feb 09 '12 at 03:25
  • 2
    I agree with the Bill, I have see using inheritance a common practice in GUI development. – Prashant Cholachagudda Feb 10 '12 at 07:57
  • 2
    You want to use composition because a square is just the composition of two triangles, in fact I think all shapes other than ellipse are just compositions of triangles. Polymorphism is about contractual obligations and 100% removed from inheritance. When triangle get's a bunch of oddities added to it because somebody wanted to make it capable of generating pyramids, if you inherit from Triangle you're going to get all of that even though you're never going to generate a 3d pyramid from your hexagon. – Jimmy Hoffa Feb 11 '13 at 22:28
  • 3
    @BilltheLizard I think a lot of the people who say it *really do mean* to never ever ever use inheritance, but they're wrong. – user253751 Nov 06 '15 at 21:25
  • 1
    @immibis, you are half right. "prefer composition" is indeed just a watered down version of what is really meant, as you say. Where you go wrong is in thinking the - well-thought out, derived from evidence and experience - advice to never ever use inheritance is somehow wrong. Inheritance is arguably an even worse feature than `goto` and global variables. It's not unreasonable to suggest it's the single worse feature ever added to programming languages ever. It depresses me that apparently clever programmers go into denial over this and try to suggest it's valid to use it sometimes. – David Arno Jan 26 '16 at 10:14
  • 1
    @DavidArno Wow, try calming down and applying a little more reason than emotion to your argument. Do you think you'll win many hearts and minds by coming right out of the gate with a blatantly biased rant and the implication that acknowledging the many (if restrained) valid uses of inheritances disqualifies thousands of people from being "apparently clever programmers"? – underscore_d Feb 26 '16 at 19:49
  • @underscore_d Out of curiosity, do you take the view that there are valid (if restrained) uses of goto? – David Arno Feb 26 '16 at 21:49
  • @DavidArno Only in extremely edge cases where the alternative - avoiding `goto` out of puritanism - leads to even more absurd code. I think it's usually a bad idea, but I don't universally demonise it like many commentators do. Anyway, I've never had to use it in my own C or C++. More importantly, it doesn't seem relevant to the actual subject, which is inheritance. What relevance were you trying to invoke, exactly? Hoping I'd say something to flag myself as A Bad Programmer and shore up your rant? – underscore_d Feb 27 '16 at 08:47
  • @underscore_d, no, there's no attempt at trickery; just genuine curiosity. I see both `goto` and inheritance as sitting in the realm of anti-patterns. I don't see any use for either, save when forced to by language or framework restraints. – David Arno Feb 29 '16 at 08:28
  • @DavidArno Fair enough. I've no doubt that inheritance is _over_used (as was the subject here), though I've not seen anything bad enough that I'd call as an anti-pattern by default. Either way, my perspective is limited and mostly hinges on C++ requiring inheritance to facilitate polymorphism (at least without some horrible workarounds) - which you might class as a language restraint? Having to use it for that reason does mean I've found a few other cases where it saves time, but those I know can be done other ways. Polymorphism's the only thing that I've not seen a practical alternative basis – underscore_d Feb 29 '16 at 12:00
  • This article may help: http://www.yegor256.com/2016/09/13/inheritance-is-procedural.html It explains the difference between implementation inheritance and subtyping and why the former is bad, while the latter is good. – yegor256 Sep 13 '16 at 17:17
  • @JimmyHoffa What is meant by "Polymorphism is about contractual obligations and **100% removed from inheritance**"? – samus Mar 06 '17 at 18:27
  • The given example looks like *interface* inheritance (regardless of the use of the `class` keyword, in this example there is barely any implementation at all. If you remove the field `name`, there is none). When people mention the perils of inheritance, they mean *implementation* inheritance. – Andres F. Mar 07 '17 at 16:02
  • 1
    @SamusArin Polymorphism is the ability to treat many things like the same thing; you only need consistent contracts across a set of types for all those types to be treated as the same type - treated as the same contract. Inheritance is not about your type contract, inheritance is about taking a type and adding stuff to it, or changing it's internal workings. – Jimmy Hoffa Mar 14 '17 at 19:09
  • @JimmyHoffa This makes sense, and to be honest this is the first time I recall reading it put this way (the contract stuff, Im aware of the many-same concept). I'm going to dig up the textbook and look for the quote that burned this notion of inheritance centric polymorphism into my mind (when I'm digging out my UML texts from storage). It could've been ambiguous, or I could've misinterpreted. – samus Mar 15 '17 at 13:06
  • It's not just an abstract concept - inheritance is implemented behind the scenes by setting up internal logic and maps so the programmer doesn't have to use a type member and check it in switch statements, if statements, or set up their own maps. Used how and where it should be used that saves a lot of tedious coding and makes the code more clear. If that result is not achieved, it was being used in the wrong way. – Craig Hicks Feb 24 '21 at 02:19

5 Answers5

98

Preferring composition isn't just about polymorphism. Although that is part of it, and you are right that (at least in nominally typed languages) what people really mean is "prefer a combination of composition and interface implementation." But, the reasons to prefer composition (in many circumstances) are profound.

Polymorphism is about one thing behaving multiple ways. So, generics/templates are a "polymorphic" feature in so far as they allow a single piece of code to vary its behavior with types. In-fact, this type of polymorphism is really the best behaved and is generally referred to as parametric polymorphism because the variation is defined by a parameter.

Many languages provide a form of polymorphism called "overloading" or ad hoc polymorphism where multiple procedures with the same name are defined in an ad hoc manner, and where one is chosen by the language (perhaps the most specific). This is the least well behaved kind of polymorphism, since nothing connects the behavior of the two procedures except developed convention.

A third kind of polymorphism is subtype polymorphism. Here a procedure defined on a given type, can also work on a whole family of "subtypes" of that type. When you implement an interface or extend a class you are generally declaring your intention to create a subtype. True subtypes are governed by Liskov's Substitution Principle, which says that if you can prove something about all objects in a supertype you can prove it about all instances in a subtype. Life gets dangerous though, since in languages like C++ and Java, people generally have unenforced, and often undocumented assumptions about classes which may or may not be true about their subclasses. That is, code is written as if more is provable than it really is, which produces a whole host of issues when you subtype carelessly.

Inheritance is actually independent of polymorphism. Given some thing "T" which has a reference to itself, inheritance happens when you create a new thing "S" from "T" replacing "T"s reference to itself with a reference to "S". That definition is intentionally vague, since inheritance can happen in many situations, but the most common is subclassing an object which has the effect of replacing the this pointer called by virtual functions with the this pointer to the subtype.

Inheritance is a dangerous like all very powerful things inheritance has the power to cause havoc. For example, suppose you override a method when inheriting from some class: all is well and good until some other method of that class assumes the method you inherit to behave a certain way, after all that is how the author of the original class designed it. You can partially protect against this by declaring all methods called by another of your methods private or non-virtual (final), unless they are designed to be overridden. Even this though isn't always good enough. Sometimes you might see something like this (in pseudo Java, hopefully readable to C++ and C# users)

interface UsefulThingsInterface {
    void doThings();
    void doMoreThings();
}

...

class WayOfDoingUsefulThings implements UsefulThingsInterface{
     private foo stuff;
     public final int getStuff();
     void doThings(){
       //modifies stuff, such that ...
       ...
     }
     ...
     void doMoreThings(){
       //ignores stuff
       ...
     }
 }

you think this is lovely, and have your own way of doing "things", but you use inheritance to acquire the ability to do "moreThings",

class MyUsefulThings extends WayOfDoingUsefulThings{
     void doThings {
        //my way
     }
}

And all is well and good. WayOfDoingUsefulThings was designed in such a way that replacing one method doesn't change the semantics of any other... except wait, no it wasn't. It just looks like it was, but doThings changed mutable state that mattered. So, even though it didn't call any override-able functions,

 void dealWithStuff(WayOfDoingUsefulThings bar){
     bar.doThings()
     use(bar.getStuff());
 }

now does something different than expected when you pass it a MyUsefulThings. Whats worse, you might not even know that WayOfDoingUsefulThings made those promises. Maybe dealWithStuff comes from the same library as WayOfDoingUsefulThings and getStuff() isn't even exported by the library (think of friend classes in C++). Worse still, you have defeated the static checks of the language without realizing it: dealWithStuff took a WayOfDoingUsefulThings just to make sure that it would have a getStuff() function that behaved a certain way.

Using composition

class MyUsefulThings implements UsefulThingsInterface{
     private way = new WayOfDoingUsefulThings()
     void doThings() {
        //my way
     }
     void doMoreThings() {
        this.way.doMoreThings();
     }
}

brings back static type safety. In general composition is easier to use and safer than inheritance when implementing subtyping. It also lets you override final methods which means that you should feel free to declare everything final/non-virtual except in interfaces the vast majority of the time.

In a better world languages would automatically insert the boilerplate with a delegation keyword. Most don't, so a downside is bigger classes. Although, you can get your IDE to write the delegating instance for you.

Now, life isn't just about polymorphism. You can don't need to subtype all the time. The goal of polymorphism is generally code reuse but it isn't the only way to achieve that goal. Often time, it makes sense to use composition, without subtype polymorphism, as a way of managing functionality.

Also, behavioral inheritance does have its uses. It is one of the most powerful ideas in computer science. Its just that, most of the time, good OOP applications can be written using only using interface inheritance and compositions. The two principles

  1. Ban inheritance or design for it
  2. Prefer composition

are a good guide for the reasons above, and don't incur any substantial costs.

Philip JF
  • 2,193
  • 14
  • 10
  • 6
    Nice answer. I would summarize it that trying to achieve code reuse with inheritance is plain wrong path. Inheritance is very strong constraint (imho adding "power" is wrong analogy!) and creates strong dependency between inherited classes. Too many dependencies = bad code :) So inheritance (aka "behaves as") typically shines for unified interfaces (=hiding complexity of inherited classes), for anything else think twice or use composition... – MaR Feb 09 '12 at 12:39
  • 2
    This is a good answer. +1. At least in my eyes, it does seem though that the extra complication may be a substantial cost, and this has made me personally a little hesitant to make a big deal out of preferring composition. Especially when you're trying to make a lot of unit testing-friendly code through interfaces, composition, and DI (I know this is adding other things in), it seems very easy to make someone go through several different files to look up very few details. Why is this not mentioned more often though, even when only dealing with the composition over inheritance design principle? – Panzercrisis Mar 07 '17 at 14:10
  • 1
    I would say inheritance is safe if you're not overriding methods. But composition is still a more flexible solution. – jmrah Nov 13 '19 at 03:11
69

Polymorphism does not necessarily imply inheritance. Often inheritance is used as an easy means to implement Polymorphic behaviour, because it is convenient to classify similar behaving objects as having entirely common root structure and behaviour. Think of all those car and dog code examples you've seen over the years.

But what about objects that aren't the same. Modelling a car and a planet would be very different, and yet both might want to implement Move() behaviour.

Actually, you basically answered your own question when you said "But I have a feeling that when people say prefer composition, they really mean prefer a combination of composition and interface implementation.". Common behaviour can be provided through interfaces and a behavioural composite.

As to which is better, the answer is somewhat subjective, and really comes down to how you want your system to work, what makes sense both contextually and architecturally, and how easy it will be to test and maintain.

S.Robins
  • 11,385
  • 2
  • 36
  • 52
  • 1
    In practice, how often does "Polymorphism via Interface" show up, and is it considered normal (as opposed to being an exploitation of a langues expressiveness). I would wager that polymorphism via inheritance was by careful design, not some consequence of the language (C++) discovered after it's specification. – samus Mar 06 '17 at 18:26
  • 3
    Who would call move() on a planet and a car and consider it the same ?! The question here is in what context should they be able to move ? If they both are 2D objects in a simple 2D game, they could inherit the move, if they were objects in a massive data simulation it might not make much sense to let them inherit from the same base and as a consequence you have to use an interface – NikkyD Mar 07 '17 at 15:09
  • 3
    @SamusArin It [shows](https://docs.oracle.com/javase/8/docs/api/java/util/List.html) [up](https://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.html) [everywhere](https://docs.oracle.com/javase/8/docs/api/java/util/Map.html), and is considered perfectly normal in languages which support interfaces. What do you mean, "an exploitation of a language's expressiveness"? This is what interfaces *are there for*. – Andres F. Mar 07 '17 at 15:10
  • 1
    @AndresF. I just came across "Polymorphism via Interface" in an example while reading through "Object Oriented Analysis and Design with Applications" which demonstrated the Observer pattern. I then realized my answer. – samus Mar 07 '17 at 15:32
  • @AndresF. I guess my vision was a bit blinded regarding this because of how I used polymorphism in my last (first) project. I had 5 record types that all derived from the same base. Anyways thanks for the enlightenment. – samus Mar 07 '17 at 16:05
32

The reason people say this is that beginning OOP programmers, fresh out of their polymorphism-through-inheritance lectures, tend to write large classes with lots of polymorphic methods, and then somewhere down the road, they end up with an unmaintainable mess.

A typical example comes from the world of game development. Suppose you have a base class for all your game entities - the player's spaceship, monsters, bullets, etc.; each entity type has its own subclass. The inheritance approach would use a few polymorphic methods, e.g. update_controls(), update_physics(), draw(), etc., and implement them for each subclass. However, this means you're coupling unrelated functionality: it is irrelevant what an object looks like for moving it, and you don't need to know anything about its AI to draw it. The compositional approach instead defines several base classes (or interfaces), e.g. EntityBrain (subclasses implement AI or player input), EntityPhysics (subclasses implement movement physics) and EntityPainter (subclasses take care of drawing), and a non-polymorphic class Entity that holds one instance of each. This way, you can combine any appearance with any physics model and any AI, and since you keep them separate, your code will be much cleaner too. Also, problems such as "I want a monster that looks like the balloon monster in level 1, but behaves like the crazy clown in level 15" go away: you simply take the suitable components and glue them together.

Note that the compositional approach still uses inheritance within each component; although ideally you'd use just interfaces and their implementations here.

"Separation of concerns" is the key phrase here: representing physics, implementing an AI, and drawing an entity, are three concerns, combining them into an entity is a fourth. With the compositional approach, each concern is modelled as one class.

tdammers
  • 52,406
  • 14
  • 106
  • 154
  • 1
    This is all good, and advocated in the article linked in the original question. I would love (whenever you get the time) if you could provide a simple example involving all of these entities, while also maintaining polymorphism (in C++). Or point me to a resource. Thanks. – MustafaM Feb 10 '12 at 03:01
  • 1
    I know your answer is very old but I did the same exact thing as you mentioned in my game and now going for composition over inheritance approach. – Sneh Oct 10 '15 at 20:44
  • "I want a monster that looks like..." how does this make sense ? An interface does not give any implementation, you would have to copy the look and behaviour code one way or the other – NikkyD Mar 07 '17 at 15:04
  • 2
    @NikkyD You take `MonsterVisuals balloonVisuals` and `MonsterBehaviour crazyClownBehaviour` and instantiate a `Monster(balloonVisuals, crazyClownBehaviour)`, alongside the `Monster(balloonVisuals, balloonBehaviour)` and `Monster(crazyClownVisuals, crazyClownBehaviour)` instances that were instantiated on level 1 and level 15 – Caleth Feb 08 '18 at 12:50
19

You will see this cycle a lot in software development discourse:

  1. Some feature or pattern (let's call it "Pattern X") is discovered to be useful for some particular purpose. Blog posts are written extolling the virtues about Pattern X.

  2. The hype leads some people to think you should use Pattern X whenever possible.

  3. Other people get annoyed to see Pattern X used in contexts where it is not appropriate, and they write blog posts stating that you should not always use Pattern X and that it is harmful in some contexts.

  4. The backlash causes some people to believe Pattern X is always harmful and should never be used.

You will see this hype/backlash cycle happen with almost any feature from GOTO to patterns to SQL to NoSQL and to, yes, inheritance. The antidote is to always consider the context.

Having Circle descend from Shape is exactly how inheritance is supposed to be used in OO languages supporting inheritance.

The rule-of-thumb "prefer composition over inheritance" is really misleading without context. You should prefer inheritance when inheritance is more appropriate, but prefer composition when composition is more appropriate. The sentence is directed towards people at stage 2 in the hype cycle, who think inheritance should be used everywhere. But the cycle has moved on, and today it seems to cause some people to think inheritance is somehow bad in itself.

Think of it like a hammer versus a screwdriver. Should you prefer a screwdriver over a hammer? The question does not make sense. You should use the tool appropriate for the job, and it all depends on what task you need to get done.

Panzercrisis
  • 3,145
  • 4
  • 19
  • 34
JacquesB
  • 57,310
  • 21
  • 127
  • 176
13

The example you've given is one where inheritance is the natural choice. I don't think anyone would claim that composition is always a better choice than inheritance -- it's just a guideline that means that it's often better to assemble several relatively simple objects than to create lots of highly specialized objects.

Delegation is one example of a way to use composition instead of inheritance. Delegation lets you modify the behavior of a class without subclassing. Consider a class that provides a network connection, NetStream. It might be natural to subclass NetStream to implement a common network protocol, so you might come up with FTPStream and HTTPStream. But instead of creating a very specific HTTPStream subclass for a single purpose, say, UpdateMyWebServiceHTTPStream, it's often better to use a plain old instance of HTTPStream along with a delegate that knows what to do with the data it receives from that object. One reason it's better is that it avoids a proliferation of classes that have to be maintained but which you'll never be able to reuse. Another reason is that the object that serves as the delegate can also be responsible for other things, such as managing the data received from the web service.

Caleb
  • 38,959
  • 8
  • 94
  • 152