Volver a JavaScript Intermedio
JavaScript
JavaScript Intermedio
Patrones de Diseño en JavaScript
Los patrones de diseño son soluciones reutilizables a problemas comunes.
Patrón Módulo
Encapsula el estado privado con una API pública:
const GestorTareas = (() => {
// Estado privado
let tareas = [];
let siguienteId = 1;
// Helper privado
function validar(tarea) {
if (!tarea.titulo) throw new Error('El título es obligatorio');
}
// API pública
return {
agregar(titulo, prioridad = 'media') {
const tarea = { id: siguienteId++, titulo, prioridad, completada: false };
validar(tarea);
tareas.push(tarea);
return tarea;
},
completar(id) {
const tarea = tareas.find(t => t.id === id);
if (tarea) tarea.completada = true;
},
obtenerTodas() { return [...tareas]; }, // Copia para evitar mutación
obtenerPendientes(){ return tareas.filter(t => !t.completada); },
};
})();
Observador / PubSub
Desacopla productores de consumidores:
class EmisorEventos {
#listeners = new Map();
on(evento, callback) {
if (!this.#listeners.has(evento)) this.#listeners.set(evento, []);
this.#listeners.get(evento).push(callback);
return () => this.off(evento, callback); // Retorna función de desuscripción
}
off(evento, callback) {
const cbs = this.#listeners.get(evento) ?? [];
this.#listeners.set(evento, cbs.filter(cb => cb !== callback));
}
emit(evento, ...args) {
(this.#listeners.get(evento) ?? []).forEach(cb => cb(...args));
}
}
const bus = new EmisorEventos();
const desuscribir = bus.on('login', usuario => console.log(`Bienvenido, ${usuario.nombre}`));
bus.emit('login', { nombre: 'Ana' });
desuscribir(); // Limpiar
Singleton
Garantiza que una clase tenga una única instancia:
class Configuracion {
static #instancia = null;
#ajustes = {};
static obtenerInstancia() {
if (!Configuracion.#instancia) {
Configuracion.#instancia = new Configuracion();
}
return Configuracion.#instancia;
}
set(clave, valor) { this.#ajustes[clave] = valor; }
get(clave) { return this.#ajustes[clave]; }
}
const cfg1 = Configuracion.obtenerInstancia();
const cfg2 = Configuracion.obtenerInstancia();
console.log(cfg1 === cfg2); // true — misma instancia
Patrón Estrategia
Intercambia algoritmos en tiempo de ejecución:
const ordenadores = {
burbuja: (arr) => { /* bubble sort */ return arr; },
mezcla: (arr) => { /* merge sort */ return arr; },
rápido: (arr) => { /* quick sort */ return arr; },
};
class OrdenadorDatos {
constructor(estrategia = 'rápido') {
this.estrategia = estrategia;
}
ordenar(datos) {
return ordenadores[this.estrategia]([...datos]);
}
}
const ordenador = new OrdenadorDatos('mezcla');
ordenador.ordenar([5, 3, 8, 1]);
ordenador.estrategia = 'rápido'; // Cambiar estrategia en tiempo de ejecución
Patrón Constructor (Builder)
Construye objetos complejos paso a paso:
class ConstructorConsulta {
#consulta = { tabla: '', condiciones: [], ordenPor: null, limite: null };
de(tabla) { this.#consulta.tabla = tabla; return this; }
donde(condición) { this.#consulta.condiciones.push(condición); return this; }
ordenarPor(col, dir = 'ASC') { this.#consulta.ordenPor = `${col} ${dir}`; return this; }
limitar(n) { this.#consulta.limite = n; return this; }
construir() {
let sql = `SELECT * FROM ${this.#consulta.tabla}`;
if (this.#consulta.condiciones.length)
sql += ` WHERE ${this.#consulta.condiciones.join(' AND ')}`;
if (this.#consulta.ordenPor)
sql += ` ORDER BY ${this.#consulta.ordenPor}`;
if (this.#consulta.limite)
sql += ` LIMIT ${this.#consulta.limite}`;
return sql;
}
}
const consulta = new ConstructorConsulta()
.de('usuarios')
.donde('edad > 18')
.donde('activo = true')
.ordenarPor('nombre')
.limitar(10)
.construir();