Patrones de diseño en TypeScript: de lo simple a lo complejo
Los patrones de diseño (design patterns) son soluciones probadas a lo largo del tiempo para problemas típicos en el desarrollo de software. En el contexto de TypeScript, que añade a JavaScript un tipado estricto y capacidades de POO, los patrones se vuelven especialmente efectivos. TypeScript permite implementar abstracciones, interfaces y polimorfismo a un nuevo nivel, haciendo que el código sea más predecible y seguro.
En este artículo analizaremos los patrones de diseño clave, su implementación en TypeScript y ejemplos prácticos de uso. El material será útil tanto para desarrolladores principiantes como para profesionales experimentados que deseen sistematizar sus conocimientos.
Patrones creacionales: Singleton y Factory Method
Los patrones creacionales se encargan de la creación de objetos, ocultando la lógica de instanciación. Consideremos dos de los más populares: Singleton y Factory Method.
Singleton (Instancia única)
Singleton garantiza que una clase tenga solo una instancia y proporciona un punto de acceso global a ella. En TypeScript, la implementación se vuelve especialmente elegante gracias a los miembros estáticos y los constructores privados.
class DatabaseConnection { private static instance: DatabaseConnection; private connectionString: string;
private constructor(connectionString: string) { this.connectionString = connectionString; console.log(`Conexión creada con: ${connectionString}`); }
static getInstance(connectionString: string): DatabaseConnection { if (!DatabaseConnection.instance) { DatabaseConnection.instance = new DatabaseConnection(connectionString); } return DatabaseConnection.instance; }
query(sql: string): void { console.log(`Ejecutando consulta: ${sql} en ${this.connectionString}`); }}
// Usoconst db1 = DatabaseConnection.getInstance('postgres://localhost:5432/mydb');const db2 = DatabaseConnection.getInstance('postgres://localhost:5432/mydb');console.log(db1 === db2); // true - el mismo objetoEste patrón es ideal para gestionar recursos: pools de conexiones, registro de logs, almacenamiento en caché. Sin embargo, tenga cuidado: Singleton puede complicar las pruebas debido al estado global.
Factory Method (Método de fábrica)
Factory Method define una interfaz para crear un objeto, pero permite que las subclases alteren el tipo de objeto que se crea. En TypeScript, esto se implementa mediante clases abstractas o interfaces.
interface Notification { send(message: string): void;}
class EmailNotification implements Notification { send(message: string): void { console.log(`Enviando email: ${message}`); }}
class SMSNotification implements Notification { send(message: string): void { console.log(`Enviando SMS: ${message}`); }}
abstract class NotificationFactory { abstract createNotification(): Notification;
notify(message: string): void { const notification = this.createNotification(); notification.send(message); }}
class EmailFactory extends NotificationFactory { createNotification(): Notification { return new EmailNotification(); }}
class SMSFactory extends NotificationFactory { createNotification(): Notification { return new SMSNotification(); }}
// Usoconst factory: NotificationFactory = new EmailFactory();factory.notify('¡Su pedido ha sido confirmado!');Factory Method ayuda a cumplir el principio de abierto/cerrado (OCP): puede agregar nuevos tipos de notificaciones sin modificar el código existente.
Patrones estructurales: Adapter y Decorator
Los patrones estructurales ayudan a organizar las relaciones entre objetos y clases, haciendo que el sistema sea más flexible y extensible.