The container object holding references to components
Backend & Runtime / Knowledge library
Composition over Inheritance
Builds behavior from collaborating components instead of deep class hierarchies
Why learn Composition over Inheritance?
It enables runtime variation with fewer fragile base-class assumptions
The problem it solves
It avoids locking reusable behavior into rigid inheritance trees
Where it came from
Composition over Inheritance is a software design principle that builds behavior from collaborating components instead of deep class hierarchies. Its guarantees and tradeoffs depend on the concrete implementation and operating context.
Build your understanding
Work through these exercises in order. Try the task before opening a reference.
- Define Composition over Inheritance and distinguish it from a neighboring concept.
- Choose a realistic task where Composition over Inheritance is appropriate.
- Identify a failure mode caused by misusing Composition over Inheritance.
- Interpret a small design or result involving Composition over Inheritance.
- Explain one tradeoff or limitation of Composition over Inheritance.
Documentation and sources
- Software Engineering at Google (abseil.io)
Connect the ideas
Explore next
Real World Example
Modular Logistics Fleet Management
A regional logistics firm managing a diverse fleet of delivery vehicles including electric vans, heavy-duty trucks, and autonomous drones.
The business problem
The engineering team initially used a deep class hierarchy where every vehicle type inherited from a base Vehicle class. As requirements grew, adding a new feature like 'GPS Tracking' required modifying the base class, which inadvertently broke unrelated subclasses, leading to fragile code and high regression risks.
How this topic solves it
By shifting to composition, the team defines small, focused components like NavigationModule, BatteryMonitor, and CargoHandler. Instead of inheriting behavior, each vehicle instance is constructed by injecting these specific components, allowing for flexible configuration without rigid class trees.
- Decompose monolithic base classes into granular, independent behavior interfaces.
- Define concrete component classes that implement these interfaces.
- Create a vehicle container class that accepts a list of components via constructor injection.
- Assemble specific vehicle configurations by passing the required components at runtime.
This diagram shows how a Vehicle entity is composed of multiple independent behavior components.
Provides GPS and routing logic
Provides energy status logic
Provides weight and volume tracking
- Vehicle → has-a → NavigationModule
- Vehicle → has-a → BatteryMonitor
- Vehicle → has-a → CargoHandler
Expected business outcome
The codebase becomes significantly more maintainable, as adding new features involves creating new components rather than modifying existing inheritance chains, effectively isolating side effects.
Limitations and checks
A primary limitation is increased object complexity during initialization. Because behavior is injected, it is harder to verify the complete state of an object at a glance. Developers must use dependency injection containers or factory patterns to ensure required components are present, which can be verified by checking for null references in the vehicle constructor.