Regístrate para acceder a más de 15 cursos gratuitos de programación con un simulador

Estrategia (Patrón) JS: Polimorfismo

Fijemos la teoría aprendida con un ejemplo práctico que muestra una aplicación típica del polimorfismo de subtipos.

Imagina que tienes que calcular el costo de un seguro de viaje. Este seguro es recomendable al viajar al extranjero para cubrir enfermedades o lesiones repentinas.

El costo del seguro depende de muchos factores. Algunos de estos factores pueden influir en el propio proceso de cálculo, es decir, no solo cambian los valores en la fórmula de cálculo, sino la fórmula misma.

En el caso de los seguros, es probable que haya una gran fórmula en la que se introducen los valores y el cálculo se realiza de una vez. Lo importante para nosotros ahora es el concepto en sí, no el conocimiento exacto del negocio de los seguros.

Si resolvemos esta tarea de manera directa, se verá como un gran lío de cálculos con muchas condiciones. Con el tiempo, este código se vuelve difícil de entender debido a la cantidad de estados que hay que mantener en mente.

// Ejemplo
if (age < 18) {
  let cost = salary * age;

  if (country === 'uganda') {
    cost = cost / 2;
  }
} else if (age >= 18 && age < 24) {
    // ...
}

¿Podemos hacer el código más comprensible y fácil de entender? A veces sí. De todos los factores que participan en el cálculo, debemos intentar encontrar aquellos que influyen globalmente en el cálculo. Estos se manifiestan como un if global a nivel superior. Supongamos que en el caso del seguro, esto es la edad. Es decir, consideramos que la edad determina la fórmula de cálculo del costo del seguro. El siguiente paso es mirar las ramas de esta construcción condicional y los rangos que se indican allí. Supongamos el siguiente escenario:

  • Hasta 18 años
  • De 18 a 24 años
  • De 24 a 65 años
  • Más de 65 años

Fíjate en un detalle importante. Antes dijimos que cada grupo de edad determina el algoritmo de cálculo del costo del seguro. Es decir, son independientes entre sí, aunque el proceso de cálculo puede ser similar en algunos casos.

Ahora pasemos de la estructura lógica al código. Cada grupo de edad será una clase que se encarga de los cálculos del costo para ese grupo:

class LessThan18 {
  // Los parámetros son los factores en los que se basa el cálculo
  calculate(params) {
    // Aquí calculamos y devolvemos el resultado
  }
}

// El nombre, por supuesto, no es el mejor; en el código real, deberíamos inventar algo más significativo
class MoreThan18AndLessThan24 {
  // La estructura de los parámetros debe coincidir al 100% con las demás clases,
  // ya que solo en este caso es posible el polimorfismo
  calculate(params) {
      // Aquí calculamos y devolvemos el resultado
  }
}

// las demás clases

Lo principal que hemos conseguido es dividir el proceso de cálculo en bloques de código independientes, que son más fáciles de entender. Cada clase de este tipo se llama estrategia (de cálculo). Es muy importante que la estrategia no sea una abstracción, un objeto con estado y tiempo de vida. Por lo tanto, los datos no se pasan al constructor, sino al propio método. En esencia, es una función normal (cálculo), que está empaquetada en una clase solo para obtener polimorfismo de subtipos. Todo esto mismo se puede hacer utilizando la despachación de funciones por claves, lo que simplifica el código.

Luego surge la pregunta: ¿cómo y dónde elegir la implementación correcta con la que se debe trabajar? Hay varias opciones aquí. La elección de la implementación puede ser delegada al código externo. Es decir, si aplicamos la inversión de dependencias, trabajamos con la estrategia lista:

calculateCost(strategy, params) {
  strategy.calculate(params);
}

Hasta ahora solo hemos evitado el problema, pero no lo hemos resuelto. En cualquier caso, en algún lugar habrá un código que contiene una construcción condicional o implementa una de las formas de despachación que discutimos en lecciones anteriores. En el caso más simple, este código se verá así:

chooseCostInsuranceStrategy(user) {
  if (user.getAge() < 18) {
    return new LessThan18();
  } else if (/* ... */) {
    // Algún código
  }
}

strategy = chooseCostInsuranceStrategy(user);
strategy.calculate(params);

Como ya viste en los ejemplos anteriores, habrá más código al usar la estrategia, aunque no tanto si se utiliza la despachación de funciones por claves en un array asociativo. Esto se aplica a casi todas las situaciones donde se usa el polimorfismo de subtipos en JavaScript. Es el precio a pagar por una separación que facilita la expansión del código y reduce su complejidad. Sin embargo, es fácil caer en la trampa y hacer el código más complejo de lo necesario. El polimorfismo puede hacer el código verboso y demasiado abstracto si se usa mucho. Además, las dependencias se pueden invertir según sea necesario.


Materiales adicionales

  1. Problema de la expresión (en inglés)

Para acceder completo a curso necesitas un plan básico

El plan básico te dará acceso completo a todos los cursos, ejercicios y lecciones de Códica, proyectos y acceso de por vida a la teoría de las lecciones completadas. La suscripción se puede cancelar en cualquier momento.

Obtener acceso
130
cursos
1000
ejercicios
2000+
horas de teoría
3200
test

Obtén acceso

Cursos de programación para principiantes y desarrolladores experimentados. Comienza tu aprendizaje de forma gratuita

  • 130 cursos, 2000+ horas de teoría
  • 1000 ejercicios prácticos en el navegador
  • 360 000 estudiantes
Al enviar el formulario, aceptas el «Política de privacidad» y los términos de la «Oferta», y también aceptas los «Términos y condiciones de uso»

Nuestros graduados trabajan en empresas como:

Bookmate
Health Samurai
Dualboot
ABBYY