Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / OOP

The Unwanted Inheritance

5.00/5 (8 votes)
27 Jul 2013CPOL4 min read 12.6K  
Inheritance and code reuse

Introduction

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?

Background

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:

  1. Forward: There is an existing abstract or concrete class and we want to extend or override its behavior.
  2. 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 AuthenticateUser? 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:

Image 1

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:

Image 2

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.

Happy coding!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)