Composition vs Inheritance
Intro to Composition Vs Inheritance
Both composition and inheritance are ways to reuse code, but they solve different
problems. Knowing when to use each is crucial for good design.
Inheritance (Is-A)
Inheritance creates an "is-a" relationship:
class Animal {
makeSound(): void {}
}
class Dog extends Animal {
// Dog IS-A Animal
}
Use inheritance when:
- You have a clear hierarchical relationship
- Subclasses are specializations of the parent
- You want to share implementation
Composition (Has-A)
Composition creates a "has-a" relationship:
class Engine {
start(): void {}
}
class Car {
#engine: Engine // Car HAS-A Engine
constructor(engine: Engine) {
this.#engine = engine
}
start(): void {
this.#engine.start()
}
}
Use composition when:
- You want flexibility to change behavior at runtime
- You need to combine multiple behaviors
- The relationship is "has-a" not "is-a"
Three Types of Composition
- Inheritance (is-a):
Dog extends Animal - Object Composition (has-a):
Carhas anEngine - Function Composition (transforms): combine functions into pipelines
// Object composition (OOP)
class EmailService {
#logger: Logger
constructor(logger: Logger) {
this.#logger = logger
}
send(email: Email) {
this.#logger.log('Sending...')
// ...
}
}
// Function composition (FP)
const sendEmail = (logger: Logger) => (email: Email) => {
logger.log('Sending...')
// ...
}
Both achieve the same goalβloose coupling and flexibilityβthrough different
mechanisms.
When to Use Each
Inheritance:
Dogis anAnimalβCircleis aShapeβManageris anEmployeeβ
Composition (object or function):
Carhas anEngineβComputerhas aCPUβUserhas aProfileβ- Data transformation pipelines β
Favor composition over inheritance when possible. Both object composition and
function composition are more flexible and easier to change than inheritance.
Use inheritance when you truly have an "is-a" relationship.
In this exercise, you'll choose between composition and inheritance.