As it often does, my day started with a quick peek at the open pull requests. We had two similar classes, one which had a parent class and one which didn’t. The discussion centered around whether or not the second class should also inherit from the parent class since it had very similar functionality. In the end we decided that there should be no parent class. After all, these days the mantra is, “Avoid inheritance and embrace composition”.
You have to understand that this was a Monday morning and the object model was obviously improved by our actions. One more code smell gone. On to bigger and better problems!
“Why should we avoid inheritance?”
Sometimes I think my hearing needs checking. Often I find that when I am particularly chuffed with my efforts and trying to remember where I hid the good coffee beans I get this annoying voice in my head. Best just to ignore it.
“What makes composition better?”
This time it was my IM pinging at me. Dawid was kindly reminding me that I should really think about this problem. I guess there is no getting around it. “Just say that Erich Gamma says composition is better than inheritance and get back to looking for the coffee”, I thought. Erich Gamma knows way more about OOD than I do, so it must be right.
“So exactly how do you use Strategy Pattern in place of inheritance?”
This inner voice was seriously disrupting my carefully cultivated Winnie-the-Pooh, Zen-like state of mind. Oh well, it’s not like I would ever remember where I hid the coffee. Everyone knows that it’s twice as hard to find coffee than it is to hide it. I was quite clever in hiding it, so there is no chance I will ever find it again.
Of course I know the answer to this question. How could I not? I’ve spent decades writing object models, both good and bad. I’ve made every mistake and then made them again just to be sure they were actually mistakes. I know from all this experience that it always works out better if you avoid inheritance unless you need it. And this is how you have to think:
… Maybe I should just go back to looking for the coffee.
Knowing something to be true and being able to say why it is true are different things. As humbling as it may be, sometimes knowing something to be true and it actually being true are different things. Is it really true in this case? Erich Gamma said so. So did a lot of other people. But to be honest, I have seen precious few good descriptions of this issue. Pretty much all I see on the internet are a list of good natured assertions. Isn’t that good enough? After all, Erich Gamma and everybody on the internet can’t be wrong!
Well, I finally spotted this brilliant explanation by Bernie Sumption. For those of you who tend to skim past the links, let me tantalize you with the fact this article includes examples with both Star Wars and hippos.
It is often much more difficult to understand when to use inheritance than it might first appear. When I first replied to Dawid’s innocent question, I spouted the normal nonsense of:
“Use inheritance when you have a clear IS-A relationship and where you do not invalidate the Liskov Substitution Principle.” The problem is that even when you think you have a clear cut IS-A relationship, it might still be better to implement it using something like the strategy pattern.
The classic example of an IS-A relationship is the decomposition of employees. One could argue that an employee is a person and workers and managers are employees. But you could also look at it another way. A person has a job. The job has a role. What makes someone a manager is the kind of role they have. Should we use inheritance for the role, with worker and manager roles? Or should the role have worker and management responsibilities?
At some point you start to think that you can replace all inheritance in a system with composition. Since you have more flexibility with composition it seems to make sense to always use composition. Are there times when you should use inheritance?
I think the answer is “yes” and to see why it is instructive to look at the Composite Pattern. In this pattern, everything is a component. There are two kinds of components: a leaf component, and a composite. The composite is made up of components. Component is the base class, while leaf and composite are the child classes.
The important thing to consider is that the composite pattern does not use the base class in order to share code. Instead it is there to share the interface. Presumably, we have a tree structure and we want to iterate over it using the interface on component. Other design patterns such as the Bridge Pattern use inheritance in the same way.
When you have a clear IS-A relationship and you want to use all the objects through the base class’s interface, then inheritance can be a good way to go. It is certainly not a tool that I would throw out of the tool chest lightly. However, if you want to deal with the derived classes through their own interfaces, it often makes much more sense to use composition.