Potencia tu proyecto de hábitos con polimorfismo en JavaScript: dos clases especializadas calculan rachas diarias y semanales con el mismo método, mientras una estrategia según la frecuencia decide qué lógica ejecutar. Verás cómo organizar logs, normalizar fechas, evitar errores comunes y preparar la UI en HTML y CSS para mostrar el resultado.
¿Cómo aplicar polimorfismo en JavaScript para rachas de hábitos?
El enfoque usa una tabla de mapeo por frecuencia que instancia calculators y un método en Habit que selecciona la estrategia correcta. Ambas clases comparten el método calculate, lo que habilita el polimorfismo: mismo mensaje, comportamientos distintos.
Mapa de estrategias con streakCalculators: daily y weekly.
Selección dinámica según this.frequency del hábito.
Manejo de fecha actual con new Date() como today.
Retorno seguro: si no hay estrategia, racha en 0.
// Mapa de estrategias por frecuenciaconst streakCalculators ={daily:newDailyStreakCalculator(),weekly:newWeeklyStreakCalculator(),};// En la clase HabitcalculateStreak(today =newDate()){const calculator = streakCalculators[this.frequency];if(!calculator)return0;// sin estrategia, no hay rachareturn calculator.calculate(this,this.getLogs(), today);}
¿Qué estructura usa calculateStreak con frecuencia y today?
El método recibe today, busca el calculator por frecuencia y llama a calculate(habit, logs, today). Aquí ocurre el polimorfismo: DailyStreakCalculator y WeeklyStreakCalculator comparten firma, pero su lógica interna difiere.
Parámetros clave: habit, logs de fechas, today.
Validación inicial: si no hay registros, racha 0.
Consistencia de interfaz: ambos calculators exponen calculate.
¿Qué hacen DailyStreakCalculator y WeeklyStreakCalculator?
Ambas clases calculan rachas con enfoques distintos. La diaria compara fechas día a día. La semanal agrupa por semana y recorre semanas consecutivas. Se usan utilidades internas (métodos privados) para formatear fechas y agrupar semanas.
DailyStreakCalculator:
Ordena logs del más reciente al más antiguo.
Normaliza today sin horas, minutos, segundos ni milisegundos.
Compara contra la fecha esperada en formato año-mes-día.
Si no coincide, rompe el ciclo y retorna la racha.
classDailyStreakCalculator{calculate(habit, logs, today){if(!logs || logs.length===0)return0;const sorted =[...logs].sort((a, b)=> b - a);// reciente → antiguoconst current =newDate(today.getFullYear(), today.getMonth(), today.getDate());let streak =0;for(const log of sorted){const expected =newDate(current); expected.setDate(current.getDate()- streak);if(this.#toKey(log)===this.#toKey(expected)) streak++;elsebreak;// racha interrumpida}return streak;}// método privado: formatea fecha a clave estable (YYYY-M-D)#toKey(date){return`${date.getFullYear()}-${date.getMonth()+1}-${date.getDate()}`;}}
WeeklyStreakCalculator:
Agrupa logs por semana con una utilería interna.
Ordena semanas con Object.keys de más reciente a más antigua.
Obtiene la semana actual y avanza comparando con la semana esperada.
Si la semana no coincide, rompe el bucle y retorna la racha.
classWeeklyStreakCalculator{calculate(habit, logs, today){if(!logs || logs.length===0)return0;const byWeek =this.#groupByWeek(logs);const weeks =Object.keys(byWeek).sort((a, b)=> b.localeCompare(a));const currentWeek =this.#weekKey(today);let streak =0;let expected = currentWeek;for(const wk of weeks){if(wk === expected){ streak++; expected =this.#prevWeek(expected);}else{break;// racha interrumpida}}return streak;}// utilería: agrupa fechas por clave de semana#groupByWeek(dates){return dates.reduce((acc, d)=>{const key =this.#weekKey(d);(acc[key]||=[]).push(d);return acc;},{});}// clave de semana y cálculo de semana previa (implementación consistente en tu código)#weekKey(date){/* ... */}#prevWeek(weekKey){/* ... */}}
¿Qué considerar al trabajar con fechas en JavaScript?
Las fechas requieren atención al detalle: normalización, conversión y consistencia. JavaScript ofrece múltiples formas de transformar fechas; cuida el formato de claves y el cálculo de número de semana y semana previa para evitar desajustes.
Normaliza la fecha para comparaciones estables.
Evita dependencias implícitas de zona horaria.
Sé consistente en las claves de día y semana.
¿Cómo depurar y probar con consola y UI?
La inicialización crea hábitos de prueba y muestra resultados en consola con live server. Un detalle importante: olvidar ejecutar un método puede romper la racha. La técnica del patito de hule ayuda a leer el código en voz alta y detectar errores.
Demo: hábito “leer” daily con 3 registros. Hábito “ejercicio” weekly con 2 registros.
Salida: toDisplayString() y calculateStreak() en console.log.
Observación: rachas en 0 por pasar this.getLogs en vez de this.getLogs().
Corrección: agregar paréntesis para ejecutar la función.
¿Qué error común con getLogs y cómo evitarlo?
Pasar una referencia a función no la ejecuta.
Usa this.getLogs() al invocar calculate.
Valida con console.log los parámetros antes del cálculo.
¿Qué reto de interfaz con HTML y CSS?
Renderiza la racha diaria y semanal con el retorno de calculate.
Usa la estructura visual ya definida en HTML y CSS.
Integra el valor en componentes de la UI de hábitos.
¿Tienes una propuesta de UI o mejoras a la lógica de rachas? Compártelas en comentarios y armemos un buen debate técnico.