CON · Concepto 8 · Programación orientada a objetos

40.Polimorfismo

La capacidad de que objetos de clases distintas respondan al mismo mensaje (mismo método) cada uno a su manera.

¿Qué es?

Polimorfismo significa «muchas formas»: objetos de clases distintas responden al mismo método cada uno a su manera.

Cuando el maestro dice «¡entreguen su trabajo!», cada alumno hace algo distinto: uno entrega un ensayo, otro una maqueta, otro un video. La orden es la misma; la forma de cumplirla depende de quién la recibe. El maestro no necesita saber qué hizo cada quien para pedirlo.

En código, eso permite recorrer una lista con objetos diferentes y llamar a area() o sonido() en todos, sin preguntar de qué clase es cada uno.

Sin polimorfismo Con polimorfismo
if tipo == "circulo": … elif tipo == "rectangulo": … figura.area()
Agregar una figura nueva obliga a cambiar todos los if Basta crear una clase nueva con su area()

Sintaxis (pseudocódigo / Python)

Pseudocódigo de diseño (no ejecutable en PSeInt)

Clase Figura
	Metodo area()      // cada hija lo define
FinClase
Clase Circulo hereda de Figura
	Metodo area(): Devolver PI * radio ^ 2
FinClase
Clase Rectangulo hereda de Figura
	Metodo area(): Devolver base * altura
FinClase

Para cada figura en figuras Hacer
	Escribir figura.area()
FinPara

Python

import math

class Figura:
    def area(self):
        raise NotImplementedError("Cada figura debe definir area()")

class Circulo(Figura):
    def __init__(self, radio):
        self.radio = radio
    def area(self):
        return math.pi * self.radio ** 2

class Rectangulo(Figura):
    def __init__(self, base, altura):
        self.base, self.altura = base, altura
    def area(self):
        return self.base * self.altura

class Triangulo(Figura):
    def __init__(self, base, altura):
        self.base, self.altura = base, altura
    def area(self):
        return self.base * self.altura / 2

figuras = [Circulo(1), Rectangulo(3, 4), Triangulo(6, 2)]
for f in figuras:
    print(type(f).__name__, round(f.area(), 2))

Salida:

Circulo 3.14
Rectangulo 12
Triangulo 6.0

El ciclo no sabe qué figura es cada una: solo llama a area().

Duck typing (sin herencia)

class Pato:
    def sonido(self):
        return "Cuac"

class Robot:
    def sonido(self):
        return "Bip bip"

for cosa in [Pato(), Robot()]:
    print(cosa.sonido())

Ejemplo guiado

Problema: el sistema de avisos de la escuela debe enviar el mismo mensaje por distintos medios. Se usa una clase abstracta que obliga a cada medio a implementar enviar().

from abc import ABC, abstractmethod

class Notificador(ABC):
    @abstractmethod
    def enviar(self, mensaje):
        pass

class Correo(Notificador):
    def __init__(self, direccion):
        self.direccion = direccion
    def enviar(self, mensaje):
        return f"[Correo a {self.direccion}] {mensaje}"

class SMS(Notificador):
    def __init__(self, telefono):
        self.telefono = telefono
    def enviar(self, mensaje):
        return f"[SMS a {self.telefono}] {mensaje[:30]}"

class Pantalla(Notificador):
    def enviar(self, mensaje):
        return f"[Pantalla del vestíbulo] {mensaje.upper()}"

def avisar_a_todos(medios, mensaje):
    for medio in medios:
        print(medio.enviar(mensaje))

medios = [
    Correo("padres@escuela.mx"),
    SMS("55 0000 0000"),
    Pantalla(),
]
avisar_a_todos(medios, "Mañana no hay clases por junta de consejo técnico")

Salida esperada:

[Correo a padres@escuela.mx] Mañana no hay clases por junta de consejo técnico
[SMS a 55 0000 0000] Mañana no hay clases por junta
[Pantalla del vestíbulo] MAÑANA NO HAY CLASES POR JUNTA DE CONSEJO TÉCNICO

Para agregar un medio nuevo (por ejemplo, un grupo de mensajería) solo se crea otra clase con enviar(): avisar_a_todos no cambia.

Errores comunes

# ✗ Mala práctica: hay que modificar esta función con cada figura nueva
def area(figura):
    if type(figura).__name__ == "Circulo":
        return 3.1416 * figura.radio ** 2
    elif type(figura).__name__ == "Rectangulo":
        return figura.base * figura.altura
# ✗ Incorrecto
from abc import ABC, abstractmethod

class Notificador(ABC):
    @abstractmethod
    def enviar(self, mensaje):
        pass

class Fax(Notificador):
    pass

Fax()
TypeError: Can't instantiate abstract class Fax without an implementation for abstract method 'enviar'

Buenas prácticas

  • Programa contra la interfaz: escribe funciones que solo usen los métodos comunes.
  • Usa clases abstractas (abc) cuando quieras obligar a implementar un método.
  • Mantén la misma firma (parámetros y tipo de retorno) en todas las versiones del método.
  • Evita cadenas de if type(...): suelen indicar que falta polimorfismo.
  • Con esta ficha terminas el nivel 8: clase, atributos, encapsulamiento, herencia y polimorfismo son los cuatro pilares de la POO.

Material descargable

Aún no hay material descargable para este tema. Pronto habrá cuadernillos, manuales o laboratorios.

Referencias

© solutions_ · Fundamentos de programación · 40 Polimorfismo