Code reuse is a topic that frequently comes up when speaking of Inheritance. Forums, blogs and wikis are home to many opinions and pseudo-definitions of inheritance that sound more or less like the following: “inheritance is a form of software reusability,” “the essence of inheritance is reusability,” “inheritance is the OOP way of reusing code,” etc.
Inheritance though is also about the good old “Is-a” relationship, and a legitimate question raises: Are base classes really the right way to reuse code?
When reviewing code, I can frequently spot misuses of inheritance authored by developers that are misguided by all these homemade definitions that proliferate on the web.
Let’s start from the beginning: there are usually two directions to apply inheritance:
- Forward: There is an existing abstract or concrete class and we want to extend or override its behavior.
- Backward: There are two or more existing concrete classes and we realize that we can extract a common base class out of them.
In my experience, the backward process is the tricky one and by far the most prone to abuses.
The problem starts when we look at some not so well designed classes - typically with many responsibilities - that just happen to have similar methods, if not identical.
For instance, let’s say that a certain class A and class B have both a generic method in common called
ReadTextFile that reads a text file into a
string (surprise, surprise).
Now, nobody likes to see or maintain several copies of the same functionality, especially if logically reusable. We want our beloved
ReadTextFile in one and only place, where it can be called by any class who needs it.
One place, alright, but where?
One way could be to put it in some kind of text file helper class, possibly in a utility library, and then inject it into whatever class needs to use it.
Unfortunately, many developers use inheritance to deal with these cases and create a base class for A and B to swipe in the common code.
When then a class C needs to read a text file, they have class C inheriting from the base class.
So, what is wrong with it? Well, many things actually.
One problem is that popular OO languages such as C# or Java do not support multiple inheritances. If class C already has its own base class, then we cannot reuse
ReadTextFile. But even if we are lucky and class C does not already have a base class, what happens if A and C have another method in common, let’s say
Do we also swipe this common method under the rug of the base class, even if B does not care about it? I have seen many developers doing that. Their sub classes may look clean and single responsibility compliant, but they are not. All you need to do is to look under the base class to find the hidden pile of bad practice: a blob of mixed concerns.
An inherited mess is still a mess, after all.
Here is how the class diagram would look like:
At this point, some developer realizes that it is not good when a class is forced to inherit a method that does not need, and starts building inheritance hierarchies like this one:
Let’s look at the diagram to check what was achieved.
The good part is that now B does not see
AuthenticateUser any longer.
The bad part is that now we have one more class and the inheritance depth has been increased. And what happens if a class D needs only
AuthenticateUser and not
ReadTextFile? My head is already spinning like Regan.
We relied on inheritance to reuse code and we ended up with a not so reusable and overcomplicated hierarchy.
But what would happen if we use an OO language that supports multiple inheritance?
Although technically we can now achieve reusability, it is still not a great idea.
In fact, if a class inherits multiple responsibilities than it has multiple responsibilities, hence it is harder to understand, maintain, debug and test. In our example, if for instance we want to change the system to use a different implementation of
AuthenticateUser in classes A, we would have to change the existing code. Had we relied on composition instead of inheritance, all we would do is to write a new authentication class with the same interface and just update the factory (or the IOC container registration). The important difference is that we would not have to change any existing business code.
The benefit of code reusability with inheritance is limited in scope and should be focalized on the specific responsibility and core business of each class. Certainly code reusability is not the primary driving reason to adopt inheritance over composition, and composition being a wiser choice in the majority of cases.