Before you start
This lesson teaches a habit that will matter more than any single design pattern: prefer composition over inheritance.
The mindset shift
When you first learn inheritance, everything looks like a tree. "Duck is a Bird. Fish is an Animal. Car is a Vehicle."
Then you meet a Fish that can't fly, a rubber duck that doesn't eat, or a self-driving Car with no driver. The tree starts growing exceptions. Every new type either creates a new branch or breaks an existing one.
Composition changes how you think:
- Instead of asking what IS this, ask what does it HAVE.
- A Duck HAS the ability to fly, swim, and eat.
- A rubber duck HAS the ability to eat that raises "not edible", plus the same fly and swim.
- A robot fish HAS the ability to swim but no ability to eat.
Each ability is its own small class. The main object holds a few of them and hands off the work.
Why the industry moved this way
Every serious book about OOP from the last 20+ years says the same thing:
- Design Patterns (Gang of Four, 1994) — italicized rule: "Favor object composition over class inheritance."
- Effective Java (Bloch) — Item 18: "Favor composition over inheritance."
- Modern languages like Go and Rust don't even have inheritance. They use composition + interfaces instead.
Not a coincidence. Composition scales better as codebases grow. Deep inheritance trees are one of the top three sources of "we can't change this without breaking everything" in real production code.
The interview payoff
In an interview, when the interviewer adds a new requirement ("what if some parking spots are electric?"), watch what happens:
- Inheritance-first person: needs to add a new subclass, maybe change the parent, hopes nothing else breaks.
- Composition-first person: adds a
charging_capabilityfield to the spot. Done.
The composition answer is faster, cleaner to read, and doesn't cause bugs in other classes. That's what interviewers really watch for.
About the exercise
Notifications are the textbook composition problem. Every real notification system (Twilio, AWS SNS, your company's) uses this shape: a small stable core, plug-in channel classes, no inheritance between channel types.
One of the tests adds a Slack channel with ZERO changes to existing code. That's the composition win, made concrete. Feel it in your fingers today; you'll reach for it in every design problem from here on.