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

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.

Visión general de las categorías de patrones de diseño OOP
| Categoría de patrón | Propósito central | Ejemplos comunes |
|---|---|---|
| Creacionales | Gestionar y abstraer la creación de objetos | Factory, Builder, Singleton, Prototype |
| Estructurales | Componer clases y objetos en estructuras más grandes y flexibles | Adapter, Decorator, Facade, Composite |
| De comportamiento | Definir cómo los objetos interactúan y se comunican | Observer, 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.

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