In the last two posts, I considered relatively exotic ways to build up a class: .NET’s partial class feature and extension methods. This time, I’ll compare two ways to make classes relate to each other: inheritance and composition, colloquially known as is-a and has-a relationships.
The programming literature builds a solid case for favoring composition over inheritance. The best-selling book, Design Patterns: Elements of Reusable Object-Oriented Software by the “Gang of Four” makes this point, as does Robert Martin’s well-known paper, Design Principles and Design Patterns.
Nevertheless, time pressures often push developers toward the quickest way to make the services of one class available through another. That would be inheritance. There are no pass-through methods to write, and very little can go immediately wrong. It should come as no surprise that one developer described a recent adventure in class design thus: “So I looked around for something to inherit from…”
But you’re one of the lucky few. You’re reading this blog, so you must have plenty of time on your hands – certainly time enough to consider my official list of…
Qualifiers for Inheritance
Only use inheritance if all of the following are true. Otherwise, use composition.
- The classes must have a true, semantic is-a relationship. Otherwise, you’re lying. Chances are good that the other programmers on your team are straight arrows, and lies will confuse them.
- You can think of no possible reason why you might want to mock the base type in unit testing. Composition allows you to inject such a mock in the derived type’s constructor. That can be very handy.
- You are willing to forego the possibility of changing the base type a run-time. Composition and an Inversion-of-Control container are a powerful combination.
- You need to use all the interfaces that the base type implements. If not, consider just inheriting from the interface(s) you need. (This is a corollary of the Interface Segregation Principle.) You can then instantiate or inject a member of what would have been the base type to implement those interfaces.
- Either you will always need the services of the base type, or constructing it costs nearly nothing. Otherwise, you might want to delay its construction until it’s actually needed. That’s impossible with inheritance, but easy with composition.
That’s my list. If you have anything to add, please leave a comment!