Curso de PHP

Polimorfismo en PHP: qué es y cómo aplicarlo

Por Víctor Peña · Publicado el

Polimorfismo en PHP

Hola, ¿cómo están? Continuando con el curso de PHP, hoy veremos el polimorfismo. Es el pilar de la POO que más cuesta al principio y el que más cambia tu forma de escribir código una vez que lo entiendes.

¡Empecemos!

Qué es el polimorfismo

La palabra viene del griego: muchas formas. En programación significa que objetos distintos pueden responder al mismo mensaje, cada uno a su manera.

Dicho de otro modo: llamas al mismo método sobre objetos de tipos diferentes y cada uno hace lo que le corresponde, sin que tú tengas que preguntar de qué tipo es.

El problema que resuelve

Vamos a verlo con el caso que se repite en todos los sistemas. Imagina que calculas el precio de envío según el transportista:

<?php
function calcularEnvio(string $transportista, float $peso): float
{
    if ($transportista === "local") {
        return 15;
    } elseif ($transportista === "nacional") {
        return 25 + ($peso * 2);
    } elseif ($transportista === "internacional") {
        return 80 + ($peso * 5);
    }

    return 0;
}

Funciona. Pero ahora piensa qué pasa cuando agregues un cuarto transportista: tienes que volver a abrir esta función y modificarla. Y si el mismo if aparece en otros cinco lugares del sistema —para calcular plazos, para mostrar el logotipo, para generar la guía—, tienes que modificar los cinco.

Ese es el problema. El polimorfismo lo elimina.

La solución polimórfica

<?php
declare(strict_types=1);

abstract class Transportista
{
    abstract public function calcularEnvio(float $peso): float;
    abstract public function nombre(): string;
}

class EnvioLocal extends Transportista
{
    public function calcularEnvio(float $peso): float
    {
        return 15;
    }

    public function nombre(): string
    {
        return "Envío local";
    }
}

class EnvioNacional extends Transportista
{
    public function calcularEnvio(float $peso): float
    {
        return 25 + ($peso * 2);
    }

    public function nombre(): string
    {
        return "Envío nacional";
    }
}

class EnvioInternacional extends Transportista
{
    public function calcularEnvio(float $peso): float
    {
        return 80 + ($peso * 5);
    }

    public function nombre(): string
    {
        return "Envío internacional";
    }
}

Y ahora el código que los usa:

<?php
function mostrarCotizacion(Transportista $transportista, float $peso): void
{
    echo $transportista->nombre() . ": ";
    echo number_format($transportista->calcularEnvio($peso), 2) . "\n";
}

$opciones = [
    new EnvioLocal(),
    new EnvioNacional(),
    new EnvioInternacional(),
];

foreach ($opciones as $opcion) {
    mostrarCotizacion($opcion, 10);
}

Salida:

Envío local: 15.00
Envío nacional: 45.00
Envío internacional: 130.00

Fíjate en lo importante: no hay ni un solo if. El bucle no sabe ni le interesa qué tipo de transportista está procesando. Cada objeto sabe calcular lo suyo.

La ventaja real

Agrega ahora un transportista nuevo:

<?php
class EnvioExpress extends Transportista
{
    public function calcularEnvio(float $peso): float
    {
        return 50 + ($peso * 3.5);
    }

    public function nombre(): string
    {
        return "Envío express";
    }
}

Y eso es todo. No tocaste mostrarCotizacion(), ni el bucle, ni ninguna de las otras clases. Solo añadiste un archivo.

Esto tiene nombre: principio abierto/cerrado. El código debe estar abierto a extenderse pero cerrado a modificarse. Añadir funcionalidad no debería obligarte a editar lo que ya funciona, porque cada edición es una oportunidad de romper algo.

Polimorfismo con interfaces

La herencia no es la única vía. Las interfaces —que veremos en detalle en una lección próxima— permiten polimorfismo entre clases sin ninguna relación de parentesco:

<?php
interface Exportable
{
    public function exportar(): string;
}

class Factura implements Exportable
{
    public function exportar(): string
    {
        return "Factura en PDF";
    }
}

class ReporteVentas implements Exportable
{
    public function exportar(): string
    {
        return "Reporte en Excel";
    }
}

function descargar(Exportable $documento): void
{
    echo $documento->exportar();
}

Factura y ReporteVentas no tienen nada que ver entre sí, pero ambas cumplen el mismo contrato. Es el polimorfismo más flexible de los dos, y en la práctica el más usado.

Polimorfismo y el tipado

Aquí se entiende por qué el tipado que vimos en la lección de funciones importa tanto:

<?php
function mostrarCotizacion(Transportista $transportista, float $peso): void

Ese Transportista $transportista significa: acepto cualquier cosa que sea un transportista. PHP verifica que el objeto que le pasas cumpla, y si no, falla de inmediato con un mensaje claro.

Es lo que hace que el polimorfismo sea seguro y no un salto de fe.

Sobrescritura: polimorfismo con herencia simple

También hay polimorfismo cuando una clase hija redefine un método del padre, como vimos en herencia:

<?php
class Empleado
{
    public function calcularSalario(): float
    {
        return 4000;
    }
}

class Vendedor extends Empleado
{
    public function calcularSalario(): float
    {
        return 3000 + 2500;
    }
}

$empleados = [new Empleado(), new Vendedor()];

foreach ($empleados as $empleado) {
    echo $empleado->calcularSalario() . "\n";   // 4000, luego 5500
}

Misma llamada, resultados distintos según el objeto real.

Un aviso: no es magia

El polimorfismo no siempre es la respuesta. Un if con dos casos que no van a crecer está perfectamente bien, y convertirlo en tres clases es complicar por complicar.

La señal de que toca polimorfismo es cuando ves el mismo condicional repetido en varios lugares del código, o cuando cada nueva funcionalidad te obliga a añadir una rama más a un if que ya es largo.

Cómo reconocer la oportunidad

Busca en tu código estos patrones:

  • Un switch o elseif largo sobre un «tipo» o un «estado».
  • El mismo condicional repetido en distintas partes.
  • Funciones que reciben un parámetro $tipo y se comportan distinto según su valor.
  • Comentarios del estilo «si algún día agregamos X, hay que modificar aquí, aquí y aquí».

Los cuatro son candidatos claros.

Para cerrar

El polimorfismo es lo que permite que un sistema crezca sin volverse frágil. La idea central en una frase: en vez de preguntar de qué tipo es un objeto para decidir qué hacer, deja que cada objeto sepa qué hacer.

Si te llevas una sola prueba práctica: cuando agregar una funcionalidad nueva te obligue a modificar código que ya funcionaba, probablemente ahí falta polimorfismo.

En la siguiente lección veremos el encapsulamiento y los modificadores de acceso.

Saludos y éxitos.

Norvic Software

Desarrollamos el software que tu empresa necesita

Somos una fábrica de software en Bolivia. Construimos sistemas a medida y aplicaciones móviles, y llevamos Inteligencia Artificial a las empresas que ya tienen un sistema funcionando.

Solicitar cotizaciónVer todos los servicios

Cotización sin costo · Respuesta directa por WhatsApp