Skip to content

Paradigmas de Programación

🚀 Clases en Vivo — Inicio: Domingo 6 de Septiembre ($200 MXN / clase). Aprende a programar desde cero con acompañamiento directo y construye el proyecto ShortURL.

En la Lección 3: Language Tour (L3) descubrimos la caja de herramientas de JavaScript para manipular textos, listas y objetos, además de blindar nuestras aplicaciones con try / catch.

Ahora que sabemos escribir código funcional, surge una pregunta fundamental: ¿Cómo organizamos programas grandes sin que se conviertan en un laberinto incontrolable? Aquí es donde entran los Paradigmas de Programación: diferentes filosofías y estilos para estructurar el pensamiento y resolver problemas en software.

En esta sesión intensiva de 4 horas, exploraremos los tres paradigmas indispensables de la industria: la Programación Orientada a Objetos (POO), la Programación Funcional y el superpoder de la Asincronía (Promises y async / await).


🗺️ Estructura de la Sesión

Cada una de nuestras sesiones semanales sigue una metodología pedagógica dividida en 5 fases:

Estructura de la Sesión
├── ☕ Fase 1: Revisión del Tema Anterior y Conclusiones
├── 🎯 Fase 2: Introducción al Tema y Fundamentos
├── 💻 Fase 3: Práctica Asistida en Vivo
├── 🗣️ Fase 4: Exposición, Debate y Debugging
└── 🏠 Fase 5: Reto Semanal y Práctica en Casa

☕ Fase 1: Revisión del Tema Anterior y Conclusiones

Comenzamos la sesión revisando el proyecto de analíticas de e-commerce y reflexionando sobre la organización del código.

1. Puesta en Común del Reto de la Lección 3

En la lección previa construimos el “Motor de Analíticas de Ventas”, combinando filter(), map(), reduce() y try / catch:

  • Discusión en grupo: ¿Cómo sentiste la diferencia entre escribir un bucle for manual y usar map() o filter()?
  • ¿Qué ventajas encontraste al envolver cálculos críticos dentro de try / catch?
  • Si tuviéramos 50 tipos diferentes de productos con reglas complejas (garantías, envíos internacionales, devoluciones), ¿dónde colocarías esa lógica sin duplicar código?

2. El Puente Conceptual: ¿Qué es un Paradigma?

Un paradigma es simplemente una forma de ver y organizar el mundo a través del código:

  • Programación Imperativa: Le dice a la computadora exactamente cómo hacer las cosas paso por paso (como una receta de cocina muy estricta).
  • Programación Orientada a Objetos (POO): Modela el software agrupando datos y comportamientos en “cajas inteligentes” llamadas Objetos (como autos, cuentas bancarias o usuarios).
  • Programación Funcional: Trata a las funciones como tuberías matemáticas puras que transforman datos sin alterar el estado global.
  • Programación Asíncrona: Permite que nuestro programa realice tareas que toman tiempo (como descargar un archivo o consultar una base de datos) sin congelar la pantalla del usuario.

JavaScript es un lenguaje multiparadigma, lo que significa que puedes combinar lo mejor de cada mundo en un solo proyecto.


🎯 Fase 2: Introducción al Tema y Fundamentos Teóricos

Analicemos en profundidad los tres pilares estructurales del software moderno.


1. Programación Orientada a Objetos (POO): Clases e Instancias

Imagina los planos de un arquitecto para construir casas:

  • La Clase (class): Es el plano arquitectónico. Define qué propiedades tendrá la casa (número de habitaciones, color) y qué acciones se pueden hacer en ella (abrir puerta, encender luces).
  • La Instancia / Objeto: Es la casa real construida en el terreno. Con un solo plano puedes construir 100 casas distintas, cada una con su propio color y dueño.
// 1. El Plano (Clase)
class Usuario {
constructor(nombre, email) {
this.nombre = nombre; // Propiedad
this.email = email; // Propiedad
this.activo = true;
}
// Método (Acción que el usuario puede realizar)
desactivarCuenta() {
this.activo = false;
return `La cuenta de ${this.nombre} ha sido desactivada.`;
}
}
// 2. Construimos una casa real a partir del plano (Instancia con "new")
const usuario1 = new Usuario("Lucía Méndez", "[email protected]");
console.log(usuario1.nombre); // "Lucía Méndez"
console.log(usuario1.desactivarCuenta());

2. Programación Funcional: Funciones Puras e Inmutabilidad

En la Programación Funcional, pensamos en las funciones como licuadoras de cocina:

  • Si colocas fresas y leche, siempre obtendrás jugo de fresa.
  • La licuadora no cambia el color de las paredes de tu cocina (no tiene efectos secundarios).
  • Una Función Pura es aquella que con las mismas entradas siempre devuelve la misma salida y no altera variables fuera de sí misma.
// Función Pura: No modifica nada externo, solo transforma y retorna
function aplicarDescuento(precioOriginal, porcentaje) {
return precioOriginal - (precioOriginal * (porcentaje / 100));
}
console.log(aplicarDescuento(100, 20)); // 80 (siempre dará 80)

3. Asincronía: La Analogía del Restaurante y el Zumbador

¿Qué ocurre cuando vas a pedir comida a un restaurante rápido?

  1. Llegas al mostrador y pides tu hamburguesa.
  2. El cajero no te hace esperar parado 15 minutos en el mostrador congelando la fila de clientes.
  3. En su lugar, te entrega un zumbador electrónico (Buzzer): una Promesa de que tu comida estará lista en el futuro.
  4. Mientras la cocina prepara tu comida, tú te sientas a revisar tu teléfono (el programa sigue funcionando).
  5. Cuando la comida está lista, el zumbador vibra (.then() o await) y recoges tu pedido.
Flujo Asíncrono
[Hacer Pedido] ──► [Recibir Promesa / Zumbador] ──► [Hacer otras cosas] ──► [¡Comida Lista! (await)]

En JavaScript moderno usamos async / await para esperar promesas de forma limpia y legible:

// La palabra "async" convierte la función en un proceso asíncrono
async function prepararCafe() {
console.log("1. Pidiendo café al barista...");
// "await" pausa esta función hasta que la promesa se complete, sin congelar el navegador
await esperarSegundos(2);
console.log("2. ☕ ¡Café listo para disfrutar!");
}

🔍 Experimento en Vivo: Síncrono vs Asíncrono en la Consola

Vamos a experimentar la diferencia en vivo:

  1. Abre la consola de tu navegador (F12).
  2. Ejecuta este código con un temporizador asíncrono (setTimeout):
    console.log("🟢 Paso 1: Inicio");
    setTimeout(() => {
    console.log("🟡 Paso 2: Tarea en segundo plano completada tras 2 segundos");
    }, 2000);
    console.log("🔴 Paso 3: Fin del script principal");
  3. Observa el orden en la consola: Verás que aparece primero el Paso 1, de inmediato el Paso 3, y 2 segundos después el Paso 2. ¡JavaScript nunca se congeló!

💻 Fase 3: Práctica Asistida en Vivo

Abriremos la consola y resolveremos juntos 4 retos guiados combinando POO, funciones puras y asincronía.


🧩 Reto 1: Modelando una CuentaBancaria con POO

Objetivo: Crear una clase con estado interno protegido y métodos que validen reglas de negocio.

class CuentaBancaria {
constructor(titular, saldoInicial = 0) {
this.titular = titular;
this.saldo = saldoInicial;
this.historial = [];
}
depositar(monto) {
if (monto <= 0) {
return "❌ El monto a depositar debe ser mayor a 0.";
}
this.saldo += monto;
this.historial.push(`Depósito: +$${monto}`);
return `Depósito exitoso. Saldo actual: $${this.saldo}`;
}
retirar(monto) {
if (monto > this.saldo) {
return `❌ Fondos insuficientes. Intentaste retirar $${monto}, pero tu saldo es $${this.saldo}`;
}
this.saldo -= monto;
this.historial.push(`Retiro: -$${monto}`);
return `Retiro exitoso de $${monto}. Saldo restante: $${this.saldo}`;
}
consultarEstado() {
return {
titular: this.titular,
saldo: this.saldo,
movimientos: this.historial
};
}
}
// Pruebas en consola:
const miCuenta = new CuentaBancaria("Joul Medina", 500);
console.log(miCuenta.depositar(200)); // Saldo: 700
console.log(miCuenta.retirar(100)); // Saldo: 600
console.log(miCuenta.retirar(1000)); // Fondos insuficientes
console.log(miCuenta.consultarEstado());

🧩 Reto 2: Tubería Funcional de Transformación de Datos

Objetivo: Encadenar funciones puras para limpiar y transformar una lista de precios en dólares a moneda local formateada.

const preciosUSD = [12.5, 45.0, 99.99, 5.0, 150.0];
const TASA_CAMBIO = 18.5; // Pesos por dólar
// 1. Funciones puras independientes
const convertirAMonedaLocal = (precioUSD) => precioUSD * TASA_CAMBIO;
const redondear = (monto) => Math.round(monto * 100) / 100;
const darFormatoMoneda = (monto) => `$${monto.toFixed(2)} MXN`;
// 2. Tubería de procesamiento con map encadenado
const preciosFormateados = preciosUSD
.map(convertirAMonedaLocal)
.map(redondear)
.map(darFormatoMoneda);
console.log("Precios originales (USD):", preciosUSD);
console.log("Precios procesados:", preciosFormateados);

🧩 Reto 3: Creando y Resolviendo una Promesa Real

Objetivo: Crear una función que simule la descarga de un archivo con resolución exitosa o fallo aleatorio.

function descargarArchivo(nombreArchivo) {
return new Promise((resolve, reject) => {
console.log(`⏳ Descargando "${nombreArchivo}" desde el servidor...`);
setTimeout(() => {
const conexionExitosa = true; // Cambia a false para probar el error
if (conexionExitosa) {
resolve(`✅ Archivo "${nombreArchivo}" descargado con éxito (5.4 MB)`);
} else {
reject(new Error(`❌ Error de red al descargar "${nombreArchivo}"`));
}
}, 2000);
});
}
// Consumo con .then() y .catch():
descargarArchivo("reporte_anual.pdf")
.then(mensajeExito => console.log(mensajeExito))
.catch(error => console.error(error.message));

🧩 Reto 4: Sincronización de Procesos con async / await y try / catch

Objetivo: Coordinar dos tareas asíncronas consecutivas de forma limpia sin callbacks anidados.

function simularEspera(segundos) {
return new Promise(resolve => setTimeout(resolve, segundos * 1000));
}
async function procesarRegistroUsuario(nombreUsuario) {
try {
console.log(`1. Verificando disponibilidad de "${nombreUsuario}"...`);
await simularEspera(1.5);
console.log(" -> Nombre disponible.");
console.log("2. Guardando usuario en la base de datos...");
await simularEspera(1.5);
console.log(" -> Usuario guardado.");
console.log("3. Enviando correo de bienvenida...");
await simularEspera(1.0);
console.log(" -> Correo enviado.");
return `🎉 ¡Registro de ${nombreUsuario} completado con éxito!`;
} catch (error) {
return `⚠️ Falla en el proceso de registro: ${error.message}`;
}
}
// Ejecutamos la función asíncrona:
procesarRegistroUsuario("joul_dev").then(resultado => console.log(resultado));

🗣️ Fase 4: Exposición de Resultados, Debate y Debugging

Compartimos soluciones y analizamos las trampas más frecuentes al trabajar con clases y asincronía.

🐞 Las 4 Trampas Clásicas de Paradigmas y Asincronía

1. Olvidar la palabra clave new al crear un objeto

  • El error: Escribir const cuenta = CuentaBancaria("Ana", 100); sin new.
  • Consecuencia: TypeError: Class constructor CuentaBancaria cannot be invoked without 'new'.
  • Solución: Las clases siempre requieren new para reservar su espacio en memoria.

2. Olvidar la palabra await en una función asíncrona

  • El error: Escribir const datos = descargarArchivo("datos.csv"); sin await.
  • Consecuencia: En lugar de recibir el texto del archivo, datos contendrá un objeto Promise { <pending> }.
  • Solución: Siempre que llames a una función que devuelva una promesa dentro de una función async, antepón await.

3. Errores Asíncronos no Capturados (Unhandled Promise Rejection)

  • El error: Llamar a una función asíncrona que falla sin envolver el await en un try / catch.
  • Consecuencia: El navegador o Node.js emitirá una advertencia crítica de promesa no manejada.
  • Solución: Todo código asíncrono con await debe vivir dentro de un bloque try { ... } catch (error) { ... }.

4. Modificar variables globales dentro de funciones

  • El error: Que una función altere un arreglo global en lugar de recibirlo como parámetro y devolver uno nuevo.
  • Consecuencia: Rompe el principio de funciones puras y causa errores “fantasma” difíciles de rastrear.

🏠 Fase 5: Definición de Práctica en Casa / Reto Semanal

Para consolidar la POO y la Asincronía, construirás el simulador de una cocina digital.

🏆 Proyecto Semanal: “El Simulador Asíncrono de Restaurante y Cocina”

Descripción del Problema: Un restaurante necesita un sistema para gestionar pedidos. Cada pedido debe representarse como un objeto con su estado, y pasar por las etapas de cocina con retardos de tiempo reales.

Especificaciones Técnicas del Proyecto:

  1. Clase Pedido (POO):

    • Propiedades: id, cliente, platillo, estado (inicia en "Recibido"), precio.
    • Métodos:
      • actualizarEstado(nuevoEstado): Cambia el estado del pedido y muestra un mensaje en consola.
      • resumen(): Devuelve un texto con la información completa del pedido.
  2. Proceso de Cocina Asíncrono (async / await):

    • Crea una función asíncrona cocinarPedido(pedido) que ejecute las siguientes etapas secuenciales con pausas de tiempo (setTimeout en Promesas):
      1. Paso 1 (1 segundo): Cambiar estado a "Preparando ingredientes".
      2. Paso 2 (2 segundos): Cambiar estado a "Cocinando en el fuego".
      3. Paso 3 (1.5 segundos): Cambiar estado a "Emplatando y decorando".
      4. Paso 4 (1 segundo): Cambiar estado a "¡Listo para entrega!".
  3. Control de Errores con try / catch:

    • Si el platillo pedido es "Veneno" o un texto vacío, la función debe lanzar un throw new Error("Platillo no permitido en el menú") y el bloque catch debe reportar la cancelación del pedido.
  4. Prueba en Consola:

    • Crea 2 pedidos (ej. Pedido 1: "Hamburguesa Gourmet", Pedido 2: "Pizza Margarita"), inícialos y observa cómo la consola narra el progreso de ambos en tiempo real.

📚 Recursos y Videos Recomendados

Para profundizar en POO, Programación Funcional y Asincronía:


🚀 Conclusión y Próximos Pasos

¡Excelente trabajo! Has dado un salto cuántico en tu madurez como desarrollador. Ahora comprendes cómo modelar entidades con Clases (POO), cómo escribir código predecible con Funciones Puras y cómo manejar tareas en segundo plano con Promesas y Async/Await.

Con estos fundamentos listos, en la próxima sesión daremos el paso hacia el desarrollo de aplicaciones completas: Lección 5: Stacks Tecnológicos & Arquitectura del Proyecto ShortURL (L5), donde diseñaremos nuestro gran proyecto de fin de curso.

🚀 Clases en Vivo — Inicio: Domingo 6 de Septiembre ($200 MXN / clase). Aprende a programar desde cero con acompañamiento directo y construye el proyecto ShortURL.