Composition over Inheritance — Detailed#
The inheritance trap#
classDiagram
class Animal {
+eat()
+sleep()
}
class Bird {
+fly()
+eat()
+sleep()
}
class Fish {
+swim()
+eat()
+sleep()
}
class Penguin {
+eat()
+sleep()
+swim()
+fly() -- throws UnsupportedOperationException
}
Animal <|-- Bird
Animal <|-- Fish
Bird <|-- Penguin
A Penguin is a Bird but can't fly. Either:
- override fly() to throw (Liskov-violating); or
- restructure the hierarchy (and rebuild your codebase).
The composition fix#
classDiagram
direction LR
class Animal {
-mover: MoveBehavior
+move()
}
class MoveBehavior {
<<interface>>
+move()
}
class Fly
class Swim
class Walk
MoveBehavior <|.. Fly
MoveBehavior <|.. Swim
MoveBehavior <|.. Walk
Animal o--> MoveBehavior
Each animal is composed of what it can do. A penguin gets Swim + Walk, no Fly. New behaviour = new class, no hierarchy edits.
Rules of thumb#
- "is-a" → inheritance (rare).
- "has-a" / "can do" → composition (default).
- If you're inheriting only to share code, prefer composition + delegation.
- If you're inheriting to be the parent type in the type system, inheritance is fine — but watch LSP.
Mixin / trait alternatives#
Languages like Scala (traits), Rust (impl blocks), TypeScript (intersection types), Python (mixins) let you mix in capabilities without single-line inheritance.
Where this shows up in this site#
- Chess engine —
Piecedoesn't subclassMoveStrategy; it has a move strategy. - Notification system —
Notificationdoesn't extendEmailDeliverer; it composes aChannel. - Logger framework —
Loggercomposes appenders + formatters instead of inheriting from them. - Vending machine / Hotel management — pricing, dispatch, and state machines composed in.
Glossary & fundamentals#
Concepts referenced in this design. Each row links to its canonical page; the tag column shows whether it is a high-level (HLD) or low-level (LLD) concept.
| Tag | Concept | What it is | Page |
|---|---|---|---|
HLD |
Observability | metrics, logs, traces, SLOs | observability |
LLD |
State machines | FSM, HSM, transitions, guards | state-machines |
LLD |
Async models | futures / async-await / coroutines / actors | async-models |
LLD |
OOP pillars | encapsulation, abstraction, inheritance, polymorphism | oop-pillars |
LLD |
SOLID principles | SRP / OCP / LSP / ISP / DIP | solid-principles |
LLD |
Behavioural patterns | Strategy, Observer, State, Command, Chain | behavioral-patterns |
LLD |
Composition over inheritance | prefer composition; small swappable parts | composition-over-inheritance |
LLD |
Error handling | exceptions vs Result, error boundaries | error-handling |