Introducción: Por qué SOLID es importante en TypeScript
SOLID es un acrónimo de cinco principios fundamentales de diseño orientado a objetos, formulados por Robert Martin (Tío Bob). En el contexto de TypeScript, estos principios adquieren un valor especial gracias al sistema de tipos estricto y al soporte de paradigmas modernos de POO. La aplicación de SOLID hace que el código sea más flexible, comprobable y mantenible, lo cual es crítico para proyectos escalables.
En este artículo analizaremos cada principio SOLID con ejemplos prácticos en TypeScript, mostraremos antipatrones y sus implementaciones correctas. Aprenderá a escribir código que no se rompa al añadir nueva funcionalidad.
1. Single Responsibility Principle (SRP) — Principio de responsabilidad única
Teoría
Cada clase debe tener una única razón para cambiar. Esto significa que la clase debe encargarse solo de una parte de la funcionalidad del programa. Si una clase realiza varias tareas, es más difícil de mantener y probar.
Antipatrón (violación de SRP)
class UserService { saveUser(user: User): void { // Guardar en BD console.log('User saved'); }
sendEmail(user: User, message: string): void { // Enviar email console.log(`Email sent to ${user.email}`); }
generateReport(user: User): string { // Generar informe return `Report for ${user.name}`; }}Implementación correcta (SRP)
class UserRepository { save(user: User): void { // Solo guardar }}
class EmailService { send(user: User, message: string): void { // Solo enviar email }}
class ReportGenerator { generate(user: User): string { // Solo generar informes }}2. Open-Closed Principle (OCP) — Principio de abierto/cerrado
Teoría
Las clases deben estar abiertas para extensión, pero cerradas para modificación. La nueva funcionalidad debe añadirse mediante herencia o composición, no modificando el código existente.
Antipatrón (violación de OCP)
class PaymentProcessor { processPayment(type: string, amount: number): void { if (type === 'card') { // Procesar tarjeta } else if (type === 'paypal') { // Procesar PayPal } else if (type === 'crypto') { // Procesar criptomoneda } // ¡Al añadir un nuevo tipo, hay que modificar la clase! }}Implementación correcta (OCP)
interface PaymentMethod { process(amount: number): void;}
class CardPayment implements PaymentMethod { process(amount: number): void { console.log(`Processing card payment: $${amount}`); }}
class PayPalPayment implements PaymentMethod { process(amount: number): void { console.log(`Processing PayPal payment: $${amount}`); }}
class PaymentProcessor { constructor(private paymentMethod: PaymentMethod) {}
process(amount: number): void { this.paymentMethod.process(amount); }}3. Liskov Substitution Principle (LSP) — Principio de sustitución de Barbara Liskov
Teoría
Los objetos de una clase base deben poder ser reemplazados por objetos de sus subclases sin alterar el funcionamiento del programa. Las subclases no deben cambiar el comportamiento esperado de la clase base.
Antipatrón (violación de LSP)
class Rectangle { constructor(public width: number, public height: number) {}
setWidth(width: number): void { this.width = width; }
setHeight(height: number): void { this.height = height; }
getArea(): number { return this.width * this.height; }}
class Square extends Rectangle {