Saltar al contenido
Explorar conocimiento

concepto · fundamentos · 6 min de lectura

Polimorfismo

El polimorfismo permite tratar distintos objetos mediante un mismo contrato, dejando que cada objeto ejecute su implementación específica.

Antes de leer esto, conviene conocer: Clase, Herencia.

Alcance en breve

Cubre

  • polymorphism as using different implementations through one shared contract
  • method overriding and interface-based substitution at a conceptual level
  • practical boundaries between polymorphism, conditionals, and accidental shape matching

No cubre

  • generic type theory and parametric polymorphism
  • dynamic dispatch internals and virtual method tables
  • advanced variance rules in static type systems

Supone

  • The reader understands classes, instances, methods, and basic inheritance vocabulary.

Resumen

Polimorfismo es la capacidad de usar objetos distintos a través de una misma interfaz o clase base, mientras cada objeto responde con su propia implementación. El código que llama no necesita preguntar qué tipo concreto recibió; solo necesita conocer el contrato que todos cumplen.

Importa porque reduce condicionales basados en tipo y permite extender comportamiento sin reescribir al consumidor. En vez de un if por cada variante, el programa invoca el mismo método y delega la diferencia al objeto correcto.

Alcance y supuestos

Este paquete cubre polimorfismo de subtipo a nivel práctico: varios objetos cumplen un contrato común, y una función puede usarlos de forma uniforme. Usa TypeScript para mostrar clases e interfaces, pero el concepto aplica a lenguajes orientados a objetos con sus propias reglas de tipos y despacho.

No cubre teoría de tipos, polimorfismo paramétrico, sobrecarga de operadores, tablas virtuales, variance, genéricos avanzados ni los detalles internos del runtime. Asume que ya entiendes clases, instancias, métodos y la idea básica de herencia.

Modelo mental

Piensa en enchufes eléctricos. Una lámpara, un cargador y un ventilador son aparatos distintos, pero todos respetan el mismo contrato físico: conectarse al tomacorriente y recibir energía. El tomacorriente no necesita saber si alimenta una lámpara o un ventilador; solo exige que el enchufe tenga la forma correcta.

El contrato común es lo que permite intercambiar objetos. En código, una función puede pedir algo que tenga render() o send(), y cada objeto decide cómo cumplir esa operación. La llamada es la misma; la implementación cambia según el objeto real.

La clave es no confundir polimorfismo con "cualquier cosa que tenga métodos parecidos". Para que sea útil, el contrato debe tener significado estable. Si dos objetos comparten un método llamado igual pero con reglas incompatibles, el consumidor queda engañado aunque el compilador lo acepte.

Uso práctico

Usa polimorfismo cuando varios tipos comparten una intención real y el consumidor no debería conocer sus detalles concretos:

  • PaymentMethod puede tener implementaciones CreditCardPayment y BankTransferPayment, ambas con pay(amount).
  • NotificationChannel puede tener EmailChannel y SmsChannel, ambas con send(message).
  • ❌ Un switch gigante por tipo suele indicar que el comportamiento está fuera del objeto que debería conocerlo.
  • ❌ Forzar una interfaz común entre objetos con significados distintos crea acoplamiento disfrazado de abstracción.

Ejemplo trabajado: enviar notificaciones por canales intercambiables

Supón que una aplicación debe enviar el mismo mensaje por email o SMS. Sin polimorfismo, el caso de uso tendría que preguntar por el tipo de canal y decidir qué función llamar. Con polimorfismo, el caso de uso solo conoce un contrato: cualquier canal debe saber enviar un mensaje.

interface NotificationChannel {
  send(message: string): string;
}

class EmailChannel implements NotificationChannel {
  constructor(private address: string) {}

  send(message: string) {
    return `Email to ${this.address}: ${message}`;
  }
}

class SmsChannel implements NotificationChannel {
  constructor(private phoneNumber: string) {}

  send(message: string) {
    return `SMS to ${this.phoneNumber}: ${message}`;
  }
}

NotificationChannel define el contrato. EmailChannel y SmsChannel cumplen ese contrato con implementaciones distintas. El consumidor no necesita saber cómo cada canal entrega el mensaje.

function notify(channel: NotificationChannel, message: string) {
  return channel.send(message);
}

const channels: NotificationChannel[] = [
  new EmailChannel("felipe@example.com"),
  new SmsChannel("+56912345678"),
];

for (const channel of channels) {
  console.log(notify(channel, "Tu reporte está listo"));
}

La salida esperada muestra una llamada uniforme con resultados específicos por objeto:

Email to felipe@example.com: Tu reporte está listo
SMS to +56912345678: Tu reporte está listo

La función notify no tiene un if (channelType === "email") ni un switch por variante. Solo llama send. Esa es la ventaja práctica: agregar PushChannel exige crear otra implementación del contrato, no modificar cada consumidor que manda notificaciones.

La frontera está en el contrato. Si send significa "entregar un mensaje al usuario", los canales son intercambiables. Si para un canal send significa "guardar borrador" y para otro significa "publicar inmediatamente", el nombre común oculta reglas distintas y el polimorfismo deja de ayudar.

Antes de aplicar polimorfismo, revisa cuatro preguntas:

  1. ¿Todos los tipos cumplen el mismo contrato semántico, no solo la misma firma?
  2. ¿El consumidor puede trabajar sin conocer el tipo concreto?
  3. ¿Agregar una nueva variante debería evitar cambios en el consumidor?
  4. ¿Una función simple o un objeto de configuración resolvería el caso con menos estructura?

Si las respuestas apuntan a un contrato estable, polimorfismo reduce ramificaciones y hace más explícitas las variaciones. Si el contrato es débil, primero nombra mejor la abstracción o no la crees.

Evidencia

  • Oracle Java Tutorials, Polymorphism describe el polimorfismo como la posibilidad de tratar objetos de una subclase como objetos de su superclase y obtener comportamiento específico en tiempo de ejecución.
  • TypeScript Handbook, Classes documenta clases, herencia e implementación de contratos de tipos, base práctica para expresar sustitución polimórfica en TypeScript.

Conexiones

Requisitos

Relacionados y alternativas

Relacionado

Ver grafo local

Fuentes citadas