Inheritance or composition: Rely on "is-a" and "has-a"?
Solution 1
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.
Solution 2
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.
Solution 3
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.
Solution 4
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 :)
Solution 5
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.
Comments
-
Tachikoma almost 2 years
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.
-
Admin over 15 yearsWell a Manager is an Employee and an Employee is a Person. What's your point ?
-
Steven A. Lowe over 15 yearsJon, 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 over 15 yearsdone - see stackoverflow.com/questions/453879/…
-
Piskvor left the building over 15 years@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.
-
Epaga over 15 yearsThat is a perfect example for an explanation on WHY you should prefer composition.
-
John Nilsson over 15 yearsI fail to see why inheritance is better at code reuse than composition is?
-
John Nilsson over 15 yearsOne might also think that a Rectangle doesn't have anything, but rather that some RenderableThing has a Shape or something along those lines
-
Lordn__n over 15 yearsA 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
-
Jonathan Leffler over 15 yearsNote 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.
-
strager over 15 yearsNice examples! I wouldn't have thought of them myself, shamefully (unless I thought hard about it).
-
alterfox over 15 yearsI 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?
-
Tom over 15 years@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 over 15 yearsThat 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.
-
Sir Rippov the Maple over 15 yearsI'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 over 15 years@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.
-
Lawrence Dol over 15 yearsI 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.
-
Kirk Broadhurst over 13 yearsThis 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.
-
Luk Aron over 3 yearsI 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.
-
Scotty Jamison almost 2 yearsFor those who don't like square/rectangle, how about this one instead: An
ElectricCar
is-aCar
, but what if yourCar
class has agetGasMillageReport()
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 inCar
, or we can't use inheritance.