¿Qué es?
El Dependency Inversion Principle dice que la lógica de negocio (alto nivel) no debería depender directamente de detalles de implementación concretos (bajo nivel), como una clase específica que envía emails o que habla con una base de datos. En vez de eso, ambos deberían depender de una abstracción (una interfaz), y la implementación concreta se "inyecta" desde afuera.
Antes: dependencia directa de una clase concreta
class EmailSender {
send(to: string, message: string): void {
console.log(`Email a ${to}: ${message}`);
}
}
// El módulo de alto nivel depende directamente del detalle de bajo nivel
class NotificationService {
private emailSender = new EmailSender();
notify(user: string, message: string): void {
this.emailSender.send(user, message);
}
}Si mañana hay que notificar por SMS o push en vez de (o además de) email, hay que modificar NotificationService directamente. Y para testear esta clase de forma aislada, es imposible reemplazar EmailSender por un mock sin tocar el código.
Después: se depende de una abstracción
interface MessageSender {
send(to: string, message: string): void;
}
class EmailSender implements MessageSender {
send(to: string, message: string): void {
console.log(`Email a ${to}: ${message}`);
}
}
class SmsSender implements MessageSender {
send(to: string, message: string): void {
console.log(`SMS a ${to}: ${message}`);
}
}
// El módulo de alto nivel solo conoce la abstracción
class NotificationService {
constructor(private sender: MessageSender) {}
notify(user: string, message: string): void {
this.sender.send(user, message);
}
}
const service = new NotificationService(new SmsSender());
service.notify('+54 9 11 1234-5678', 'Tu pedido fue enviado');NotificationService ya no sabe (ni le importa) si se manda un email o un SMS: recibe cualquier MessageSender por su constructor. Esto se llama inyección de dependencias, y también hace que sea trivial pasar un sender falso en los tests.