Patrones de diseño en JavaScript: cómo escribir código reutilizable y escalable

Cuando empiezas a programar en JavaScript, probablemente lo haces con funciones sueltas, condicionales, ciclos y algún que otro objeto. Pero a medida que tus proyectos crecen, también lo hace la complejidad de tu código. Y ahí es donde entran en juego los patrones de diseño en JavaScript, una forma práctica y elegante de estructurar tu lógica para que sea más mantenible, reutilizable y escalable.

En este artículo vas a aprender qué son los patrones de diseño, cómo se aplican en JavaScript, y por qué deberías conocerlos si quieres llevar tus habilidades al siguiente nivel.

¿Qué son los patrones de diseño en JavaScript?

Los patrones de diseño son soluciones reutilizables a problemas comunes de programación. No son fragmentos de código específicos, sino más bien estructuras o enfoques probados que te ayudan a resolver situaciones similares una y otra vez, pero de forma organizada.

En JavaScript, gracias a su naturaleza flexible y orientada a objetos (aunque también funcional), puedes implementar estos patrones de formas muy variadas. Desde clases con constructor hasta funciones que devuelven otras funciones, las posibilidades son muchas.

Un patrón de diseño no es una receta exacta, sino una guía estructural.

Tipos de patrones de diseño en JavaScript

A continuación, veremos los patrones más comunes divididos en tres categorías: creacionales, estructurales y de comportamiento. También hablaremos de algunos patrones modernos y adaptados al ecosistema JavaScript actual.


Patrones de creación en JavaScript

Este tipo de patrones se usa para crear objetos o instancias de forma controlada. Son útiles cuando necesitas encapsular la lógica de creación o evitar duplicaciones.

Singleton

El patrón Singleton garantiza que solo exista una única instancia de un objeto en toda la aplicación. Es útil, por ejemplo, para gestionar configuraciones globales o conexiones.

const Singleton = (function () {
  let instance;
  function createInstance() {
    return { mensaje: 'Soy la única instancia' };
  }
  return {
    getInstance: function () {
      if (!instance) instance = createInstance();
      return instance;
    }
  };
})();

Este patrón es ideal cuando necesitas centralizar el estado de algo.

Factory

El patrón Factory se utiliza para crear objetos de forma flexible, dependiendo de parámetros o condiciones.

function crearUsuario(tipo) {
  if (tipo === 'admin') return { rol: 'admin', permisos: ['leer', 'escribir'] };
  return { rol: 'usuario', permisos: ['leer'] };
}

Este enfoque te permite evitar new repetitivos y adaptar el contenido según contexto.

Builder

Builder es un patrón útil cuando quieres construir objetos complejos paso a paso, en vez de un solo constructor con muchos parámetros.

class Pizza {
  constructor() {
    this.ingredientes = [];
  }
  agregar(ingrediente) {
    this.ingredientes.push(ingrediente);
    return this;
  }
}
const pizza = new Pizza().agregar('queso').agregar('tomate');

Muy útil para crear configuraciones encadenadas.


Patrones estructurales en JavaScript

Estos patrones se encargan de organizar clases y objetos para que trabajen juntos de manera eficiente.

Module

Este patrón encapsula lógica y variables para evitar contaminar el espacio global. Desde ES6 se puede hacer con export y import, pero el patrón clásico sigue siendo útil:

const modulo = (function () {
  const secreto = 'oculto';
  function publico() {
    console.log('Esto es público');
  }
  return { publico };
})();

Proxy

Proxy permite interceptar operaciones sobre un objeto. Puede ser útil para validaciones, logs o accesos controlados.

const usuario = { nombre: 'Ana' };
const proxy = new Proxy(usuario, {
  get: (obj, prop) => {
    console.log(`Accediendo a ${prop}`);
    return obj[prop];
  }
});

Adapter

El patrón Adapter sirve para adaptar la interfaz de un objeto a lo que necesita otro componente.

class APIAntigua {
  conectar() { console.log('Conectado con API antigua'); }
}
class Adaptador {
  constructor() {
    this.api = new APIAntigua();
  }
  conectarNuevo() {
    return this.api.conectar();
  }
}

Patrones de comportamiento en JavaScript

Este grupo de patrones está relacionado con la comunicación entre objetos y cómo se gestionan las responsabilidades.

Observer

Permite que un objeto (observador) reaccione cuando otro objeto (sujeto) cambia su estado.

class Sujeto {
  constructor() {
    this.observadores = [];
  }
  suscribir(obs) {
    this.observadores.push(obs);
  }
  notificar() {
    this.observadores.forEach(o => o());
  }
}

Strategy

Strategy permite cambiar el comportamiento de una función de forma dinámica, usando diferentes estrategias.

function enviarCorreo(strategy) {
  strategy();
}
const gmail = () => console.log('Enviado con Gmail');
const outlook = () => console.log('Enviado con Outlook');
enviarCorreo(gmail);

Command

Este patrón encapsula peticiones como objetos. Útil para deshacer acciones o programar comandos reutilizables.

class Luz {
  encender() { console.log('Luz encendida'); }
}
class ComandoEncender {
  constructor(luz) { this.luz = luz; }
  ejecutar() { this.luz.encender(); }
}

Patrones modernos y adaptados a JavaScript

En el mundo JavaScript actual, muchos desarrolladores se enfrentan a retos que no siempre están contemplados en los libros clásicos de patrones. Por eso, han surgido enfoques adaptados a las necesidades modernas, especialmente en entornos frontend, aplicaciones SPA y desarrollo con librerías como React, Vue o frameworks como Express.

Pub/Sub

El patrón Publicación/Suscripción es una evolución del Observer, pero con menor acoplamiento. Se utiliza en sistemas donde los emisores de eventos no necesitan saber quién los está escuchando. Esto lo hace ideal para manejar flujos de datos en tiempo real o eventos globales sin crear dependencias fuertes entre módulos.

En JavaScript es común usarlo para notificaciones de interfaz, comunicación entre componentes o gestión de eventos personalizados en apps web complejas.

Middleware

El patrón Middleware es muy frecuente en bibliotecas como Express, Redux o incluso en sistemas de autenticación. Permite encadenar funciones intermedias que procesan una petición antes de llegar a su destino final. Esto hace posible interceptar, modificar o cancelar acciones en tiempo de ejecución sin alterar el flujo principal de la aplicación.

Gracias a este enfoque, puedes centralizar validaciones, logging, control de acceso o transformaciones de datos, manteniendo tu código modular y reutilizable.

Hook Pattern

Inspirado por React, este patrón permite encapsular lógica reutilizable en funciones. A diferencia de otros patrones clásicos, los hooks no modifican estructuras externas, sino que aportan una forma limpia y clara de compartir comportamiento entre componentes.

El Hook Pattern se ha extendido más allá de React y ahora es común verlo en bibliotecas personalizadas, donde funciones como useAuth, useFetch o useForm permiten reutilizar lógica sin repetir código.

Middleware

Común en Express, Redux y otras librerías. Permite interceptar procesos y añadir lógica intermedia.

Hook Pattern

Inspirado por React, permite encapsular lógica reutilizable en funciones, sin alterar el componente principal.


Ejemplo práctico combinando varios patrones

Imagina que estás desarrollando una aplicación web para gestionar tareas personales y de equipo. Esta app incluye funcionalidades como creación de tareas, actualizaciones en tiempo real y persistencia en localStorage o base de datos.

Puedes aplicar varios patrones para mantener el código organizado y mantenible:

  • Factory. Utiliza una función para crear objetos Tarea, que pueden variar dependiendo del tipo (urgente, recurrente, etc.). Esto te permite instanciar tareas sin preocuparte por los detalles internos.
  • Observer. Usa este patrón para notificar automáticamente a otros componentes cuando cambie el estado de una tarea. Por ejemplo, si una tarea es completada, el contador de tareas pendientes se actualiza al instante.
  • Module. Encapsula funciones que interactúan con el DOM, como añadir tareas a la lista o actualizar el contador visual. Así evitas repetir lógica en diferentes partes de la app.
  • Middleware. Añade validaciones antes de guardar tareas, como revisar que tengan un título o una fecha válida. Esto te permite insertar lógica reutilizable y mantener tu flujo limpio.
  • Hook Pattern. Si estás usando React, puedes encapsular la lógica de tareas en hooks como useTareas, useFiltro o usePersistencia, haciendo que tu componente principal se mantenga enfocado solo en el renderizado.

Al combinar estos patrones, lograrás que tu app no solo funcione bien, sino que sea fácil de mantener, extender y testear. Este enfoque modular es especialmente útil en proyectos a largo plazo con equipos grandes o funcionalidades que crecen con el tiempo.


Escenarios ideales para aplicar patrones de diseño

Existen contextos donde los patrones de diseño brillan con mayor efectividad. Uno de ellos es cuando tu proyecto empieza a crecer y necesitas escalar funcionalidades sin romper lo anterior. Otro caso es cuando trabajas en equipo y necesitas una estructura clara para que todos entiendan cómo interactúan las piezas del sistema.

También son útiles en apps con mucha lógica de negocio, donde repetir estructuras te lleva a errores. Aplicando patrones puedes mantener la coherencia y reducir bugs desde el diseño. Y por supuesto, son indispensables cuando se construyen librerías o frameworks que luego otros desarrolladores usarán.

Errores comunes al aplicar patrones en JavaScript

Aunque dominar patrones de diseño es muy valioso, también es fácil cometer errores al aplicarlos. Uno de los más frecuentes es usarlos sin necesidad real, lo que añade complejidad innecesaria al proyecto. Otro fallo habitual es implementarlos de forma incompleta o mal entendida, lo que genera más confusión que solución.

También es común aplicar el patrón correcto en el lugar equivocado. Por ejemplo, usar Singleton para algo que en realidad debería ser un Factory. Por eso es fundamental entender bien el propósito de cada patrón antes de usarlo.

Cómo aprender patrones de forma progresiva

No hace falta memorizar todos los patrones a la vez. Puedes empezar por aquellos más intuitivos, como Module o Factory, e ir aplicándolos en proyectos personales. A medida que ganes confianza, podrás explorar los estructurales y de comportamiento.

Una buena estrategia es leer código de otros desarrolladores, especialmente en proyectos open source. También puedes buscar implementaciones de los mismos patrones en diferentes lenguajes. Esto te dará perspectiva y reforzará tu capacidad para adaptarlos según el entorno.

Buenas prácticas al usar patrones de diseño en JavaScript

  • No uses patrones por usar.. Evalúa primero si realmente lo necesitas.
  • Refactoriza con propósito.. A veces el código simple es suficiente.
  • Documenta tus decisiones.. Facilita el trabajo en equipo.
  • Comienza por los patrones más simples.. Singleton, Module o Factory suelen ser buenos puntos de partida.
  • Usa herramientas modernas.. Como Prettier o linters para mantener consistencia.

Cuándo evitar los patrones de diseño

Aunque pueden ser muy útiles, hay casos donde aplicar un patrón complica más que ayuda:

  • Cuando el proyecto es muy pequeño o temporal. En esos casos, la simplicidad puede ser tu mejor patrón.
  • Si la solución original ya es suficientemente clara. Añadir patrones sin necesidad solo complicará el código.
  • Si el equipo no está familiarizado y puede generar confusión. La curva de aprendizaje puede ser una barrera si no se acompaña de documentación.

No conviertas tu código en una clase de teoría innecesaria.


Conclusión

Los patrones de diseño en JavaScript son una herramienta poderosa, pero como toda herramienta, deben usarse con criterio. Conocerlos te ayudará a escribir mejor código, trabajar mejor en equipo y enfrentarte a proyectos más grandes con confianza.

Y si ya has leído artículos como Todos los métodos de JavaScript que deberías conocer, o ya manejas librerías como las que mencionamos en Librerías de JavaScript más usadas, este es el siguiente paso lógico.

Recuerda: no se trata solo de que tu código funcione, sino de que sea entendible, mantenible y elegante.

Compartir:

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Tabla de contenidos

Más posts

Categorías

Contáctame

Escríbeme a través del formulario. Estoy encantado de ayudarte con diseño web, contenido visual, redes o cualquier duda.