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 mediante la herencia, donde las clases secundarias sobrescriben los métodos de las clases principales. Las interfaces proporcionan otra forma eficaz de lograr el polimorfismo, especialmente al trabajar con clases no relacionadas que comparten un comportamiento común.

Con las interfaces, el polimorfismo proviene del propio contrato. Cualquier clase que implementa 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 están relacionadas en absoluto, excepto por implementar la misma interfaz. Esta es la ventaja clave frente al polimorfismo basado en la herencia: puedes agrupar clases no relacionadas según sus capacidades en lugar de su ascendencia.

Punto clave: El polimorfismo basado en interfaces permite usar clases no relacionadas indistintamente en función de un comportamiento compartido, lo que hace que tu código sea más flexible y esté desacoplado de implementaciones específicas.

challenge icon

Desafío

Fácil

Construyamos un sistema de almacenamiento que demuestre el polimorfismo basado en interfaces. Crearás distintos 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 garantiza 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 mediante el constructor. Implementa save($key, $data) para que devuelva "Saving '[data]' to file [directory]/[key].txt". Implementa retrieve($key) para que devuelva "Reading from file [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 mediante el constructor. Implementa save($key, $data) para que devuelva "Inserting '[data]' into table [tableName] with key [key]". Implementa retrieve($key) para que devuelva "Selecting from table [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, y devuelva 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 y después el almacenamiento de base de datos. Imprime cada resultado en su propia línea. Después, usa fetchData() para recuperar datos del almacenamiento de archivos e imprime ese resultado.

Observa cómo storeData() y fetchData() funcionan de forma 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; aun así, son intercambiables mediante el contrato de la interfaz.

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: Crea una función storeData que acepte un Storable, key y data
// Debería devolver el resultado de llamar a save() con la key y data

// TODO: Crea una función fetchData que acepte un Storable y key
// Debería devolver el resultado de llamar a retrieve() con la key

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

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

// TODO: Usa storeData() para guardar data con el almacenamiento de archivos, luego imprime el resultado

// TODO: Usa storeData() para guardar data con el almacenamiento de base de datos, luego imprime el resultado

// TODO: Use fetchData() to retrieve from file storage, then print the result
?>
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

Practica por tu cuenta: Compilador de PHP online