Inheritance vs Composition: Which is Better for Your JavaScript Project?
By: Fernando Doglio
Tags: Programming, 100daysofcode, Object Oriented, Technology, Software Development

Definitions

As I said, both of these concepts aren’t precisely new, but in my case, I learned programming back in the day thinking that if you needed to extend behavior you did it by sub-classing, and because of that, inheritance was my only tool when it came to extending and adding behavior to a class. Let’s start there.

Inheritance

Essentially, in an object oriented context, Inheritance is a technique we can use to create derived classes that borrow everything from their parents, meaning every public and protected property and method, leaving out private ones. This allows you to maintain a relation with the parent class that updates the moment its code changes, making it a better alternative than just copying its code and extending it manually.

The above TypeScript code shows the basics of inheritance. Both the Dog and Cat classes extend or inherit from FourLeggedAnimal , which means they take on all their properties and methods, we don’t have to re-define them unless, like in my example, we want to overwrite what they do. What inheritance allows us is to abstract common behavior a state (in other words, methods and properties) into a single place where we can pull from (the parent class).

Some programming languages allow for multiple inheritances, such as JAVA, which in turn, let you to do what we just did but from multiple sources.

Another big benefit from the inheritance, mainly on strongly typed languages such as JAVA, TypeScript and others, is that variables declared with the type of your parent class can hold objects from its child classes as well. In other words:

Following with the same class definitions from before, this example declares a list of element of type FourLeggedAnimals while in reality, it contains a Cat and a Dog . This is possible because both objects have the same parent class. the same goes for the fact that we’re able to call the speak method safely on line 6. Thanks to the fact that this method is already defined in the parent class, we know all of our objects will have it (we can’t know specifically the implementation that will be used unless we’ve seen the code, but we can be sure this line will not error out due to a missing method error).

What about composition?

Although as you’ll see, both tools can yield a very similar result, composition is a whole different beast from inheritance. Instead of sub-typing like we did before, composition allows us to model a has one relationship between objects.

This in turn, helps you to encapsulate state and behavior inside a component and then use that component from other classes, formally known as composite.

If we go back to our animals example, we can re-think our internal architecture and have something like this:

Essentially we now have three different components where we’ve encapsulated behavior in two of them (the Barker and the Meower components) and state on the AnimalProperties we’ve encapsulated our state. We no longer have a common class for Dog and Cat to inherit from.

The point of a component-based approach is that you can now easily maintain and modify the code for any of them without affecting the main classes or their code. These type of relationship is called loosely coupled.

Now, we can take this to the next level and by adding a simple interface, we can simplify our code even further.

Check that out, we’re back to having only one animal class that contains our main logic. And just to clarify, the interface only lets us generalize our object shape, it’s not implementing anything (interfaces don’t really implement code, only define their API). With it we can create a common “type” if you will that describes the shape of our object, which in turn lets us define a variable to carry any of them inside (see the actor property inside the Animal class).

How do we create either a Dog or a Cat then? Let me show you:

First things first: notice that the end result is the same. We’re able to create a generic list of animals, iterate over it and call the same method with 100% certainty that all objects will have it. Again, we don’t know how it will be implemented, because that’s where the components (in this case) come into play.

The main difference here is that instead of having two different classes to describe our animals, we only have one, which mind you, it so malleable that can be turned into any animal given the right set of components.

And if you haven’t noticed yet, although the same effect can be achieved by either inheritance or composition, the first one happens during compilation (or interpretation) time, however the second one happens during execution time. How is this important? It is actually very important, you see, given the right set of methods, you can turn a Cat into a Dog or even into a brand new animal during execution, something you couldn’t do with inheritance.

With the new setActor method, you can change the barker for a moewer at any given point and while that behavior might seem a bit odd, there could be use cases where this level of dynamism is perfect for your logic.

Which one is better then?

Now that we understand what they are and how can they be used, the truth is: they’re both great!

Sorry to disappoint but I truly think there are perfect use cases for each of them.

The case for Inheritance

Inheritance makes sense because we tend to relate OOP concepts to real-world objects and then we try to generalize their behavior by generalizing their nature.

In other words, we don’t think of a cat and a doc as having 4 legs and a set of organs that allow them to either bark or meow. We think of them as animals, which translates to inheritance.

And because of that, the ideal use case for going with inheritance is having 80% of your code being common between two or more classes and at the same time, having the specific code being very different. Not only that, but having the certainty that there is no case where you’d need to swap the specific code with each other. Then inheritance is definitely the way to go, with it you’ll have a simpler internal architecture and less code to think about.

The case for composition

As we’ve seen so far, composition is a very flexible tool. You definitely have to change your mindset a bit, especially if you, like me, have been taught all about inheritance in the past and how that is the only solution to code-reuse inside an OOP context.

However, now that you’ve seen the light (you’re welcome by the way), you know that’s not true. Not only that, you also know that the generic code can also be abstracted into different components, which in turn can be as complex as they need (as long as they keep their public interface the same) and that we can swap them during runtime, which is something that to me, is amazingly flexible.

The other great benefit I see is that while with inheritance, if you need to create a new specific class (like adding a Lion class now), you’d have to understand the code of the FourLeggedAnimal class to make sure you now what you’re getting from it. And this would be just so that you can implement a different version of the speak method. However, if you went with composition, all you’d have to do is create a new class implementing the new logic for the speak method, unaware of anything else, and that’s it.

Of course, withing the context of this example, the extra cognitive load of reading a very simple class might seem irrelevant, however, consider a real-world scenario where you’d have to go through hundreds of lines of code just to make sure you understand a base class. That’s definitely not ideal.