Menu
Coddy logo textTech

Polimorfismo mediante interfaces

Parte de la sección Programación Orientada a Objetos del Journey de PHP de Coddy — lección 34 de 91.

En la lección anterior, viste el polimorfismo a través de la herencia, donde las clases hijas sobrescriben los métodos del padre. Las interfaces proporcionan otra forma poderosa de lograr el polimorfismo, especialmente cuando se trabaja con clases no relacionadas que comparten un comportamiento común.

Con las interfaces, el polimorfismo proviene del propio contrato. Cualquier clase que implemente una interfaz garantiza que tiene ciertos métodos, independientemente de su jerarquía de herencia:

<?php
interface Notifiable {
    public function send(string $message);
}

class EmailNotifier implements Notifiable {
    public function send(string $message) {
        return "Email: " . $message;
    }
}

class SMSNotifier implements Notifiable {
    public function send(string $message) {
        return "SMS: " . $message;
    }
}

class PushNotifier implements Notifiable {
    public function send(string $message) {
        return "Push: " . $message;
    }
}

function notify(Notifiable $notifier, string $message) {
    return $notifier->send($message);
}

echo notify(new EmailNotifier(), "Hello") . "\n";
echo notify(new SMSNotifier(), "Hello") . "\n";
echo notify(new PushNotifier(), "Hello");

Salida:

Email: Hello
SMS: Hello
Push: Hello

La función notify() funciona con cualquier objeto Notifiable. Estas clases no comparten una clase padre; no tienen ninguna relación entre sí, excepto por el hecho de implementar la misma interfaz. Esta es la ventaja clave sobre el polimorfismo basado en la herencia: puedes agrupar clases no relacionadas por sus capacidades en lugar de por su ascendencia.

Punto clave: El polimorfismo basado en interfaces permite que clases no relacionadas se utilicen de forma intercambiable basándose en un comportamiento compartido, lo que hace que su código sea más flexible y esté desacoplado de implementaciones específicas.

challenge icon

Desafío

Fácil

Vamos a construir un sistema de almacenamiento que demuestre el polimorfismo basado en interfaces. Crearás diferentes backends de almacenamiento — un sistema de archivos y una base de datos — que no tienen nada en común excepto que ambos implementan la misma interfaz de almacenamiento. Una sola función funcionará con cualquier tipo de almacenamiento, mostrando cómo las interfaces permiten el polimorfismo entre clases completamente no relacionadas.

Organizarás tu código en cuatro archivos:

  • Storable.php — Define una interfaz Storable con dos firmas de método: save($key, $data) y retrieve($key). Este contrato asegura que cualquier sistema de almacenamiento pueda guardar y recuperar datos, independientemente de cómo esté implementado internamente.
  • FileStorage.php — Crea una clase FileStorage que implemente Storable. Incluye el archivo de la interfaz. La clase debe tener una propiedad privada $directory establecida a través del constructor. Implementa save($key, $data) para que devuelva "Saving '$data' to file $this->directory/$key.txt". Implementa retrieve($key) para que devuelva "Reading from file $this->directory/$key.txt".
  • DatabaseStorage.php — Crea una clase DatabaseStorage que también implemente Storable. Incluye el archivo de la interfaz. La clase debe tener una propiedad privada $tableName establecida a través del constructor. Implementa save($key, $data) para que devuelva "Inserting '$data' into table $this->tableName with key $key". Implementa retrieve($key) para que devuelva "Selecting from table $this->tableName where key = $key".
  • main.php — Incluye ambos archivos de almacenamiento. Crea una función llamada storeData que acepte un parámetro Storable, una clave y datos. La función debe devolver el resultado de llamar a save() con la clave y los datos. Crea otra función llamada fetchData que acepte un parámetro Storable y una clave, devolviendo el resultado de llamar a retrieve().

Recibirás cuatro entradas: una ruta de directorio, un nombre de tabla, una clave y algunos datos para almacenar. Crea un FileStorage con el directorio y un DatabaseStorage con el nombre de la tabla. Usa tu función storeData() para guardar los datos usando primero el almacenamiento de archivos, luego el almacenamiento de base de datos. Imprime cada resultado en su propia línea. Luego usa fetchData() para recuperar del almacenamiento de archivos e imprime ese resultado.

Observa cómo storeData() y fetchData() funcionan de manera idéntica con ambos tipos de almacenamiento — solo les importa que el objeto implemente Storable. El sistema de archivos y la base de datos tienen implementaciones internas completamente diferentes, sin embargo, son intercambiables a través del contrato de la interfaz.

Hoja de referencia

Las interfaces permiten el polimorfismo al permitir que clases no relacionadas se utilicen indistintamente basándose en un comportamiento compartido en lugar de en la jerarquía de herencia.

Cualquier clase que implemente una interfaz garantiza que tiene ciertos métodos, creando un contrato que permite el comportamiento polimórfico:

<?php
interface Notifiable {
    public function send(string $message);
}

class EmailNotifier implements Notifiable {
    public function send(string $message) {
        return "Email: " . $message;
    }
}

class SMSNotifier implements Notifiable {
    public function send(string $message) {
        return "SMS: " . $message;
    }
}

function notify(Notifiable $notifier, string $message) {
    return $notifier->send($message);
}

echo notify(new EmailNotifier(), "Hello");
echo notify(new SMSNotifier(), "Hello");

La función notify() acepta cualquier objeto que implemente Notifiable, independientemente de la jerarquía de clases. Esto hace que el código sea más flexible y esté desacoplado de implementaciones específicas.

Ventaja clave: El polimorfismo basado en interfaces agrupa clases no relacionadas por sus capacidades, no por su ascendencia, permitiendo que implementaciones completamente diferentes se utilicen indistintamente a través de un contrato común.

Pruébalo tú mismo

<?php
require_once 'FileStorage.php';
require_once 'DatabaseStorage.php';

// Leer entradas
$directory = trim(fgets(STDIN));
$tableName = trim(fgets(STDIN));
$key = trim(fgets(STDIN));
$data = trim(fgets(STDIN));

// TODO: Crear una función storeData que acepte un Storable, una clave y datos
// Debe devolver el resultado de llamar a save() con la clave y los datos

// TODO: Crear una función fetchData que acepte un Storable y una clave
// Debe devolver el resultado de llamar a retrieve() con la clave

// TODO: Crear una instancia de FileStorage con el directorio

// TODO: Crear una instancia de DatabaseStorage con el nombre de la tabla

// TODO: Usar storeData() para guardar datos con el almacenamiento de archivos, luego imprimir el resultado

// TODO: Usar storeData() para guardar datos con el almacenamiento de base de datos, luego imprimir el resultado

// TODO: Usar fetchData() para recuperar del almacenamiento de archivos, luego imprimir el resultado
?>
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