32

When I design classes and have to choose between inheritance and composition, I usually use the rule of thumb: if the relationship is "is-a" - use inheritance, and if the relationship is "has-a" - use composition.

Is it always right?

Thank you.

Federico A. Ramponi
  • 46,145
  • 29
  • 109
  • 133
Igor
  • 26,650
  • 27
  • 89
  • 114

10 Answers10

41

No - "is a" does not always lead to inheritence. A well cited example is the relationship between a square and a rectangle. A square is a rectangle, but it will be bad to design code that inherits a Square class off a Rectangle class.

My suggestion is to enhance your "is a / has a" heuristic with the Liskov Substitution Principle. To check whether an inheritence relationship complies with the Liskov Substitution Principle, ask whether clients of a base class can operate on the sub class without knowing that it is operating on a sub class. Of course, all the properties of the sub class must be preserved.

In the square / rectangle example, we must ask whether a client of rectangle can operate on a square without knowing that it is a square. All that the client must know is that it is operating on a rectangle. The following function demonstrates a client that assumes that setting the width of a rectangle leaves the height unchanged.

void g(Rectangle& r)
{
    r.SetWidth(5);
    r.SetHeight(4);
    assert(r.GetWidth() * r.GetHeight()) == 20);
}

This assumption is true for a rectangle, but not for a square. So the function cannot operate on a square and therefore the inheritence relationship violates the Liskov Substitution principle.

Other examples

ruohola
  • 21,987
  • 6
  • 62
  • 97
Sir Rippov the Maple
  • 7,491
  • 5
  • 42
  • 52
  • 2
    Ok, it illustrates the point. But still, why would a square be mutable? – John Nilsson Jan 17 '09 at 21:18
  • 2
    Everyone has heard the square and rectangle problem, but I have never actually come across a real world example of this (I am a junior dev, so that doesn't mean much). Can you provide one? – Ed S. Jan 17 '09 at 23:49
  • 1
    @Ed - Example: TCP socket uses a file descriptor, so you might say it is-a file. However, most file APIs assume random access is possible (e.g. fseek). A TCP socket is also an IP socket, but no clients should be accessing the IP layer directly (let the TCP stack handle it). – Tom Jan 18 '09 at 06:27
  • 1
    I've seen a real word example where we have a Transaction class with a collection of Sources and Destinations. We subclass Withdrawal, DebitOrder, ScheduledWithdrawal, etc. but those classes constrain the Sources and Destinations to be a certain type, hence LSP is violated. – Sir Rippov the Maple Jan 18 '09 at 08:28
  • 2
    @John - your question highlights the fact that inheritence (as it relates to the Liskov Substitution Principle) is all about behaviour. If the square was not mutable, then there would be no problem because no client can change the width or the height. – Sir Rippov the Maple Jan 19 '09 at 11:40
  • 1
    I agree with Tumpi; the square/rectangle example cited is implying a problem with mutability more so than with inheritance. The is-a relationship would be well expressed by inheritance if the shape was not mutable. – Lawrence Dol Feb 05 '09 at 04:48
  • 3
    This is a furphy. A square might be a rectangle in mathematics, but if you define rectangle to be a quad that can height and width set independently, then square doesn't fit. In this case, rectangle actually extends square by having the additional property of two different side lengths, instead of just one. – Kirk Broadhurst Feb 24 '11 at 05:31
  • __I think this is not a good example!!!!!__ since a rectangle may be a square in some cases, the assumption of __setting the width of a rectangle leaves the height unchanged__ is fundamentally wrong. In my opinion, is a relationship that could be very well done in inheritance. – Luk Aron Jan 17 '21 at 03:47
  • For those who don't like square/rectangle, how about this one instead: An `ElectricCar` is-a `Car`, but what if your `Car` class has a `getGasMillageReport()` method? No mutability here. The is-a rule fails to ask us to inspect the behaviors of the classes, while LSP does. LSP > is-a. Sure you could say this is silly, because the Car class is obviously not general enough to include the concept of an electric car, but that's the whole point of LSP, we can follow LSP to see this issue and know that either something needs to change in `Car`, or we can't use inheritance. – Scotty Jamison Jun 15 '22 at 02:44
9

Yes and no.

The line can be blurred. This hasn't been helped by some pretty terrible examples of OO programming from the early days of OO like: Manager is an Employee is a Person.

The thing you have to remember about inheritance is: inheritance breaks encapsulation. Inheritance is an implementation detail. There's all sorts written on this subject.

The pithiest way to sum it up is:

Prefer composition.

That doesn't mean use it to the complete exclusion of inheritance. It just means inheritance is a fallback position.

cletus
  • 616,129
  • 168
  • 910
  • 942
  • 2
    Well a Manager is an Employee and an Employee _is_ a Person. What's your point ? –  Jan 17 '09 at 19:03
  • @Iraimbilamja: Yes, a people Manager usually is-a Employee, but this is something completely different from the Manager pattern. Unfortunately, I've seen some explanations which conflated these two meanings. – Piskvor left the building Jan 17 '09 at 20:40
  • That is a perfect example for an explanation on WHY you should prefer composition. – Epaga Jan 17 '09 at 20:41
  • 3
    A Manager is an Employee but a Person has (zero or more) Employee roles is a MUCH better abstraction. If your objects change type (class) that's a warning sign you're doing something wrong and that's what happens if an Employee becomes a Manager. You can argue Manager is a property of Employee too – cletus Jan 17 '09 at 22:09
  • Note that the question originally said both "is a" and "has a" were inheritance - see comments to my answer. It is now corrected to match your assumed version, but 'other way round' was officially inaccurate. – Jonathan Leffler Jan 17 '09 at 23:14
8

If the relationship is "is-a" - use inheritance, and if the relationship is "has-a" - use composition. Is it always right?

In a sense, yes. But you must be careful not to introduce unnecessary, artificial "is-a" relationships.

For example, one may think that a ThickBorderedRectangle is-a Rectangle, which seems reasonable at first sight, and decide to use inheritance. But this situation is better described saying that a Rectangle has-a Border, which may or may not be a ThickBorder. In this case he would better prefer composition.

In the same fashion, one may think that a ThickBorder is-a special Border and use inheritance; but it's better to say that a Border has-a width, hence preferring composition.

In all these ambiguous cases, my rules of thumb are think twice and, as others advised, prefer composition over inheritance.

Federico A. Ramponi
  • 46,145
  • 29
  • 109
  • 133
  • 1
    One might also think that a Rectangle doesn't have anything, but rather that some RenderableThing has a Shape or something along those lines – John Nilsson Jan 17 '09 at 21:23
  • Nice examples! I wouldn't have thought of them myself, shamefully (unless I thought hard about it). – strager Jan 18 '09 at 00:45
3

I don't think it's as simple as "is-a" vs "has-a" - as cletus said, the line can get very blurry. It's not like two people would always come to the same conclusion given just that as a guideline.

Also as cletus said, prefer composition over inheritance. To my mind, there have to be really good reasons to derive from a base class - and in particular, the base class really needs to have been designed for inheritance, and indeed for the kind of specialization you want to apply.

Beyond that - and this will probably be an unpopular idea - it comes down to taste and gut feeling. Over time I think good developers get a better sense of when inheritance will work and when it won't, but they may well find it hard to express clearly. I know I find it hard, as this answer demonstrates. Maybe I'm just projecting the difficulty onto other people though :)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Jon, where do you get this "designed for inheritance" mantra? This is the 3rd or 4th time I've heard you say this, and I just don't understand it. Do I have to buy and read your book to get this explained? Or should I just ask a question for you to answer so you get more rep for it? ;-) I'll do that – Steven A. Lowe Jan 17 '09 at 19:29
  • done - see http://stackoverflow.com/questions/453879/how-do-you-design-a-class-for-inheritance – Steven A. Lowe Jan 17 '09 at 19:33
2

Inheritance doesn't always mean there is an "is-a" relationship and the lack of one doesn't always mean there isn't.

Examples:

  • If you use protected or private inheritance then you lose external substitutability, that is, as far as anyone outside of your hierarchy is concerned a Derived is not a type of Base.
  • If you override a base virtual member and change behaviour observable from someone referencing the Base, from the original Base implementation, then you have effectively broken substitutability. Even though you have a public inheritance heirarchy Derived is-not-a Base.
  • Your class can sometimes be substituitable for another without any inheritance relationship at work. For example, if you were to use templates and identical interfaces for the appropriate const member functions then an "Ellipse const&" where the Ellipse happens to be perfectly circular could be substituitable for a "Circle const&".

What i'm trying to say here is it takes a lot of thought and work to maintain an "is-a" substitutable relationship between two types whether you use inheritance or not.

nly
  • 179
  • 7
1

NoYes. If the relationship is "has-a", use composition. (Added: the original version of question said "use inheritance" for both - a simple typo, but the correction alters the first word of my answer from "No" to "Yes".)

And use composition by default; only use inheritance when necessary (but then don't hesitate to use it).

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
1

People often say that inheritance is an "is-a" relationship, but that can get you into trouble. Inheritance in C++ splits into two ideas: code reuse and defining interfaces.

The first says "My class is like this other class. I'll just write code for the delta between them and reuse the other implementation from the other class as much as I can."

The second depends on abstract base classes and is a list of methods the class promises to implement.

The first can be very handy, but it can also cause maintenance problems if not done well and so some people will go so far as saying you should never do it. Most people emphasize the second, using inheritance for what other languages explicitly call an interface.

John D. Cook
  • 29,517
  • 10
  • 67
  • 94
1

I think John has the right Idea. Your looking at your class relationships as creating model which is what they try to get you to do when they start teaching you OOP principals. Really, your better off looking at how things function in terms the codes architecture. That is, whats the best way for other programmers (or yourself) to re-use your code without breaking it or having to look at the implementation to see what it does.

I've found that inheritance often breaks the "black box" ideal, so if information hiding is important it may not be the best way to go. I've found increasingly that inheritance is better for providing a common interface than it is for re-using components in other places.

Of course every situation is unique, and there is no correct way to make this decision, only rules of thumb.

BigSandwich
  • 2,768
  • 2
  • 22
  • 26
0

At first, I would like to say that I totally agree to the most up-voted answer that strengthens is-A relationship should be asserted by a test method to check conformity of LSP.

Actually, the idea was introduced by the book, [Agile Principles, Patterns, Practices by Robert C. Martin & Micah Martin]

In the book was introduced a heuristics that a subclass cannot have less responsibility than its base class, which can be applied to the well-known Square-Rectangle example to find a square is not a rectangle.

However, I think it is one of the worst examples in OOP explanation.

It is because a square is no more than a state that a rectangle object can reach so trying to find out what relationship is there between a class and one of its instances are far wrong approach.

In real world, out of the square, we have a lot more flavored states of a Rectangle object like landscape, portrait, 16:9(1080P), 4:3(VGA) and etc.

However, a program have no flavor to any of them.

Big Square
  • 55
  • 6
  • This does not really answer the question. If you have a different question, you can ask it by clicking [Ask Question](https://stackoverflow.com/questions/ask). To get notified when this question gets new answers, you can [follow this question](https://meta.stackexchange.com/q/345661). Once you have enough [reputation](https://stackoverflow.com/help/whats-reputation), you can also [add a bounty](https://stackoverflow.com/help/privileges/set-bounties) to draw more attention to this question. - [From Review](/review/late-answers/32702266) – Vishal Beep Sep 17 '22 at 14:47
-2

My rule of thumb (though not concrete) is that if I can use the same code for different classes, then I put that code into a parent class and use inheritance. Otherwise I use composition.

This causes me to write less code which is inherently easier to maintain.

Sam
  • 2,166
  • 2
  • 20
  • 28
  • 1
    I fail to see why inheritance is better at code reuse than composition is? – John Nilsson Jan 17 '09 at 21:21
  • 2
    I have done this before but now I know it's not a good idea, especially in languages that don't support multiple inheritance. What if later you need to reuse that code in a class that needs to belong in another hierarchy? – Germán Jan 18 '09 at 03:33
  • 1
    That also causes you to have more interdependencies within your codebase. While it's less code, it will end up being more complex due to the inability to analyze anything independently. – Tom Jan 18 '09 at 06:34