Menu
Coddy logo textTech

Polimorfismo mediante interfaces

Parte de la sección Programación Orientada a Objetos del Journey de Dart de Coddy — lección 58 de 110.

En la lección anterior, viste el polimorfismo a través de la herencia: usar un tipo de clase padre para trabajar con objetos hijos. Pero el polimorfismo también funciona a través de interfaces, y este enfoque suele ser más flexible.

Cuando las clases implementan la misma interfaz, puedes tratarlas polimórficamente incluso si no tienen una relación de herencia:

abstract class Printable {
  void printDetails();
}

class Invoice implements Printable {
  @override
  void printDetails() => print('Invoice #1234');
}

class Report implements Printable {
  @override
  void printDetails() => print('Monthly Report');
}

class Receipt implements Printable {
  @override
  void printDetails() => print('Receipt for purchase');
}

void printAll(List<Printable> items) {
  for (var item in items) {
    item.printDetails();
  }
}

void main() {
  var documents = [Invoice(), Report(), Receipt()];
  printAll(documents);
}

Invoice, Report y Receipt son clases completamente independientes; no comparten un padre. Sin embargo, como todas implementan Printable, podemos almacenarlas en la misma lista y llamar a printDetails() en cada una de ellas.

Esto es potente porque te permite definir contratos de comportamiento sin forzar una jerarquía de herencia. Cualquier clase puede implementar una interfaz, independientemente de lo que extienda. Tu código se vuelve más modular ya que puedes añadir nuevos tipos imprimibles sin modificar el código existente; simplemente implementa la interfaz.

challenge icon

Desafío

Fácil

Vamos a construir un sistema de reproductor de medios que demuestre el polimorfismo a través de interfaces. Crearás una interfaz que defina lo que significa ser "reproducible" (playable), luego implementarás varios tipos de medios no relacionados que cumplan con este contrato, permitiendo que sean manejados de manera uniforme a pesar de no tener una relación de herencia.

Organizarás tu código en dos archivos:

  • media.dart: Define tu interfaz y las clases de medios aquí:
    • Una clase abstracta Playable con dos métodos abstractos: play() y String getMediaType()
    • Una clase Song que implemente Playable. Debe tener un String title y un String artist. Su método play() imprime Playing song: [title] by [artist] y getMediaType() devuelve 'Audio'
    • Una clase Video que implemente Playable. Debe tener un String title y un int duration (en minutos). Su método play() imprime Playing video: [title] ([duration] min) y getMediaType() devuelve 'Video'
    • Una clase Podcast que implemente Playable. Debe tener un String title y un int episode. Su método play() imprime Playing podcast: [title] - Episode [episode] y getMediaType() devuelve 'Podcast'
    También crea una función playAll(List<Playable> playlist) que itere a través de la lista y para cada elemento imprima [Media Type]: seguido de la llamada a play() en ese elemento.
  • main.dart: Importa tu archivo de medios y demuestra el polimorfismo basado en interfaces:
    • Crea una List<Playable> que contenga una Song con el título 'Imagine' y el artista 'John Lennon', un Video con el título 'Dart Tutorial' y una duración de 15, y un Podcast con el título 'Tech Talk' y el episodio 42
    • Pasa esta lista a playAll()

Observa cómo Song, Video y Podcast no comparten ninguna clase padre; son tipos completamente no relacionados. Sin embargo, debido a que todos implementan Playable, puedes almacenarlos en la misma lista y tratarlos de manera uniforme a través de la interfaz.

Salida esperada:

Audio:
Playing song: Imagine by John Lennon
Video:
Playing video: Dart Tutorial (15 min)
Podcast:
Playing podcast: Tech Talk - Episode 42

Hoja de referencia

El polimorfismo funciona a través de interfaces, lo que permite que clases no relacionadas sean tratadas de manera uniforme sin necesidad de relaciones de herencia.

Las clases que implementan la misma interfaz pueden utilizarse de forma polimórfica:

abstract class Printable {
  void printDetails();
}

class Invoice implements Printable {
  @override
  void printDetails() => print('Invoice #1234');
}

class Report implements Printable {
  @override
  void printDetails() => print('Monthly Report');
}

void printAll(List<Printable> items) {
  for (var item in items) {
    item.printDetails();
  }
}

void main() {
  var documents = [Invoice(), Report()];
  printAll(documents);
}

El polimorfismo basado en interfaces le permite:

  • Definir contratos de comportamiento sin forzar jerarquías de herencia
  • Almacenar tipos no relacionados en la misma colección
  • Agregar nuevos tipos sin modificar el código existente
  • Hacer que el código sea más modular y flexible

Pruébalo tú mismo

import 'media.dart';

void main() {
  // TODO: Crear una List<Playable> que contenga:
  // - Una Song con el título 'Imagine' y el artista 'John Lennon'
  // - Un Video con el título 'Dart Tutorial' y una duración de 15
  // - Un Podcast con el título 'Tech Talk' y el episodio 42
  
  // TODO: Pasar la lista a playAll()
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos