Patrones de diseño en TypeScript: de la teoría a la práctica
Los patrones de diseño (Design Patterns) son soluciones probadas para problemas típicos en el desarrollo de software. En el contexto de TypeScript, con su potente sistema de tipos y soporte para POO, muchos patrones clásicos del libro "Gang of Four" (GoF) adquieren una implementación nueva y más elegante. En este artículo, analizaremos tres patrones clave con ejemplos reales en TypeScript que te ayudarán a escribir código más limpio, escalable y mantenible.
1. Patrón creacional: Singleton
Propósito: Garantiza que una clase tenga solo una instancia y proporciona un punto de acceso global a ella.
En TypeScript, la implementación de Singleton es especialmente conveniente gracias a las propiedades estáticas y los constructores privados. Este patrón es ideal para gestionar conexiones a bases de datos, loggers o cachés.
class DatabaseConnection { private static instance: DatabaseConnection; private connectionString: string;
private constructor(connectionString: string) { this.connectionString = connectionString; console.log(`Conexión creada a: ${connectionString}`); }
public static getInstance(connectionString?: string): DatabaseConnection { if (!DatabaseConnection.instance) { if (!connectionString) { throw new Error("Connection string required for first initialization"); } DatabaseConnection.instance = new DatabaseConnection(connectionString); } return DatabaseConnection.instance; }
public query(sql: string): void { console.log(`Ejecutando consulta: ${sql} en ${this.connectionString}`); }}
// Usoconst db1 = DatabaseConnection.getInstance("postgres://localhost:5432/mydb");const db2 = DatabaseConnection.getInstance();
console.log(db1 === db2); // true — el mismo objetodb1.query("SELECT * FROM users");Importante: En TypeScript se pueden usar readonly y genéricos para implementaciones más flexibles. Sin embargo, recuerda que Singleton puede complicar las pruebas — considera la inyección de dependencias (DI) como alternativa.
2. Patrón estructural: Decorator
Propósito: Añade dinámicamente nuevas responsabilidades a un objeto sin modificar su estructura. En TypeScript, los decoradores son una característica de primera clase del lenguaje (experimental en ES5, pero ampliamente utilizada en Angular y NestJS).
Veamos un ejemplo con un decorador para registrar llamadas a métodos:
// Interfaz objetivointerface ILogger { log(message: string): void;}
// Clase baseclass SimpleLogger implements ILogger { public log(message: string): void { console.log(`[LOG]: ${message}`); }}
// Decorador (clase envoltorio)class TimestampDecorator implements ILogger { private logger: ILogger;
constructor(logger: ILogger) { this.logger = logger; }
public log(message: string): void { const timestamp = new Date().toISOString(); this.logger.log(`[${timestamp}] ${message}`); }}
// Decorador para formateoclass UpperCaseDecorator implements ILogger { private logger: ILogger;
constructor(logger: ILogger) { this.logger = logger; }
public log(message: string): void { this.logger.log(message.toUpperCase()); }}
// Usolet logger: ILogger = new SimpleLogger();logger.log("Hola mundo"); // [LOG]: Hola mundo
logger = new TimestampDecorator(logger);logger = new UpperCaseDecorator(logger);logger.log("los decoradores funcionan"); // [LOG]: [2025-01-15T10:30:00.000Z] LOS DECORADORES FUNCIONANConsejo: En TypeScript también están disponibles los decoradores de clases y propiedades (sintaxis @decorator