Dependency Inversion
The D in SOLID: high-level code should depend on abstractions (interfaces), not concrete details. Pass dependencies in instead of creating them inside, so they're easy to swap or fake in tests.
// Bad: locked to one email provider
class SignupService {
private mailer = new GmailMailer()
signup(email: string) { this.mailer.send(email, "Welcome") }
}
// Good: depends on an interface, passed in
interface Mailer { send(to: string, msg: string): void }
class SignupService {
constructor(private mailer: Mailer) {}
signup(email: string) { this.mailer.send(email, "Welcome") }
}
new SignupService(new GmailMailer())
new SignupService(new FakeMailer()) // in tests