Skip to main content

Composition Over Inheritence

Inheritence is often misused or overused or even forced for several reasons, and they are mostly due to education, misunderstanding or habit. Many developers who studied OOP are introduced with inheritence in early stage and then it stays with them as a default way how to build relationships between objects. Escaping this matrix can be quite challenging, as lacking alternative perspectives often leads to the trap of believing that inheritance is the solution to everything. Here, we will try to find the reasoning behind this way of thinking.

Historical Influence

Early OOP examples often revolved around "is-a" relationships like Car -> Vehicle, Dog -> Animal, reinforcing inheritance as the primary method of code reuse. We can see this in early OOP literature, such as the influential "Gang of Four Design Patterns",  where inheritance is prominently discussed. And then, as a result, developers internalized it as the default design approach.

Inheritance = Code Reuse (Misconception)

Many developers learn that inheritance is the primary way to reuse code. They might think: “If I need to reuse functionality, I must derive from a base class.” This mindset leads to deep hierarchies, tight coupling, even when simpler or more modular solutions (like composition) would work better.


Simplicity of Implementation

Inheritance is Easy to Implement - defining a base class and extending it is straightforward, both conceptually and in terms of syntax. Composition requires more design work, such as defining interfaces or injecting dependencies. Let's show the difference in one simple example:

Inheritence

public class Vehicle 
{ 
	public void Move() 
	{ 
		Console.WriteLine("Moving"); 
	} 
}
public class Car : Vehicle { }

Composition

public interface IMovable 
{ 
	void Move(); 
}

public class Car 
{ 
	private readonly IMovable _movable; 
	public Car(IMovable movable) 
	{ 
		_movable = movable; 
	} 
}

Legacy code

Older systems were built using inheritance-heavy designs, and developers working on these systems are forced to maintain and extend these hierarchies.

Frameworks that Encourage Inheritance

Many frameworks historically relied on inheritance (e.g., GUI frameworks like Swing or .NET's WinForms) where developers extended base classes to customize behavior.
Developers often mimic these patterns without questioning if they're the best solution.

How to Shift Away from Overusing Inheritance

Educate About Composition:

Teach patterns like Strategy, Decorator, and Dependency Injection as alternatives to deep hierarchies. Example: Instead of a Car base class, use a IMovable, IEngine interface and inject behavior dynamically.

Promote Design Principles:

Follow SOLID principles, especially: 
  • Single Responsibility: Avoid bloated base classes. 
  • Dependency Inversion: Prefer abstractions over specific implementations.

Use Interfaces and Delegation:

Example: Instead of class Car : Vehicle, use Car with a composable IEngine or IMovable.

Emphasize Refactoring:

Refactor overly complex hierarchies into flatter, modular designs using composition.

Conclusion

Inheritance is often "forced" because it's historically emphasized, easy to use, and aligns with developers' early understanding of OOP. However, it has significant limitations that make composition a more flexible, scalable, and maintainable choice in many scenarios. By learning and applying composition patterns, developers can avoid rigid hierarchies and design systems that are more robust and adaptable.

Comments

Popular posts from this blog

Design Patterns: Singleton

Tyipically the first design pattern most people learn, often wrongly ☺ To give an introduction, we can say that singleton is one of the creational design patterns which ensures only one class instance with single point of access thru entire application.  Because it is relatively simple to implement, the Singleton pattern is sometimes misapplied in situations where it is not the most suitable choice. When to use it? Here are the few examples of corrent usage of singleton: Configuration Management  Centralized configuration settings for consistent use thru entire application Caching Maintaning  Single istance of cached objects for easy and fast acces Logging  Ensure unified mechanism to avoid duplication of log files, formats, etc Global State Management  Centralized management of the state which is needed to be shared accross the application Resource sharing  Thread pools, database connection, I/O operations When not to use it? On the other hand, here are fe...

Design Patterns: Builder

This is also, like a Singleton , one of the creational design patterns. It provides the way of creating complex objects step by step by simple chaining and every particular step is independent of other steps. Let us dive into the real example of usage. For showing purpose we have created an example in C# which creates simple SQL queries using described pattern.  using System; using System.Text; namespace BuilderPatternExample { public interface ISqlQueryBuilder { ISqlQueryBuilder Select(string columns); ISqlQueryBuilder From(string table); ISqlQueryBuilder Where(string condition); ISqlQueryBuilder OrderBy(string columns); string Build(); } public class SelectQueryBuilder : ISqlQueryBuilder { private readonly StringBuilder _queryBuilder; public SelectQueryBuilder() { _queryBuilder = new StringBuilder(); } public ISqlQueryBuilder Select(string columns) { ...

Why Do Employers Lie In Interviews?

This is a very common subject that many of us have already experienced. But when you realize that half of what has been said at interviews is actually a lie, you are already at least six months in the company, you have already started some project and it wouldn’t be appropriate to leave the company at that moment. Why is this happening? First of all, let us see how the usual interview process looks like in software development companies. First round interview in most of these companies is an interview with HR. This is the first insight about the company. A person who works in HR is usually someone who, in most cases, doesn’t understand what the software is and how the software development process goes. Big respect to those companies where HR knows these things. This phase usually contains some standard questions about your personality, what do you like about the company, how this company is something that you are actually looking for, where you see yourself in five/ten  years etc… ...