Inheritance Should Not Be Preferred as a Means to Extend Code

Inheritance tends to create inflexible codebases and is often misused

Bad example of inheritance
Image by Giedrius Kristinaitis

Inheritance is a powerful tool in the toolbox. It allows us to form hierarchies of related objects that share common functionality, and the best part is that parent types don’t have to know anything about the subtypes, allowing us to create countless variations of something.

It sounds very nice in theory, however, in practice, knowing when to use inheritance is a bit tricky, and inheritance is often used to extend functionality when it shouldn’t be.

Also, the image will make more sense when you read the second to last paragraph.

Everything’s Protected

Inheritance-polluted codebases use the protected access modifier excessively. After all, inheritance becomes problematic if everything’s private by default because you can’t access private parent class members in child classes.

The classic argument for using the protected access modifier everywhere by default goes like this: “you never know what code you’ll need to modify, you don’t know how you’ll need to modify it, therefore, you should use the protected access modifier, which will allow you to change anything in your code in the future“.

It’s true, you really don’t know what you’ll need to extend, it’s just that the whole protected access modifier part is not right.

Inheritance Is Used as the Preferred Way of Extending Functionality

Using the protected access modifier everywhere by default is a bad idea. The argument for it has a big assumption built into it. The assumption is that inheritance is the only and/or preferred way of extending functionality. This assumption prevents your code from living a long and healthy life.

So what’s the problem with inheritance as a way of extending code? It sounds nice in theory. However…

In practice, extending functionality by using inheritance tends to work only once or just a couple of times for a given piece of code.

The more time goes by, the harder it becomes to do any further extensions.

Inheritance limits extension

Let’s say you decide to extend a class with a bunch of protected instance variables and methods, and let’s say that class renders HTML content on the screen.

You need to create 2 variations of the class: one with debugging functionality that displays layout boundaries, margins, and paddings, and another one that creates render logs with the HTML content.

You won’t squeeze all the functionality into a single class if you have at least a tiny bit of respect for code design, because, the fact is that the rendering functionality doesn’t need debugging or logging in order to work, and putting everything into a single class would create a god object with a bunch of conditionals to control what features should (not) be used.

So, let’s say you go with the second option, which is to create 2 child classes, one with debugging, and another one with logging.

Now let’s say you have one place in code that only needs the debugging feature and another place that needs both debugging and logging features.

What do you do? You need to provide a class of the HTML renderer type, however, the features are in two separate child classes, and you can only use one. There’s nothing you can do, you can’t use both at the same time.

This is how extending functionality by using inheritance becomes very limiting and leads to situations with no way out.

Life is likely to become hard when you have 2 or more features in child classes, because it’s only a matter of time until you’ll have to use combinations of those features.

More Arguments for the Overuse of Inheritance and Protected Members

It won’t happen

There’s another argument for using inheritance that goes like “come on, in reality, most of the time, you don’t have multiple variations of the parent class, you only extend it once, so it’s fine to use inheritance”.

The argument has a flaw because it assumes that you know for sure that you won’t need to extend functionality anymore in the future.

It contradicts the first argument for using inheritance to extend functionality, which is that you don’t know what you’ll need to extend, therefore, it’s an invalid argument.

The open-closed principle

Then there’s yet another argument for using protected instance variables and methods and using inheritance to extend functionality: “if you make instance variables private, it goes against the open-closed principle“.

It doesn’t if you do it right. The open-closed principle doesn’t mean you should use the protected access modifier everywhere, it‘s not about access modifiers, because if it was, you couldn’t apply it in languages that don’t support access modifiers or inheritance.

Composition Over Inheritance

Using inheritance to extend functionality is a band-aid solution to design problems. Instead, composition should be the preferred way of extending functionality.

What is composition? It’s a way of composing functionality by using other objects that implement pieces of functionality separately.

Example revisited

Let’s come back to the HTML renderer example. How could you solve the problem with composition? Well, there are a lot of ways you could do it with composition. You could have the rendering part be separate from HTML element parsing.

The HTML renderer could be made to not care what and how is done to render an element on the screen, and could accept multiple such renderers and render elements with the provided smaller classes. One rendering class could be the real renderer, and another one the debug renderer, you could inject them both by themselves or in a form of a composite into the HTML renderer class and use them both at the same time.

As for the logging part, it really shouldn’t be a part of the HTML renderer, but if you’re in a situation where it must be a part of it, then you could do a similar thing as with the rendering.

When you use composition you have a lot more options to choose from to implement something and a lot more freedom to extend your implementation later.

Is-a Relationships

Inheritance makes little sense when your code design is good, except in cases where you have a true is-a relationship. is-a relationships in OOP are often defined like this: “is-a relationship exists when there’s a subtype of another type“. In other words, in the context of inheritance, when a class extends another class.

I think it’s a bad way to think about is-a relationships (unless you’re working with programming language theory or some functional concepts).

I like to think about it a bit differently. I think of the underlying concepts that types/classes are trying to represent. If the concepts are not related in an is-a way, then it doesn’t matter if you use inheritance or not, it still doesn’t turn it into an is-a relationship.

For example, if you extend an HTML renderer class to log each render, that doesn’t create an is-a relationship, because if you look at the concepts, logging is not rendering, even though in the class hierarchy logging class extends the rendering class and it looks like an is-a relationship semantically.

The takeaway is that you should prefer composition over inheritance when it comes to extending functionality, because inheritance is very limiting unless you’re dealing with an actual is-a relationship.

Inheritance-polluted codebase that overuses the protected access modifier is inflexible and should be a cause of concern.

Forget the classic class Dog extends Animal example that only has a method to print the sound the animal makes. The real world is more complicated than that.

Also, don’t take the “OOP is supposed to model the real world” statement too literally (it’s debatable how true that statement actually is (I think it’s a bit misleading), but I won’t get into the OOP vs <insert your favorite paradigm here> hole right now (just for now)), it could make you overuse inheritance.

Level Up Coding

Thanks for being a part of our community! Before you go:

🚀👉 Join the Level Up talent collective and find an amazing job


Inheritance Should Not Be Preferred as a Means to Extend Code was originally published in Level Up Coding on Medium, where people are continuing the conversation by highlighting and responding to this story.


This content originally appeared on Level Up Coding - Medium and was authored by Giedrius Kristinaitis

Inheritance tends to create inflexible codebases and is often misused

Bad example of inheritance
Image by Giedrius Kristinaitis

Inheritance is a powerful tool in the toolbox. It allows us to form hierarchies of related objects that share common functionality, and the best part is that parent types don’t have to know anything about the subtypes, allowing us to create countless variations of something.

It sounds very nice in theory, however, in practice, knowing when to use inheritance is a bit tricky, and inheritance is often used to extend functionality when it shouldn’t be.

Also, the image will make more sense when you read the second to last paragraph.

Everything’s Protected

Inheritance-polluted codebases use the protected access modifier excessively. After all, inheritance becomes problematic if everything’s private by default because you can’t access private parent class members in child classes.

The classic argument for using the protected access modifier everywhere by default goes like this: "you never know what code you'll need to modify, you don't know how you'll need to modify it, therefore, you should use the protected access modifier, which will allow you to change anything in your code in the future".

It's true, you really don't know what you'll need to extend, it's just that the whole protected access modifier part is not right.

Inheritance Is Used as the Preferred Way of Extending Functionality

Using the protected access modifier everywhere by default is a bad idea. The argument for it has a big assumption built into it. The assumption is that inheritance is the only and/or preferred way of extending functionality. This assumption prevents your code from living a long and healthy life.

So what’s the problem with inheritance as a way of extending code? It sounds nice in theory. However…

In practice, extending functionality by using inheritance tends to work only once or just a couple of times for a given piece of code.

The more time goes by, the harder it becomes to do any further extensions.

Inheritance limits extension

Let’s say you decide to extend a class with a bunch of protected instance variables and methods, and let's say that class renders HTML content on the screen.

You need to create 2 variations of the class: one with debugging functionality that displays layout boundaries, margins, and paddings, and another one that creates render logs with the HTML content.

You won’t squeeze all the functionality into a single class if you have at least a tiny bit of respect for code design, because, the fact is that the rendering functionality doesn’t need debugging or logging in order to work, and putting everything into a single class would create a god object with a bunch of conditionals to control what features should (not) be used.

So, let’s say you go with the second option, which is to create 2 child classes, one with debugging, and another one with logging.

Now let’s say you have one place in code that only needs the debugging feature and another place that needs both debugging and logging features.

What do you do? You need to provide a class of the HTML renderer type, however, the features are in two separate child classes, and you can only use one. There’s nothing you can do, you can’t use both at the same time.

This is how extending functionality by using inheritance becomes very limiting and leads to situations with no way out.

Life is likely to become hard when you have 2 or more features in child classes, because it’s only a matter of time until you’ll have to use combinations of those features.

More Arguments for the Overuse of Inheritance and Protected Members

It won’t happen

There’s another argument for using inheritance that goes like “come on, in reality, most of the time, you don’t have multiple variations of the parent class, you only extend it once, so it’s fine to use inheritance”.

The argument has a flaw because it assumes that you know for sure that you won’t need to extend functionality anymore in the future.

It contradicts the first argument for using inheritance to extend functionality, which is that you don’t know what you’ll need to extend, therefore, it’s an invalid argument.

The open-closed principle

Then there’s yet another argument for using protected instance variables and methods and using inheritance to extend functionality: "if you make instance variables private, it goes against the open-closed principle".

It doesn't if you do it right. The open-closed principle doesn't mean you should use the protected access modifier everywhere, it‘s not about access modifiers, because if it was, you couldn't apply it in languages that don't support access modifiers or inheritance.

Composition Over Inheritance

Using inheritance to extend functionality is a band-aid solution to design problems. Instead, composition should be the preferred way of extending functionality.

What is composition? It’s a way of composing functionality by using other objects that implement pieces of functionality separately.

Example revisited

Let’s come back to the HTML renderer example. How could you solve the problem with composition? Well, there are a lot of ways you could do it with composition. You could have the rendering part be separate from HTML element parsing.

The HTML renderer could be made to not care what and how is done to render an element on the screen, and could accept multiple such renderers and render elements with the provided smaller classes. One rendering class could be the real renderer, and another one the debug renderer, you could inject them both by themselves or in a form of a composite into the HTML renderer class and use them both at the same time.

As for the logging part, it really shouldn’t be a part of the HTML renderer, but if you’re in a situation where it must be a part of it, then you could do a similar thing as with the rendering.

When you use composition you have a lot more options to choose from to implement something and a lot more freedom to extend your implementation later.

Is-a Relationships

Inheritance makes little sense when your code design is good, except in cases where you have a true is-a relationship. is-a relationships in OOP are often defined like this: "is-a relationship exists when there's a subtype of another type". In other words, in the context of inheritance, when a class extends another class.

I think it's a bad way to think about is-a relationships (unless you're working with programming language theory or some functional concepts).

I like to think about it a bit differently. I think of the underlying concepts that types/classes are trying to represent. If the concepts are not related in an is-a way, then it doesn’t matter if you use inheritance or not, it still doesn't turn it into an is-a relationship.

For example, if you extend an HTML renderer class to log each render, that doesn't create an is-a relationship, because if you look at the concepts, logging is not rendering, even though in the class hierarchy logging class extends the rendering class and it looks like an is-a relationship semantically.

The takeaway is that you should prefer composition over inheritance when it comes to extending functionality, because inheritance is very limiting unless you’re dealing with an actual is-a relationship.

Inheritance-polluted codebase that overuses the protected access modifier is inflexible and should be a cause of concern.

Forget the classic class Dog extends Animal example that only has a method to print the sound the animal makes. The real world is more complicated than that.

Also, don’t take the “OOP is supposed to model the real world” statement too literally (it’s debatable how true that statement actually is (I think it’s a bit misleading), but I won’t get into the OOP vs <insert your favorite paradigm here> hole right now (just for now)), it could make you overuse inheritance.

Level Up Coding

Thanks for being a part of our community! Before you go:

🚀👉 Join the Level Up talent collective and find an amazing job


Inheritance Should Not Be Preferred as a Means to Extend Code was originally published in Level Up Coding on Medium, where people are continuing the conversation by highlighting and responding to this story.


This content originally appeared on Level Up Coding - Medium and was authored by Giedrius Kristinaitis


Print Share Comment Cite Upload Translate Updates
APA

Giedrius Kristinaitis | Sciencx (2022-11-28T01:10:41+00:00) Inheritance Should Not Be Preferred as a Means to Extend Code. Retrieved from https://www.scien.cx/2022/11/28/inheritance-should-not-be-preferred-as-a-means-to-extend-code/

MLA
" » Inheritance Should Not Be Preferred as a Means to Extend Code." Giedrius Kristinaitis | Sciencx - Monday November 28, 2022, https://www.scien.cx/2022/11/28/inheritance-should-not-be-preferred-as-a-means-to-extend-code/
HARVARD
Giedrius Kristinaitis | Sciencx Monday November 28, 2022 » Inheritance Should Not Be Preferred as a Means to Extend Code., viewed ,<https://www.scien.cx/2022/11/28/inheritance-should-not-be-preferred-as-a-means-to-extend-code/>
VANCOUVER
Giedrius Kristinaitis | Sciencx - » Inheritance Should Not Be Preferred as a Means to Extend Code. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2022/11/28/inheritance-should-not-be-preferred-as-a-means-to-extend-code/
CHICAGO
" » Inheritance Should Not Be Preferred as a Means to Extend Code." Giedrius Kristinaitis | Sciencx - Accessed . https://www.scien.cx/2022/11/28/inheritance-should-not-be-preferred-as-a-means-to-extend-code/
IEEE
" » Inheritance Should Not Be Preferred as a Means to Extend Code." Giedrius Kristinaitis | Sciencx [Online]. Available: https://www.scien.cx/2022/11/28/inheritance-should-not-be-preferred-as-a-means-to-extend-code/. [Accessed: ]
rf:citation
» Inheritance Should Not Be Preferred as a Means to Extend Code | Giedrius Kristinaitis | Sciencx | https://www.scien.cx/2022/11/28/inheritance-should-not-be-preferred-as-a-means-to-extend-code/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.