December 20, 2025 (4mo ago) — last updated January 11, 2026 (3mo ago)

Patrones de diseño OOP: guía práctica

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

← Back to blog
Cover Image for Patrones de diseño OOP: guía práctica

Los patrones de diseño OOP son plantillas reutilizables que te ayudan a resolver problemas arquitectónicos comunes. En esta guía encontrarás patrones creacionales, estructurales y de comportamiento con ejemplos prácticos en TypeScript para mejorar la mantenibilidad, las pruebas y la colaboración en tu equipo.

Guía práctica de patrones de diseño OOP

Domina los patrones de diseño OOP con nuestra guía. Aprende patrones creacionales, estructurales y de comportamiento con ejemplos de código claros y prácticos en TypeScript para elevar tus habilidades, mejorar la mantenibilidad y facilitar 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 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 costo de mantenimiento. El mantenimiento de software puede suponer una parte significativa del coste total del proyecto, por lo que diseñar con claridad y propósito es una inversión que paga a largo plazo5.

Familiarizarte con patrones comunes —creacionales, estructurales y de comportamiento— te permite comunicar la intención de diseño con tu equipo, refactorizar código legado con seguridad y elegir la solución adecuada para cada problema arquitectónico.

¿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 funcionan 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.

El lenguaje compartido de los 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.

“Design Patterns: Elements of Reusable Object-Oriented Software” catalogó 23 patrones fundamentales que siguen siendo lectura esencial para practicantes de OOP1. Estudios académicos previos también han formalizado la reutilización de patrones y sus beneficios 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 ayuda 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ía de patrónPropósito centralEjemplos comunes
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 permiten gestionar instanciación.

El patró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.

El 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, lo que 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.

El patrón 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.");

El patrón Decorator: añadir funcionalidad dinámicamente

Decorator añade 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.

El patrón 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.

El patrón 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 el patrón 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 de diseño 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 debería 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.

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
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
3.
Martin Fowler, “Singleton,” Bliki, discute las compensaciones del Singleton y las implicaciones para las pruebas. https://martinfowler.com/bliki/Singleton.html
4.
Robert C. Martin (Uncle Bob), “The SOLID Principles,” que explica Open/Closed y objetivos de diseño relacionados. 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 y su impacto en costes. 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.