When to Use Composition vs Inheritance
When to Use
π¨βπΌ Let's build a notification system. We need to decide whether to use
inheritance or composition for different scenarios.
π¨ Open
and:
Inheritance (is-a)
- Create a
Loggerbase class withlog(message: string): voidthat prints exactlyLog: {message} - Create a
FileLoggerthatextends Loggerand overrideslogto print exactlyFile Log: {message} - Create a
ConsoleLoggerthatextends Loggerand overrideslogto print exactlyConsole Log: {message}
Composition (has-a)
- Create an
EmailServicethat has a logger (composition), not is a logger:- Accept a
Loggerin the constructor - Keep that logger as private class state (use
#) sendEmail(to: string, subject: string): voidmust produce the send-action messageSending email to ${to}: ${subject}through the injected logger and on the console (both)
- Accept a
- Export all four:
export { Logger, FileLogger, ConsoleLogger, EmailService }
Fixtures and success criteria
const fileLogger = new FileLogger()
const consoleLogger = new ConsoleLogger()
const emailService = new EmailService(fileLogger)
emailService.sendEmail('user@example.com', 'Welcome')
fileLogger instanceof LoggerandconsoleLogger instanceof LoggerfileLogger.log('hi')printsFile Log: hiconsoleLogger.log('hi')printsConsole Log: hinew Logger().log('hi')printsLog: hiEmailServicecan be constructed with either logger subclass- After
sendEmail('user@example.com', 'Welcome')with aFileLogger, console output includes both:File Log: Sending email to user@example.com: WelcomeSending email to user@example.com: Welcome
π° Think about the relationship: Is it "is-a" or "has-a"?