Inyección de Dependencias en TypeScript: guía sencilla para principiantes

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción: ¿Qué es la Inyección de Dependencias y para qué sirve?

En el mundo del desarrollo de software, a menudo nos encontramos con situaciones donde una clase (o módulo) depende de otra. Por ejemplo, la clase UserService puede usar la clase DatabaseService para guardar datos. El enfoque tradicional es crear una instancia de DatabaseService directamente dentro de UserService. Esto genera un fuerte acoplamiento en el código, lo que dificulta su prueba, mantenimiento y extensión.

Inyección de Dependencias (DI) es un patrón de diseño que resuelve este problema. En lugar de crear las dependencias dentro de la clase, estas se "inyectan" desde el exterior. Esto implementa el principio de Inversión de Control (IoC): no es la clase la que gestiona sus dependencias, sino que el código externo (un contenedor o la parte que llama) las proporciona.

En este artículo, analizaremos cómo implementar la Inyección de Dependencias en TypeScript con ejemplos sencillos. Aprenderás por qué DI se considera una buena práctica y cómo ayuda a escribir código limpio y comprobable.



El problema: Acoplamiento rígido sin DI

Comencemos con un ejemplo que muestra cómo no se debe escribir código. Imagina que tenemos una aplicación que envía notificaciones a los usuarios.

// Sin Inyección de Dependenciasclass EmailService {    sendEmail(to: string, message: string): void {        console.log(`Enviando email a ${to}: ${message}`);        // ... lógica real de envío    }}

class NotificationService { private emailService: EmailService;

constructor() { // Problema: acoplamiento rígido this.emailService = new EmailService(); }

notifyUser(userEmail: string, message: string): void { this.emailService.sendEmail(userEmail, message); }}

// Usoconst notifier = new NotificationService();notifier.notifyUser('user@example.com', '¡Hola!');

En este código, NotificationService está fuertemente acoplado a EmailService. ¿Qué pasa si queremos enviar SMS o notificaciones push? Tendríamos que modificar el código de NotificationService. Además, probar NotificationService es difícil: no podemos reemplazar el EmailService real por un objeto mock.



Solución: Inyección de dependencias a través del constructor

La forma más simple y común de implementar DI es pasar las dependencias a través del constructor. Esto hace que el código sea flexible y fácil de probar.

Primero, definamos una interfaz para nuestro servicio de notificaciones. Este es un paso clave: programamos a nivel de abstracciones, no de implementaciones concretas.

// Definimos una interfaz para cualquier servicio de notificacionesinterface INotificationService {    send(to: string, message: string): void;}

// Implementación para Emailclass EmailService implements INotificationService { send(to: string, message: string): void { console.log(`[EMAIL] Enviado a ${to}: ${message}`); }}

// Implementación para SMSclass SMSService implements INotificationService { send(to: string, message: string): void { console.log(`[SMS] Enviado a ${to}: ${message}`); }}

// Clase que usa DIclass UserNotifier { private notifier: INotificationService;

// La dependencia se inyecta a través del constructor constructor(notifier: INotificationService) { this.notifier = notifier; }

notifyUser(userContact: string, message: string): void { this.notifier.send(userContact, message); }}

// Usoconst emailNotifier = new UserNotifier(new EmailService());emailNotifier.notifyUser('alice@example.com', '¡Tu pedido está listo!');

const smsNotifier = new UserNotifier(new SMSService());smsNotifier.notifyUser('+7-999-123-45-67', 'Código de verificación: 1234');

Ahora UserNotifier no sabe qué servicio específico

Blogs

Book Recommendations