Composition over Inheritance โ Prefer HAS-A over IS-A
Prefer HAS-A (inject a service) over IS-A when behaviour varies. ยท Deep trees are hard to change; composition stays modular.
Watch
Watch, then scroll down for code and practice.
In code
class EmailService {
send(to: string, body: string) { /* โฆ */ }
}
class OrderService {
constructor(private mail: EmailService) {}
placeOrder() {
// โฆ
this.mail.send("user@x.com", "Confirmed");
}
}๐ Key ideas
The problem with deep inheritance
Deep hierarchies (A extends B extends C) become rigid. A change to the base class ripples everywhere, and the Banana-Gorilla-Jungle problem kicks in.
Composition
Instead of extending a class to reuse behaviour, inject the dependency. Employee HAS-A PayrollService โ it doesn't need to BE one.
When inheritance is OK
One level is usually fine when there's a genuine IS-A relationship. But if you're using inheritance just to reuse code, reach for composition instead.
Interfaces + composition
Define capability via interfaces (Payable, Manageable) and inject implementations. This makes roles mix-and-matchable without class explosions.
๐ง Practice โ Apply What You Learned
๐ Now apply what you learned
Pick a problem above, write your solution, and get AI feedback on your design.
Start Practice โ