December 20, 2025 (9mo ago) — last updated August 3, 2026 (1mo ago)

Patrones OOP en TypeScript: guía práctica

Aprende patrones creacionales, estructurales y de comportamiento en TypeScript con ejemplos prácticos para mejorar diseño, pruebas y mantenibilidad del código.

← Back to blog
Cover Image for Patrones OOP en TypeScript: guía práctica

Los patrones de diseño OOP son plantillas probadas que resuelven problemas de arquitectura comunes. En esta guía encontrarás patrones creacionales, estructurales y de comportamiento con ejemplos prácticos en TypeScript para mejorar la mantenibilidad, facilitar las pruebas y potenciar la colaboración en tu equipo. Aplicar patrones desde etapas tempranas reduce la complejidad y el coste de mantenimiento: estudios estiman que el mantenimiento puede representar hasta el 60–80% del coste total del ciclo de vida del software5.

Patrones OOP en TypeScript: guía práctica

Domina los patrones de diseño OOP con esta guía. Aprende patrones creacionales, estructurales y de comportamiento con ejemplos claros en TypeScript para mejorar la mantenibilidad, facilitar las pruebas y potenciar la colaboración en tu equipo.

Introducción

Los patrones de diseño en la programación orientada a objetos son plantillas reutilizables y probadas para resolver retos de diseño recurrentes. No son fragmentos de código para copiar y pegar; son marcos conceptuales que te ayudan a estructurar clases y objetos para que tu código sea más fácil de mantener, extender y probar.

Aplicar patrones desde etapas tempranas reduce la complejidad y el coste de mantenimiento: estudios estiman que el mantenimiento puede representar hasta el 60–80% del coste total del ciclo de vida del software5. Familiarizarte con patrones creacionales, estructurales y de comportamiento te permite comunicar la intención de diseño de forma más eficiente con tu equipo y refactorizar código legado con seguridad.


¿Qué son los patrones de diseño en programación orientada a objetos?

Programar sin un patrón claro puede llevar a una base de código enmarañada que resulta difícil de escalar y mantener. Los patrones de diseño actúan como recetas: describen problemas repetitivos y ofrecen estructuras comprobadas para resolverlos.

Un boceto de un gorro de chef dejando caer letras doradas en forma en un libro de recetas abierto.

Un lenguaje compartido para desarrolladores

Un beneficio clave de los patrones es proporcionar un vocabulario compartido. Al decir “Factory” o “Singleton”, los desarrolladores experimentados entienden la intención y la estructura de alto nivel, lo que acelera la colaboración. El libro Design Patterns: Elements of Reusable Object-Oriented Software catalogó 23 patrones fundamentales que siguen siendo lectura esencial para practicantes de OOP1. Investigaciones académicas también formalizaron la reutilización de patrones en proyectos grandes2.

“Un patrón de diseño no es un diseño terminado que pueda transformarse directamente en código. Es una descripción o plantilla de cómo resolver un problema que puede usarse en muchas situaciones diferentes.”

Revisar principios básicos de OOP como polimorfismo y herencia te ayudará a aplicar patrones de forma efectiva. Para un repaso práctico, consulta nuestra guía comparando polimorfismo vs herencia.

Las tres categorías principales de patrones de diseño

El Gang of Four organizó los patrones en tres categorías: creacionales, estructurales y de comportamiento. Conocer estas categorías te ayuda a elegir rápidamente el enfoque apropiado.

Diagrama que ilustra patrones de diseño creacionales, estructurales y de comportamiento con ilustraciones simples de cajas.

Visión general de las categorías de patrones de diseño OOP

CategoríaPropósitoEjemplos
CreacionalesGestionar y abstraer la creación de objetosFactory, Builder, Singleton, Prototype
EstructuralesComponer clases y objetos en estructuras más grandes y flexiblesAdapter, Decorator, Facade, Composite
De comportamientoDefinir cómo los objetos interactúan y se comunicanObserver, Strategy, Command, Iterator

Patrones creacionales: especialistas en creación

Los patrones creacionales controlan cómo se crean los objetos para que el código cliente no esté fuertemente acoplado a clases concretas. Ocultan la lógica de creación, aumentan la flexibilidad y facilitan la gestión de instanciación.

Singleton: asegurando una única instancia

Singleton garantiza que una clase tenga solo una instancia y proporciona un punto de acceso global. Es útil para recursos compartidos como una conexión a base de datos o un logger, pero puede introducir estado global que complica las pruebas y la gestión de dependencias3.

Ejemplo: un Singleton en TypeScript para una conexión a base de datos.

class DatabaseConnection {
  private static instance: DatabaseConnection;

  private constructor() {
    // Evita llamadas externas a 'new'
    console.log("Connecting to the database...");
  }

  public static getInstance(): DatabaseConnection {
    if (!DatabaseConnection.instance) {
      DatabaseConnection.instance = new DatabaseConnection();
    }
    return DatabaseConnection.instance;
  }

  public query(sql: string): void {
    console.log(`Executing query: ${sql}`);
  }
}

// Uso
const db1 = DatabaseConnection.getInstance();
const db2 = DatabaseConnection.getInstance();

db1.query("SELECT * FROM users");
console.log(db1 === db2); // true

Compensación del Singleton: usa Singletons para recursos verdaderamente globales y prefiere la inyección de dependencias para mejorar la capacidad de prueba y la modularidad3.

Factory Method: que las subclases decidan qué crear

Factory Method define una interfaz para crear un objeto, mientras que las subclases deciden qué producto concreto instanciar. Desacopla el código cliente de las clases concretas y facilita la extensión.

Ejemplo: renderizar botones específicos del sistema operativo en TypeScript.

interface Button {
  render(): void;
  onClick(f: () => void): void;
}

class WindowsButton implements Button {
  render() { console.log("Rendering a button in Windows style."); }
  onClick(f: () => void) { console.log("Windows button click event."); f(); }
}

class MacButton implements Button {
  render() { console.log("Rendering a button in macOS style."); }
  onClick(f: () => void) { console.log("Mac button click event."); f(); }
}

abstract class Dialog {
  abstract createButton(): Button;

  render() {
    const okButton = this.createButton();
    okButton.render();
  }
}

class WindowsDialog extends Dialog {
  createButton(): Button { return new WindowsButton(); }
}

class MacDialog extends Dialog {
  createButton(): Button { return new MacButton(); }
}

// Cliente
const os: string = "windows";
let dialog: Dialog;
if (os === "windows") dialog = new WindowsDialog(); else dialog = new MacDialog();
dialog.render();

Factory Method mantiene al creador sin conocimiento de los productos concretos, facilitando añadir nuevos diálogos como LinuxDialog.

Patrones estructurales: el pegamento arquitectónico

Los patrones estructurales ayudan a componer objetos en sistemas robustos y adaptables. Simplifican relaciones entre componentes para que puedas cambiar partes sin romper el conjunto.

Ilustraciones hechas a mano de un adaptador eléctrico y una pila de anillos que representan patrones de diseño OOP.

Adapter: puenteando interfaces incompatibles

Adapter envuelve una interfaz incompatible para que se ajuste a lo que tu sistema espera. Evita cambios invasivos en código legado.

Ejemplo: adaptar un ModernLogger a una interfaz ILogger existente.

class ModernLogger {
  public logInfo(message: string): void {
    console.log(`[INFO]: ${message}`);
  }
}

interface ILogger { log(message: string): void; }

class LoggerAdapter implements ILogger {
  private modernLogger: ModernLogger;
  constructor() { this.modernLogger = new ModernLogger(); }
  public log(message: string): void { this.modernLogger.logInfo(message); }
}

const logger: ILogger = new LoggerAdapter();
logger.log("User logged in successfully.");

Decorator: añadir funcionalidad dinámicamente

Decorator permite añadir responsabilidades a los objetos en tiempo de ejecución envolviéndolos. Es más flexible que la subclasificación y respeta el Principio de Responsabilidad Única.

Ejemplo: componer una suscripción con complementos opcionales.

interface Subscription { getDescription(): string; getCost(): number; }

class BasicSubscription implements Subscription {
  getDescription(): string { return "Basic Plan"; }
  getCost(): number { return 10; }
}

abstract class SubscriptionDecorator implements Subscription {
  protected subscription: Subscription;
  constructor(subscription: Subscription) { this.subscription = subscription; }
  abstract getDescription(): string;
  abstract getCost(): number;
}

class PremiumSupportDecorator extends SubscriptionDecorator {
  getDescription(): string { return `${this.subscription.getDescription()}, Premium Support`; }
  getCost(): number { return this.subscription.getCost() + 5; }
}

class CloudStorageDecorator extends SubscriptionDecorator {
  getDescription(): string { return `${this.subscription.getDescription()}, 1TB Cloud Storage`; }
  getCost(): number { return this.subscription.getCost() + 7; }
}

let mySubscription: Subscription = new BasicSubscription();
mySubscription = new PremiumSupportDecorator(mySubscription);
mySubscription = new CloudStorageDecorator(mySubscription);
console.log(mySubscription.getDescription());
console.log(mySubscription.getCost());

Este enfoque composicional mantiene las funcionalidades modulares y fáciles de probar.

Patrones de comportamiento: organizar la comunicación

Los patrones de comportamiento gobiernan la interacción y la responsabilidad de los objetos. Crean canales de comunicación claros: Observer para notificaciones dirigidas por eventos y Strategy para intercambiar algoritmos sin tocar a los clientes.

Observer: notificar a las partes interesadas

Observer crea una relación uno‑a‑muchos para que, cuando un Sujeto cambia de estado, los Observadores sean notificados automáticamente. Es ideal para sistemas dirigidos por eventos.

Ejemplo: un servicio simple de notificaciones.

interface Subject { attach(observer: Observer): void; detach(observer: Observer): void; notify(): void; }
interface Observer { update(subject: Subject): void; }

class NotificationService implements Subject {
  public state: string = '';
  private observers: Observer[] = [];
  attach(observer: Observer): void { this.observers.push(observer); }
  detach(observer: Observer): void { const i = this.observers.indexOf(observer); if (i !== -1) this.observers.splice(i, 1); }
  notify(): void { for (const o of this.observers) o.update(this); }
  public createNewPost(title: string): void { this.state = `New Post: ${title}`; console.log(`\nNotificationService: A new post was created.`); this.notify(); }
}

class EmailNotifier implements Observer { public update(subject: Subject): void { if (subject instanceof NotificationService) console.log(`EmailNotifier: Sending email about “${subject.state}”`); } }
class PushNotifier implements Observer { public update(subject: Subject): void { if (subject instanceof NotificationService) console.log(`PushNotifier: Sending push notification for “${subject.state}”`); } }

const notificationService = new NotificationService();
const emailer = new EmailNotifier();
const pusher = new PushNotifier();
notificationService.attach(emailer);
notificationService.attach(pusher);
notificationService.createNewPost("Understanding Observer Pattern");
notificationService.detach(pusher);
notificationService.createNewPost("Why Strategy is Awesome");

Observer produce un diseño de bajo acoplamiento: los Sujetos no necesitan conocer los Observadores concretos.

Strategy: encapsular algoritmos

Strategy permite intercambiar algoritmos en tiempo de ejecución y evita grandes bloques condicionales. Se alinea con el Principio Abierto/Cerrado al permitir nuevas estrategias sin modificar el código existente4.

Ejemplo: estrategias de pago para un carrito de compras.

interface PaymentStrategy { pay(amount: number): void; }
class CreditCardStrategy implements PaymentStrategy { pay(amount: number): void { console.log(`Paying $${amount} with Credit Card.`); } }
class PayPalStrategy implements PaymentStrategy { pay(amount: number): void { console.log(`Paying $${amount} via PayPal.`); } }

class ShoppingCart {
  private paymentStrategy: PaymentStrategy;
  constructor(strategy: PaymentStrategy) { this.paymentStrategy = strategy; }
  public setPaymentStrategy(strategy: PaymentStrategy) { this.paymentStrategy = strategy; }
  public checkout(amount: number): void { this.paymentStrategy.pay(amount); }
}

const cart = new ShoppingCart(new CreditCardStrategy());
cart.checkout(150);
cart.setPaymentStrategy(new PayPalStrategy());
cart.checkout(150);

Strategy reduce la complejidad condicional y facilita la extensibilidad.

Errores comunes de diseño y estrategias de refactorización

Elegir un patrón es solo la mitad de la batalla. Evita anti‑patrones como el God Object, que acapara responsabilidades y viola el Principio de Responsabilidad Única. Busca olores de código —métodos largos, dependencias excesivas o clases monstruo— y abórdalos con refactorización disciplinada.

Refactorizar significa mejorar la estructura interna sin cambiar el comportamiento externo. Así es como domas sistemas legados con seguridad.

Refactorizando hacia Strategy

Un gran switch o una larga cadena de if/else es un olor clásico. Refactoriza extrayendo el comportamiento variable a una interfaz Strategy con clases de estrategia concretas. Reemplaza el condicional por una única llamada al método de la estrategia seleccionada. Este cambio mejora la extensibilidad, la capacidad de prueba y la alineación con los principios SOLID4.

Preguntas frecuentes (respuestas rápidas)

¿Cuántos patrones debo aprender primero?

Concéntrate en 5–7 patrones esenciales: Singleton, Factory, Adapter, Decorator, Observer y Strategy. Aprende el problema que cada patrón resuelve; después será más fácil aprender otros.

¿Cuándo debo evitar usar un patrón?

Evita patrones que añadan complejidad sin resolver un problema real. No introduzcas un patrón “por si acaso”. Aplica YAGNI: añade patrones cuando exista una necesidad clara.

¿Puedo aplicar patrones en un estilo funcional?

Sí. Muchos patrones se mapean a técnicas funcionales. Strategy puede ser parámetros de función, y Decorator puede ser funciones de orden superior. Aplica el principio subyacente en lugar de copiar las formas OOP al pie de la letra.


En Clean Code Guy, ayudamos a equipos a construir software que perdure incorporando principios fundamentales en su flujo de trabajo. Descubre cómo nuestras auditorías de código y refactorización lista para IA pueden ayudar a tu equipo a entregar con confianza en https://cleancodeguy.com.

Resumen rápido: 3 preguntas clave

Q: ¿Qué problema resuelven los patrones de diseño?

A: Proporcionan soluciones comprobadas a problemas recurrentes de arquitectura y diseño, reducen el acoplamiento y mejoran la comunicación entre desarrolladores.

Q: ¿Cuál es el primer patrón que debo aplicar en un proyecto nuevo?

A: Comienza con Factory para desacoplar la creación de objetos y Strategy para evitar condicionales complejos.

Q: ¿Cómo afectan los patrones a las pruebas unitarias?

A: Facilitan las pruebas al fomentar la separación de responsabilidades y permitir sustituir dependencias por mocks o dobles.

1.
Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides, Design Patterns: Elements of Reusable Object-Oriented Software (Addison‑Wesley, 1994). [https://en.wikipedia.org/wiki/Design_Patterns](https://en.wikipedia.org/wiki/Design_Patterns)
2.
W. G. Griswold et al., “A Formal Foundation for Design Pattern Abstraction and Reuse,” Proceedings of ECOOP ’93, University of California, San Diego. [https://cseweb.ucsd.edu/~wgg/CSE210/ecoop93-patterns.pdf](https://cseweb.ucsd.edu/~wgg/CSE210/ecoop93-patterns.pdf)
3.
Martin Fowler, “Singleton,” Bliki, discusión sobre compensaciones y pruebas. https://martinfowler.com/bliki/Singleton.html
4.
Robert C. Martin (Uncle Bob), “The SOLID Principles.” https://8thlight.com/blog/uncle-bob/2012/08/13/the-s-o-l-i-d-principles.html
5.
Información general sobre mantenimiento de software: estudios y resúmenes indican que las tareas de mantenimiento pueden representar entre el 60% y el 80% del coste total del ciclo de vida del software. https://en.wikipedia.org/wiki/Software_maintenance
← Back to blog
🙋🏻‍♂️

La IA escribe código.
Tú lo haces durar.

En la era de la aceleración de la IA, el código limpio no es solo una buena práctica — es la diferencia entre sistemas que escalan y bases de código que colapsan bajo su propio peso.