Logo

dev-resources.site

for different kinds of informations.

Inheritance vs composition: a fight against Egyptian gods

Published at
11/17/2023
Categories
oop
inheritance
composition
Author
shurna83
Categories
3 categories in total
oop
open
inheritance
open
composition
open
Author
8 person written this
shurna83
open
Inheritance vs composition: a fight against Egyptian gods

We all heard about the "favor composition over inheritance" mantra, but only a few have read Thoughtworks' article on the matter, by Steven Lowe. What I am going to do in this bite is to describe what would happen if this principle is ignored.

Unfortunately, I have seen this happening a lot of times, unveiling a common misunderstanding of how inheritance should be used.

Pyramids and gods

Everything starts with a conversation that goes like this:

Alice: "Hey, those two classes have something in common."

Bob: "Yeah, let's create a common base class to maximize code reuse!"

Do it once. Do it twice. Do it for a sprint. Do it for a whole quarter. Now each class exposes a few methods to subclasses in addition to every other method exposed by its superclasses. And there's a fairly good amount of classes. What was initially a simple hierarchy turned out to be a fearsome pyramid, staring grumpily at you as you try to understand what's the reason for that AbstractThingWithProxyWithoutUserManagerBase to exist.

Even worse, you may find that some subclasses just override superclass methods with empty implementations or use other tricks to subtract functionality from the base class.

And here begins the nightmare. You need that WithProxy thing, but you already subclassed AbstractSpaceRocketWithChewingGumsInThePocketHandler and that WithProxy is just too far away in the pyramid. You hear the Sphynx laughing at you: "You'll never solve this riddle!". You are tempted to refactor only a bit of that pyramid, but you're unsure where and how to start. This is Seth threatening you: "Don't you dare! Hail the holy pyramid or everything will crawl down!"

"I need to deliver the feature!", you cry. "My customer agreed on a deadline!". Of course a tight deadline. So you brace yourself, dry the tears from your eyes and start massaging the pyramid to spot a hole where you can pinpoint your new class. You find it! Feature done! The customer will be happy! You lie to yourself: "I paid attention, I know I did my best! This will work!". But you don't really know what you just did and only guessed and bet for your solution to be right. Sphynx is still laughing, and Seth hails the now bigger pyramid.

The ancient Egyptian gods won once again.

A pragmatic short-term solution

We need to visualize our goals:

  1. We need to deliver a feature now

  2. We need to be able to deliver more features in the future

To reach goal 1, we need to pave the way to reach goal 2 and take a first step toward it. This is because if I want to deliver a feature in the future, then the code around will hopefully be good enough to let me do it easily. So, if I want to deliver a feature now when the code is not good enough, I have to accommodate it to be good enough for the feature to be implemented properly.

In our short example, we could extract that WithProxy in a utility class and use it wherever is needed, breaking the verticality of inheritance and favoring the horizontality of composition.

In the long run, the pyramid will get shorter and our Egyptian gods will stop laughing at us.

The ideal solution

The issue here is that inheritance was used as the default approach to code reuse, while we should carefully evaluate what to use case by case. When Alice and Bob started arguing to pull the code up the hierarchy, we should have replied: "Why don't we segregate the common code in a class and establish a dependency relationship instead?". This is the start of the composition approach.

The takeaway

Understanding the difference between is-a relationship versus has-a is crucial to make a proper decision and the Thoughtworks' article is a must-read on the topic.

Inheritance's main goal is to model domains, not to reuse code. Code reuse comes as a side effect. Always use composition if you just want to share some common code.

But if you want a rough rule of thumb just to start over, here is a decision flow in six steps:

  1. Read Thoughtworks' article

  2. Read Thoughtworks' article

  3. Did you read Thoughtworks' article?

  4. Do you want just to put some code in common? Go with composition. Common code is a dependency, a has-a relationship.

  5. Are you modeling entities for the same domain? Go with inheritance. Modeling entities falls inside the is-a hat (beware to not cross your domain boundaries and to not subtract functionalities from base classes. Subclasses must be functionality additives.)

  6. Something else? In doubt? Go with composition by default and move to inheritance only when you figure out you actually need inheritance.

inheritance Article's
30 articles in total
Favicon
Code Smell 286 - Overlapping Methods
Favicon
Understanding Traits in PHP and How They Differ from Inheritance
Favicon
Understanding Classes and Inheritance in JavaScript
Favicon
Mastering Generalization in OOP: Techniques and Examples
Favicon
Upcasting — Using a Superclass Reference for a Subclass Object
Favicon
Mapping inheritance hierarchies with MapStruct
Favicon
Python Inheritance Explained: Types, Examples, and Best Practices
Favicon
Mastering TypeScript: Understanding the Power of extends
Favicon
PHP: Herança vs. Composição
Favicon
Method Resolution Order in Python 3
Favicon
Why we don't use RemoteWebDriver driver = new ChromeDriver()
Favicon
Understanding JavaScript Inheritance: A Deep Dive into Prototypal and Constructor Patterns
Favicon
Understanding Inheritance and Polymorphism: Simplified OOP Concepts
Favicon
JS Inheritance - Part 2: Factory Functions vs. Classes
Favicon
Inheritance with access-specifier in cpp
Favicon
Inheritance in Dart
Favicon
RoR - extend, < (inheritance), include - know the difference
Favicon
Inheritance vs composition: a fight against Egyptian gods
Favicon
Object Oriented Concept - Inheritance
Favicon
Exploring Component Inheritance in Aventus
Favicon
Python Inheritance
Favicon
Inheritance vs Composition: Using a Role-Playing Game in JavaScript as an Example
Favicon
PostgreSQL - Partitioning & Inheritance
Favicon
Prototype and Prototypical Inheritance
Favicon
Ruby - Inheritance
Favicon
MultiLevel Inheritance in Java
Favicon
Overloading vs Overriding in Typescript
Favicon
Java Error - at least one public class is required in main file
Favicon
Polymorphism in Java.
Favicon
Method Overriding in Java.

Featured ones: