Patrones de diseño en JavaScript con ejemplos de código

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Patrones de diseño en JavaScript con ejemplos



Los patrones de diseño (design patterns) son soluciones probadas para problemas típicos en el desarrollo de software. Para un desarrollador de JavaScript, el conocimiento de patrones es especialmente importante, ya que el lenguaje tiene un modelo de objetos flexible y se usa ampliamente tanto en el cliente como en el servidor (Node.js). En este artículo analizaremos cuatro patrones clave con ejemplos prácticos en JavaScript.



¿Por qué estudiar patrones de diseño?

Los patrones ayudan a:

  • Simplificar el mantenimiento y la escalabilidad del código
  • Hacer el código más legible y predecible
  • Evitar "reinventar la rueda"
  • Mejorar la comunicación en el equipo (lenguaje común)


1. Singleton (Instancia única)

El patrón Singleton garantiza que una clase tenga solo una instancia y proporciona un punto de acceso global a ella. En Node.js, este patrón se usa a menudo para conectarse a una base de datos, trabajar con configuración o registro (logging).



class DatabaseConnection {  constructor() {    if (DatabaseConnection.instance) {      return DatabaseConnection.instance;    }    this.connection = null;    DatabaseConnection.instance = this;  }

connect() { // Simulación de conexión a la BD this.connection = "connected"; console.log("Conexión a la BD establecida"); }

query(sql) { if (!this.connection) { throw new Error("No hay conexión a la BD"); } console.log(`Ejecutando consulta: ${sql}`); return { data: [] }; }}

// Usoconst db1 = new DatabaseConnection();const db2 = new DatabaseConnection();

db1.connect();console.log(db1 === db2); // true - ambos apuntan a la misma instanciadb2.query("SELECT * FROM users");


Cuándo aplicarlo: gestión de recursos compartidos (BD, configuración, caché). Precaución: el uso excesivo puede provocar un fuerte acoplamiento del código.



2. Factory (Fábrica)

Factory Method define una interfaz para crear un objeto, pero permite que las subclases alteren el tipo de objeto creado. En JavaScript, esto a menudo se implementa mediante funciones o clases normales.



class User {  constructor(name, role) {    this.name = name;    this.role = role;  }

getPermissions() { return []; }}

class Admin extends User { getPermissions() { return ["read", "write", "delete", "manage"]; }}

class Editor extends User { getPermissions() { return ["read", "write"]; }}

class Viewer extends User { getPermissions() { return ["read"]; }}

// Fábricaclass UserFactory { createUser(name, role) { switch (role) { case "admin": return new Admin(name, role); case "editor": return new Editor(name, role); case "viewer": return new Viewer(name, role); default: throw new Error("Rol desconocido"); } }}

// Usoconst factory = new UserFactory();const admin = factory.createUser("Ana", "admin");const viewer = factory.createUser("Iván", "viewer");

console.log(admin.getPermissions()); // ["read", "write", "delete", "manage"]console.log(viewer.getPermissions()); // ["read"]


Ventajas: gestión centralizada de la creación de objetos, facilidad para agregar nuevos tipos, ocultación de la lógica de instanciación.



3. Observer (Observador)

El patrón Observer define una dependencia de "uno a muchos" entre objetos. Cuando un objeto (sujeto) cambia de estado, todos los objetos dependientes (observadores) son notificados automáticamente. Esta es la base de la programación orientada a eventos en Node.js.



class EventEmitter {  constructor() {    this.events = {};  }

on(eventName, listener) { if (!this.events[eventName]) { this.events[eventName] = []; } this.events[eventName].push(listener); }

emit(eventName, ...args) { const listeners = this.events[eventName]; if (listeners) { listeners.forEach(listener => listener(...args)); } }

off(eventName, listenerToRemove) { const listeners = this.events[eventName]; if (listeners) { this.events[eventName] = listeners.filter( listener => listener !== listenerToRemove ); } }}

// Usoconst emitter = new EventEmitter();

function onUserLoggedIn(user) { console.log(`Usuario conectado: ${user}`);}

emitter.on("userLoggedIn", onUserLoggedIn);emitter.emit("userLoggedIn", "María"); // Usuario conectado: María

emitter.off("userLoggedIn", onUserLoggedIn);emitter.emit("userLoggedIn", "Pedro"); // No pasa nada


Cuándo aplicarlo: sistemas de eventos, actualización de UI en tiempo real, comunicación entre componentes desacoplados. Precaución: puede causar fugas de memoria si los observadores no se eliminan correctamente.



4. Module (Módulo)

El patrón Module permite encapsular código en unidades separadas, ocultando detalles internos y exponiendo solo una API pública. En JavaScript moderno, esto se implementa mediante módulos ES (import/export) o CommonJS (require/module.exports).



// math.js - Módulo ESconst PI = 3.14159;

function add(a, b) { return a + b;}

function subtract(a, b) { return a - b;}

function circleArea(radius) { return PI * radius * radius;}

export { add, subtract, circleArea };

// app.jsimport { add, circleArea } from './math.js';

console.log(add(5, 3)); // 8console.log(circleArea(10)); // 314.159


Ventajas: encapsulación, reutilización, espacio de nombres limpio, facilidad para pruebas. En Node.js también se usa el patrón Module a través de CommonJS.



Conclusión

Los patrones de diseño son herramientas poderosas en el arsenal de un desarrollador de JavaScript. Singleton ayuda a gestionar recursos compartidos, Factory simplifica la creación de objetos, Observer proporciona una comunicación flexible entre componentes, y Module organiza el código en partes lógicas. La elección del patrón depende de la tarea específica: no intente aplicar patrones donde no se necesitan, pero cuando surja un problema típico, los patrones probados le ahorrarán tiempo y esfuerzo.



Practique la implementación de estos patrones en sus proyectos y, con el tiempo, aprenderá a reconocer situaciones donde su aplicación es apropiada. ¡Feliz codificación!

Blogs

Book Recommendations