Polimorfismo en Java explicado: cómo funciona, ejemplos y buenas prácticas

.## 1. Qué aprenderás en este artículo

目次

1.1 Polimorfismo en Java — Explicado en una frase

En Java, polimorfismo significa:

“Tratar diferentes objetos a través del mismo tipo, mientras su comportamiento real cambia según el objeto concreto.”

En términos más simples, puedes escribir código usando una clase o interfaz padre, y luego intercambiar la implementación real sin cambiar el código que la llama.
Esta idea es una piedra angular de la programación orientada a objetos en Java.

1.2 Por qué el polimorfismo es importante

El polimorfismo no es solo un concepto teórico.
Ayuda directamente a escribir código que es:

  • Más fácil de ampliar
  • Más fácil de mantener
  • Menos frágil cuando cambian los requisitos

Situaciones típicas donde el polimorfismo brilla incluyen:

  • Características que ganarán más variaciones con el tiempo
  • Código lleno de crecientes sentencias if / switch
  • Lógica de negocio que cambia independientemente de sus llamadores

En el desarrollo Java del mundo real, el polimorfismo es una de las herramientas más efectivas para controlar la complejidad.

1.3 Por qué los principiantes suelen tener dificultades con el polimorfismo

Muchos principiantes encuentran el polimorfismo difícil al principio, principalmente porque:

  • El concepto es abstracto y no está ligado a una nueva sintaxis
  • A menudo se explica junto con la herencia y las interfaces
  • Se centra en el pensamiento de diseño, no solo en la mecánica del código

Como resultado, los estudiantes pueden “conocer el término” pero sentirse inseguros sobre cuándo y por qué usarlo.

1.4 El objetivo de este artículo

Al final de este artículo, comprenderás:

  • Qué significa realmente el polimorfismo en Java
  • Cómo la sobrescritura de métodos y el comportamiento en tiempo de ejecución trabajan juntos
  • Cuándo el polimorfismo mejora el diseño — y cuándo no
  • Cómo reemplaza la lógica condicional en aplicaciones reales

El objetivo es ayudarte a ver el polimorfismo no como un concepto difícil, sino como una herramienta de diseño natural y práctica.

2. Qué significa el polimorfismo en Java

2.1 Tratar objetos a través de un tipo padre

En el núcleo del polimorfismo en Java hay una idea simple:

Puedes tratar un objeto concreto a través de su clase o interfaz padre.

Considera el siguiente ejemplo:

Animal animal = new Dog();

Esto es lo que está sucediendo:

  • El tipo de variable es Animal
  • El objeto real es un Dog

Aunque la variable está declarada como Animal, el programa sigue funcionando correctamente.
Esto no es un truco — es una característica fundamental del sistema de tipos de Java.

2.2 La misma llamada a método, comportamiento diferente

Ahora observa esta llamada a método:

animal.speak();

El código en sí nunca cambia.
Sin embargo, el comportamiento depende del objeto real almacenado en animal.

  • Si animal se refiere a un Dog → se ejecuta la implementación del perro
  • Si animal se refiere a un Cat → se ejecuta la implementación del gato

Por eso se llama polimorfismo
una interfaz, muchas formas de comportamiento.

2.3 Por qué usar un tipo padre es tan importante

Podrías preguntarte:

“¿Por qué no usar simplemente Dog en lugar de Animal en todas partes?”

Usar el tipo padre te brinda ventajas poderosas:

  • El código que llama no depende de clases concretas
  • Se pueden añadir nuevas implementaciones sin modificar el código existente
  • El código se vuelve más fácil de reutilizar y probar

Por ejemplo:

public void makeAnimalSpeak(Animal animal) {
    animal.speak();
}

Este método funciona para:

  • Dog
  • Cat
  • Cualquier clase de animal futura

El llamador solo se preocupa por lo que el objeto puede hacer, no por lo que es.

2.4 Relación entre polimorfismo y sobrescritura de métodos

El polimorfismo a menudo se confunde con la sobrescritura de métodos, así que aclaremos.

  • Sobrescritura de métodos → Una subclase proporciona su propia implementación de un método del padre
  • Polimorfismo → Llamar al método sobrescrito a través de una referencia de tipo padre

La sobrescritura permite el polimorfismo, pero el polimorfismo es el principio de diseño que lo utiliza.

2.5 No es una sintaxis nueva — es un concepto de diseño

.El polimorfismo no introduce nuevas palabras clave de Java ni sintaxis especial.

  • Ya usas class, extends y implements
  • Ya llamas a los métodos de la misma manera

Lo que cambia es cómo piensas sobre la interacción de objetos.

En lugar de escribir código que depende de clases concretas, diseñas código que depende de abstracciones.

2.6 Conclusión clave de esta sección

Para resumir:

  • El polimorfismo permite que los objetos se usen a través de un tipo común
  • El comportamiento real se determina en tiempo de ejecución
  • El llamador no necesita conocer los detalles de implementación

En la siguiente sección, exploraremos por qué Java puede hacer esto, analizando en detalle la sobrescritura de métodos y el enlace dinámico.

3. El mecanismo central: sobrescritura de métodos y enlace dinámico

3.1 Qué se decide en tiempo de compilación vs tiempo de ejecución

Para comprender realmente el polimorfismo en Java, debes separar el comportamiento en tiempo de compilación del comportamiento en tiempo de ejecución.

Java toma dos decisiones diferentes en dos etapas distintas:

  • Tiempo de compilación → Verifica si una llamada a método es válida para el tipo de la variable
  • Tiempo de ejecución → Decide qué implementación del método se ejecuta realmente

Esta separación es la base del polimorfismo.

3.2 La disponibilidad de métodos se verifica en tiempo de compilación

Considera este código nuevamente:

Animal animal = new Dog();
animal.speak();

En tiempo de compilación, el compilador de Java solo mira:

  • El tipo declarado : Animal

Si Animal define un método speak(), la llamada se considera válida. El compilador no le importa qué objeto concreto se asignará más tarde.

Esto significa:

  • Solo puedes llamar a métodos que existan en el tipo padre
  • El compilador no “adivina” el comportamiento de la subclase

3.3 El método real se elige en tiempo de ejecución

Cuando el programa se ejecuta, Java evalúa:

  • A qué objeto animal se refiere realmente
  • Si esa clase sobrescribe el método llamado

Si Dog sobrescribe speak(), entonces se ejecuta la implementación de Dog, no la de Animal.

Esta selección de método en tiempo de ejecución se llama enlace dinámico (o despacho dinámico).

3.4 Por qué el enlace dinámico permite el polimorfismo

Sin enlace dinámico, el polimorfismo no existiría.

Si Java siempre llamara a los métodos basándose en el tipo declarado de la variable, este código no tendría sentido:

Animal animal = new Dog();

El enlace dinámico permite a Java:

  • Retrasar la decisión del método hasta tiempo de ejecución
  • Ajustar el comportamiento al objeto real

En resumen:

  • La sobrescritura define la variación
  • El enlace dinámico la activa

Juntos, hacen posible el polimorfismo.

3.5 Por qué los métodos y campos static son diferentes

Una fuente común de confusión son los miembros static.

Regla importante:

  • Los métodos y campos estáticos NO participan en el polimorfismo

¿Por qué?

  • Pertenecen a la clase, no al objeto
  • Se resuelven en tiempo de compilación, no en tiempo de ejecución

Esto significa:

Animal animal = new Dog();
animal.staticMethod(); // resolved using Animal, not Dog

La selección del método es fija y no cambia según el objeto real.

3.6 Confusión común de principiantes — aclarada

Resumamos claramente las reglas clave:

  • ¿Puedo llamar a este método? → Verificado usando el tipo declarado (tiempo de compilación)
  • ¿Qué implementación se ejecuta? → Decidido por el objeto real (tiempo de ejecución)
  • ¿Qué soporta el polimorfismo? → Solo los métodos de instancia sobrescritos

Una vez que esta distinción está clara, el polimorfismo deja de parecer misterioso.

3.7 Resumen de la sección

  • Java valida las llamadas a métodos usando el tipo de la variable
  • El tiempo de ejecución elige el método sobrescrito según el objeto
  • Este mecanismo se llama enlace dinámico
  • Los miembros estáticos no son polimórficos

En la siguiente sección, veremos cómo escribir código polimórfico usando herencia (extends), con ejemplos concretos.

4. Escribir código polimórfico usando herencia (extends)

4.1 El patrón básico de herencia

final answer.Comencemos con la forma más directa de implementar polimorfismo en Java: herencia de clases.

class Animal {
    public void speak() {
        System.out.println("Some sound");
    }
}

class Dog extends Animal {
    @Override
    public void speak() {
        System.out.println("Woof");
    }
}

class Cat extends Animal {
    @Override
    public void speak() {
        System.out.println("Meow");
    }
}

Aquí:

  • Animal define un comportamiento común
  • Cada subclase sobrescribe ese comportamiento con su propia implementación

Esta estructura es la base del polimorfismo mediante herencia.

4.2 Usar el Tipo Padre es la Clave

Ahora observa el código que llama:

Animal a1 = new Dog();
Animal a2 = new Cat();

a1.speak();
a2.speak();

Aunque ambas variables son del tipo Animal,
Java ejecuta la implementación correcta según el objeto real.

  • Dog"Woof"
  • Cat"Meow"

El código que llama no necesita saber — ni preocuparse — por la clase concreta.

4.3 ¿Por Qué No Usar Directamente el Tipo Subclase?

Los principiantes a menudo escriben código así:

Dog dog = new Dog();
dog.speak();

Esto funciona, pero limita la flexibilidad.

Si más adelante introduces otro tipo de animal, deberás:

  • Cambiar las declaraciones de variables
  • Actualizar los parámetros de los métodos
  • Modificar las colecciones

Usar el tipo padre evita esos cambios:

List<Animal> animals = List.of(new Dog(), new Cat());

La estructura se mantiene igual aunque se añadan nuevas subclases.

4.4 ¿Qué Debe Ir en la Clase Padre?

Al diseñar polimorfismo basado en herencia, la clase padre debe contener:

  • Comportamiento compartido por todas las subclases
  • Métodos que tengan sentido sin importar el tipo concreto

Evita colocar comportamiento en la clase padre que solo se aplique a algunas subclases.
Eso suele indicar un problema de diseño.

Una buena regla práctica:

Si tratar un objeto como del tipo padre se siente “incorrecto”, la abstracción es errónea.

4.5 Uso de Clases Abstractas

A veces la clase padre no debería tener ninguna implementación por defecto.
En esos casos, usa una clase abstracta.

abstract class Animal {
    public abstract void speak();
}

Esto impone reglas:

  • Las subclases deben implementar speak()
  • La clase padre no puede instanciarse

Las clases abstractas son útiles cuando deseas forzar un contrato, no proporcionar comportamiento.

4.6 Los Inconvenientes de la Herencia

La herencia es poderosa, pero tiene contrapartes:

  • Acoplamiento fuerte entre padre e hijo
  • Jerarquías de clases rígidas
  • Refactorizaciones más difíciles más adelante

Por estas razones, muchos diseños modernos en Java prefieren interfaces sobre la herencia.

4.7 Resumen de la Sección

  • La herencia permite el polimorfismo mediante la sobrescritura de métodos
  • Siempre interactúa a través del tipo padre
  • Las clases abstractas obligan a implementar el comportamiento requerido
  • La herencia debe usarse con cautela

A continuación, exploraremos polimorfismo usando interfaces, que suele ser el enfoque preferido en proyectos Java del mundo real.

5. Escribiendo Código Polimórfico Usando Interfaces (implements)

5.1 Las Interfaces Representan “Qué Puede Hacer un Objeto”

En el desarrollo Java real, las interfaces son la forma más común de implementar polimorfismo.

Una interfaz representa una capacidad o rol, no una identidad.

interface Speaker {
    void speak();
}

En este punto no hay implementación — solo un contrato.
Cualquier clase que implemente esta interfaz promete proporcionar ese comportamiento.

5.2 Definiendo el Comportamiento en las Clases Implementadoras

Ahora implementemos la interfaz:

class Dog implements Speaker {
    @Override
    public void speak() {
        System.out.println("Woof");
    }
}

class Cat implements Speaker {
    @Override
    public void speak() {
        System.out.println("Meow");
    }
}

Estas clases no comparten una relación padre‑hijo.
Sin embargo, pueden tratarse de forma uniforme a través de la interfaz Speaker.

final.### 5.3 Usar el Tipo de Interfaz en el Código Llamador

El poder de las interfaces se vuelve evidente en el lado del llamador:

Speaker s1 = new Dog();
Speaker s2 = new Cat();

s1.speak();
s2.speak();

El código llamador:

  • Depende solo de la interfaz
  • No tiene conocimiento de implementaciones concretas
  • Funciona sin cambios cuando se añaden nuevas implementaciones

Esto es verdadero polimorfismo en la práctica.

5.4 Por Qué se Prefieren las Interfaces en la Práctica

Las interfaces a menudo se prefieren sobre la herencia porque proporcionan:

  • Acoplamiento suelto
  • Flexibilidad entre clases no relacionadas
  • Soporte para múltiples implementaciones

Una clase puede implementar múltiples interfaces, pero solo puede extender una clase.
Esto hace que las interfaces sean ideales para diseñar sistemas extensibles.

5.5 Ejemplo del Mundo Real: Comportamiento Intercambiable

Las interfaces brillan en escenarios donde el comportamiento puede cambiar o ampliarse:

  • Métodos de pago
  • Canales de notificación
  • Estrategias de almacenamiento de datos
  • Mecanismos de registro

Ejemplo:

public void notifyUser(Notifier notifier) {
    notifier.send();
}

Puedes añadir nuevos métodos de notificación sin modificar este método.

5.6 Interfaz vs Clase Abstracta — Cómo Elegir

Si no estás seguro de cuál usar, sigue esta guía:

  • Usa una interfaz cuando te importa el comportamiento
  • Usa una clase abstracta cuando deseas estado o implementación compartida

En la mayoría de los diseños modernos de Java, comenzar con una interfaz es la opción más segura.

5.7 Resumen de la Sección

  • Las interfaces definen contratos de comportamiento
  • Permiten un polimorfismo flexible y de acoplamiento suelto
  • El código llamador depende de abstracciones, no de implementaciones
  • Las interfaces son la elección predeterminada en el diseño Java profesional

A continuación, examinaremos una trampa común: usar instanceof y downcasting, y por qué a menudo indican un problema de diseño.

6. Trampas Comunes: instanceof y Downcasting

6.1 Por Qué los Desarrolladores Recurren a instanceof

Al aprender polimorfismo, muchos desarrolladores eventualmente escriben código como este:

if (speaker instanceof Dog) {
    Dog dog = (Dog) speaker;
    dog.fetch();
}

Esto suele ocurrir porque:

  • Una subclase tiene un método que no está declarado en la interfaz
  • El comportamiento necesita diferir según la clase concreta
  • Se añadieron requisitos después del diseño original

Querer “comprobar el tipo real” es un instinto natural, pero a menudo indica un problema más profundo.

6.2 Qué Sale Mal Cuando instanceof se Propaga

Usar instanceof ocasionalmente no es inherentemente incorrecto.
El problema surge cuando se convierte en el principal mecanismo de control.

if (speaker instanceof Dog) {
    ...
} else if (speaker instanceof Cat) {
    ...
} else if (speaker instanceof Bird) {
    ...
}

Este patrón conduce a:

  • Código que debe cambiar cada vez que se añade una nueva clase
  • Lógica centralizada en el llamador en lugar del objeto
  • Pérdida del beneficio central del polimorfismo

En ese punto, el polimorfismo se elude efectivamente.

6.3 El Riesgo del Downcasting

El downcasting convierte un tipo padre en un subtipo específico:

Animal animal = new Dog();
Dog dog = (Dog) animal;

Esto funciona solo si la suposición es correcta.

Si el objeto no es realmente un Dog, el código falla en tiempo de ejecución con una ClassCastException.

El downcasting:

  • Empuja errores del tiempo de compilación al tiempo de ejecución
  • Hace suposiciones sobre la identidad del objeto
  • Aumenta la fragilidad

6.4 ¿Puede Esto Resolverse con Polimorfismo?

Antes de usar instanceof, pregúntate:

  • ¿Puede expresarse este comportamiento como un método?
  • ¿Puede la interfaz ampliarse en su lugar?
  • ¿Puede la responsabilidad trasladarse a la propia clase?

Por ejemplo, en lugar de comprobar tipos:

speaker.performAction();

Deja que cada clase decida cómo realizar esa acción.

6.5 Cuándo instanceof es Aceptable

Hay casos donde instanceof es razonable:

  • Integración con bibliotecas externas
  • Código heredado que no puedes rediseñar
  • Capas de frontera (adaptadores, serializadores)

La regla clave:

Mantén instanceof en los bordes, no en la lógica central.

6.6 Guía práctica

  • Evita instanceof en la lógica de negocio
  • Evita diseños que requieran downcasting frecuente
  • Si sientes que estás obligado a usarlos, reconsidera la abstracción

A continuación, veremos cómo el polimorfismo puede reemplazar la lógica condicional (if / switch) de manera limpia y escalable.

7. Reemplazando sentencias if / switch con Polimorfismo

7.1 Un olor de código condicional común

Considera este ejemplo típico:

public void processPayment(String type) {
    if ("credit".equals(type)) {
        // Credit card payment
    } else if ("bank".equals(type)) {
        // Bank transfer
    } else if ("paypal".equals(type)) {
        // PayPal payment
    }
}

A primera vista, este código parece correcto.
Sin embargo, a medida que aumenta el número de tipos de pago, también lo hace la complejidad.

7.2 Aplicando Polimorfismo en su lugar

Podemos refactorizar esto usando polimorfismo.

interface Payment {
    void pay();
}
class CreditPayment implements Payment {
    @Override
    public void pay() {
        // Credit card payment
    }
}

class BankPayment implements Payment {
    @Override
    public void pay() {
        // Bank transfer
    }
}

Código de llamada:

public void processPayment(Payment payment) {
    payment.pay();
}

Ahora, agregar un nuevo tipo de pago no requiere cambios en este método.

7.3 Por qué este enfoque es mejor

Este diseño ofrece varios beneficios:

  • La lógica condicional desaparece
  • Cada clase posee su propio comportamiento
  • Se pueden añadir nuevas implementaciones de forma segura

El sistema se vuelve abierto a la extensión, cerrado a la modificación.

7.4 Cuándo no reemplazar condicionales

El polimorfismo no siempre es la elección correcta.

Evita sobreutilizarlo cuando:

  • El número de casos es pequeño y fijo
  • Las diferencias de comportamiento son triviales
  • Las clases adicionales reducen la claridad

Los condicionales simples suelen ser más claros para lógica sencilla.

7.5 Cómo decidir en la práctica

Pregúntate:

  • ¿Esta rama crecerá con el tiempo?
  • ¿Otros añadirán nuevos casos?
  • ¿Los cambios afectan a muchos lugares?

Si la respuesta es “sí”, el polimorfismo probablemente sea la mejor opción.

7.6 La refactorización incremental es la mejor

No necesitas un diseño perfecto desde el principio.

  • Comienza con condicionales
  • Refactoriza cuando la complejidad aumente
  • Deja que el código evolucione de forma natural

Este enfoque mantiene el desarrollo práctico y mantenible.

A continuación, discutiremos cuándo se debe usar el polimorfismo — y cuándo no — en proyectos del mundo real.

8. Directrices prácticas: Cuándo usar Polimorfismo — y cuándo no

8.1 Señales de que el polimorfismo es adecuado

El polimorfismo es más valioso cuando se espera un cambio.
Deberías considerarlo seriamente cuando:

  • Es probable que aumente el número de variaciones
  • El comportamiento cambia independientemente del llamador
  • Quieres mantener estable el código que llama
  • Diferentes implementaciones comparten el mismo rol

En estos casos, el polimorfismo te ayuda a localizar el cambio y reducir los efectos en cadena.

8.2 Señales de que el polimorfismo es excesivo

El polimorfismo no es gratuito. Introduce más tipos e indireccionamiento.

Evítalo cuando:

  • El número de casos es fijo y pequeño
  • La lógica es corta y poco probable que cambie
  • Las clases extra perjudicarían la legibilidad

8.3 Evita diseñar para un futuro imaginario

Un error común de principiantes es añadir polimorfismo de forma preventiva:

“Podríamos necesitar esto más tarde.”

En la práctica:

  • Los requisitos a menudo cambian de maneras inesperadas
  • Muchas extensiones previstas nunca ocurren

Generalmente es mejor comenzar de forma simple y refactorizar cuando aparecen necesidades reales.

8.4 Una visión práctica del Principio de Sustitución de Liskov (LSP)

Puedes encontrarte con el Principio de Sustitución de Liskov (LSP) al estudiar POO.

Una forma práctica de entenderlo es:

.> “Si reemplazo un objeto por uno de sus subtipos, nada debería romperse.”

Si usar un subtipo genera sorpresas, excepciones o manejo especial,
la abstracción probablemente está equivocada.

8.5 Haz la Pregunta de Diseño Correcta

Cuando no estés seguro, pregúntate:

  • ¿Necesita el llamador saber qué implementación es?
  • ¿O solo qué comportamiento proporciona?

Si solo el comportamiento es suficiente, el polimorfismo suele ser la elección adecuada.

8.6 Resumen de la Sección

  • El polimorfismo es una herramienta para gestionar el cambio
  • Úsalo donde se espere variación
  • Evita la abstracción prematura
  • Refactoriza hacia el polimorfismo cuando sea necesario

A continuación, concluiremos el artículo con un resumen claro y una sección de preguntas frecuentes.

9. Resumen: Puntos Clave sobre el Polimorfismo en Java

9.1 La Idea Central

En esencia, el polimorfismo en Java se basa en un principio sencillo:

El código debe depender de abstracciones, no de implementaciones concretas.

Al interactuar con objetos a través de clases padre o interfaces,
permites que el comportamiento cambie sin reescribir el código que los llama.

9.2 Lo Que Debes Recordar

Estos son los puntos más importantes del artículo:

  • El polimorfismo es un concepto de diseño, no una sintaxis nueva
  • Se implementa mediante la sobrescritura de métodos y el enlace dinámico
  • Los tipos padre definen qué se puede invocar
  • El comportamiento real se decide en tiempo de ejecución
  • Las interfaces suelen ser el enfoque preferido
  • instanceof y el downcasting deben usarse con moderación
  • El polimorfismo ayuda a reemplazar lógica condicional creciente

9.3 Una Ruta de Aprendizaje para Principiantes

Si aún estás construyendo intuición, sigue esta progresión:

  1. Familiarízate con el uso de tipos de interfaz
  2. Observa cómo se comportan los métodos sobrescritos en tiempo de ejecución
  3. Comprende por qué las condicionales se vuelven más difíciles de mantener
  4. Refactoriza hacia el polimorfismo cuando la complejidad aumente

Con práctica, el polimorfismo se vuelve una elección de diseño natural en lugar de un “concepto difícil”.

10. Preguntas Frecuentes: Preguntas Comunes sobre el Polimorfismo en Java

10.1 ¿Cuál es la diferencia entre polimorfismo y sobrescritura de métodos?

La sobrescritura de métodos es un mecanismo: redefinir un método en una subclase.
El polimorfismo es el principio que permite que los métodos sobrescritos se llamen a través de una referencia de tipo padre.

10.2 ¿Se considera la sobrecarga de métodos como polimorfismo en Java?

En la mayoría de los contextos de Java, no.
La sobrecarga se resuelve en tiempo de compilación, mientras que el polimorfismo depende del comportamiento en tiempo de ejecución.

10.3 ¿Por qué debería usar interfaces o tipos padre?

Porque ellos:

  • Reducen el acoplamiento
  • Mejoran la extensibilidad
  • Estabilizan el código que llama

Tu código se vuelve más fácil de mantener a medida que evolucionan los requisitos.

10.4 ¿Es siempre malo usar instanceof?

No, pero debe limitarse.

Es aceptable en:

  • Capas de frontera
  • Sistemas heredados
  • Puntos de integración

Evita usarlo en la lógica de negocio central.

10.5 ¿Cuándo debería elegir una clase abstracta en lugar de una interfaz?

Usa una clase abstracta cuando:

  • Necesites estado o implementación compartida
  • Exista una relación “es-un” fuerte

Usa interfaces cuando el comportamiento y la flexibilidad sean más importantes.

10.6 ¿El polimorfismo afecta al rendimiento?

En aplicaciones empresariales típicas, las diferencias de rendimiento son insignificantes.

La legibilidad, mantenibilidad y corrección son mucho más importantes.

10.7 ¿Debo reemplazar cada if o switch por polimorfismo?

No.

Utiliza el polimorfismo cuando se espere variación y esta esté creciendo.
Mantén las condicionales cuando la lógica sea simple y estable.

10.8 ¿Cuáles son buenos ejemplos de práctica?

Escenarios de buena práctica incluyen:

  • Procesamiento de pagos
  • Sistemas de notificaciones
  • Exportadores de formatos de archivo
  • Estrategias de registro (logging)

Donde sea necesario intercambiar comportamientos, el polimorfismo encaja de forma natural.