¿Es bisiesto?

JS Refactoring

El próximo año es bisiesto. ¿Cómo podemos saber si un año es bisiesto? Es una simple regla: el año debe ser divisible por cuatro. Esta regla, como todas, tiene sus excepciones: Si el año es divisible por 100, no es bisiesto, a menos que sea divisible por 400 también. Para saber más detalles pueden revisar la Wikipedia.

Veamos unos ejemplos:

  • 2000: Es divisible por 4, por 100 y por 400 también, entonces es bisiesto.
  • 2019: No es divisible por 4, entonces no es bisiesto.
  • 2020: Es divisible por 4 pero no por 100, entonces es bisiesto.
  • 2100: Es divisible por 4 y por 100, pero no por 400, entonces no es bisiesto.

Es un ejercicio sencillo de programar, pero me resultó muy interesante.

Solución inicial

Por lo general, mi manera de resolver los problemas es buscar una solución que funcione y después refactorizar mi código por una solución más elegante y/o con mejor rendimiento.

Esta fue mi solución inicial:

export const isLeap = (year) => {
  if (year % 4 === 0) {
    if (year % 100 === 0) {
      if (year % 400 === 0) {
        return true;
      }
      return false;
    }
    return true;
  }

  return false;
};

Fue simplemente traducir literalmente las reglas explicadas anteriormente a Javascript. Resuelve el problema, pero se puede mejorar.

Refactorización

Refactorización con cláusulas de guarda

Mi primera idea fue eliminar los if anidados con las cláusulas de guarda. Aquí está el código:

export const isLeap = (year) => {
  if (year % 400 === 0) {
    return true;
  }

  if (year % 100 === 0) {
    return false;
  }

  return year % 4 === 0;
};

El código verifica las condiciones más específicas primero y después las más genéricas. De esta manera evitamos hacer comparaciones redundantes.

  • Si un año es divisible por 400, es porque es divisible por 100 y por 4, entonces es bisiesto.
  • Si se cumple la condición de que el año es divisible por 100, podemos decir con seguridad que

no es bisiesto, porque en este punto ya sabemos que no es divisible por 400.

  • Y finalmente si llegamos al return final ya sabemos que el año no es divisible por 400 ni

por 100, entonces solo aplica la regla más genérica: es bisiesto si es divisible por 4.

Para saber más acerca de las cláusulas de guarda les recomiendo ver la definición aquí Refactoring: Guard Clauses y ver este video de la serie RigorTalks de Carlos Buenosvinos que explica muy bien como funcionan.

Esta solución es mucho mejor en mi opinión que la inicial, pero todavía es un poco verbosa. ¿Se podrá reducir más?

Refactorización: Eliminando los if

Francesco Cirillo, creador de la Técnica pomodoro, es el creador tambien de una campaña muy curiosa: Campaña Anti-if. ¿Es posible crear una solución sin el uso de if para este problema? Veamos:

Primer paso

Eliminamos el if más anidado y retornamos directamente el resultado de year % 400 === 0

export const isLeap = (year) => {
  if (year % 4 === 0) {
    if (year % 100 === 0) {
       return year % 400 === 0
    }
    return true;
  }

  return false;
};

Segundo paso

Continuando con la misma idea, eliminamos el if anidado, pero en este caso hay que combinarlo con el valor de retorno anterior y modificar ligeramente la condición.

Si el año no es divisible por 100 (year % 100 !== 0) o (||) si el año es divisible por 400 (year % 400 === 0) entonces es bisiesto. Esto se traduce a year % 100 !== 0 || year % 400 === 0 en código Javascript.

export const isLeap = (year) => {
  if (year % 4 === 0) {
    return year % 100 !== 0 || year % 400 === 0;
  }
 
  return false;
};

Paso final

Ahora es el turno de eliminar el último if. Para garantizar que el año es bisiesto se tiene que cumplir la condición del if (year % 4 === 0) y (&&) la condición del return (year % 100 !== 0 || year % 400 === 0). Eso se traduce al siguiente código Javascript:

export const isLeap = (year) => {
  return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
};

De esta manera hemos eliminado todos los if y reducido nuestro código a una sola línea.

Aprendiendo de otros

Incluso cuando nos sentimos contento con nuestras soluciones, creo que es muy buena idea ver las de otros porque de seguro que aprenderemos algo nuevo. Una que me resultó muy interesante fue la de lainjiang. Es una línea igualmente pero las condiciones son distintas.

Referencias