JS: Polimorfismo

Teoría: Código que mata al polimorfismo

Formación de objetos

¿Es la siguiente función polimórfica?

const sayHiByEmail = (user) => {
  const sender = new EmailSender();
  // Send an email to the user
  sender.send(user.getEmail(), 'hi!');
};

Por un lado, sí, porque el usuario se pasa desde fuera y tenemos la posibilidad de reemplazarlo con un objeto de otra clase. Pero, por otro lado, dentro de la función se utiliza explícitamente la clase EmailSender y no se puede reemplazar sin reescribir el código.

Este código muestra una idea simple pero importante. El polimorfismo de subtipos es posible cuando el objeto se pasa a la función desde fuera, y no se construye directamente dentro de ella.

Honestamente, podemos crear el objeto dentro de la función, pero en ese caso, el nombre de la clase debe formarse (o obtenerse) dinámicamente. Este método lo veremos más adelante, en la lección sobre metaprogramación.

Comprobación de tipos

Otro ejemplo con truco. ¿Hay polimorfismo en el siguiente código?

const sayHi = (user) => {
  if (user instanceof User) {
    console.log(`Hello, ${user}!`);
  } else if (user instanceof Guest) {
    console.log('Hello, guest!');
  } else {
    console.log('Who are you?');
  }
};

En este ejemplo, parece que todo está bien porque el objeto se pasa desde fuera, pero hay un truco. Dentro de la función se verifica explícitamente el tipo, lo que significa que el comportamiento no lo determina el objeto, sino que la propia función decide cómo comportarse. Además, la función está fuertemente ligada a los tipos definidos dentro de ella y deberá reescribirse si cambian. Y como resultado, no hay polimorfismo de subtipos.

A veces se encuentra la comprobación de tipos y se puede usar adecuadamente para no complicar el código, pero con mayor frecuencia indica un mal diseño. Este tipo de código no se ajusta a la programación orientada a objetos (POO) en su comprensión moderna.

Para resolver la tarea anterior, hay varios enfoques:

  • Trasladar la lógica dentro de las propias clases. Entonces, el código de la función se convertirá en algo así: user.sayHi(). Con este enfoque, hay que tener cuidado, ya que es fácil crear un objeto todopoderoso (god object). Con mayor frecuencia, debemos utilizar otro enfoque.

  • Será necesario añadir una nueva interfaz en forma de métodos isUser e isGuest.

    const sayHi = (user) => {
      if (user.isUser()) {
        console.log(`Hello, ${user}!`);
      } else if (user.isGuest()) {
        console.log('Hello, guest!');
      } else {
        console.log('Who are you?');
      }
    };

Aunque no se reduce la cantidad de código, esto es polimorfismo de subtipos. El código se basa en métodos, no en tipos. Cambiar la estructura de las clases no afectará a esta función si la lógica permanece igual.

Completado

0 / 14