Menu
Coddy logo textTech

Polymorphisme via les interfaces

Fait partie de la section Programmation Orientée Objet du Journey PHP de Coddy — leçon 34 sur 91.

Dans la leçon précédente, vous avez vu le polymorphisme par héritage, où les classes enfants redéfinissent les méthodes parentes. Les interfaces offrent un autre moyen puissant de réaliser le polymorphisme, particulièrement lors de l'utilisation de classes non liées qui partagent un comportement commun.

Avec les interfaces, le polymorphisme provient du contrat lui-même. Toute classe implémentant une interface garantit qu'elle possède certaines méthodes, indépendamment de sa hiérarchie d'héritage :

<?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");

Sortie :

Email: Hello
SMS: Hello
Push: Hello

La fonction notify() fonctionne avec n'importe quel objet Notifiable. Ces classes ne partagent pas de classe parente - elles sont totalement indépendantes, sauf pour l'implémentation de la même interface. C'est l'avantage clé par rapport au polymorphisme basé sur l'héritage : vous pouvez regrouper des classes sans lien par leurs capacités plutôt que par leur ascendance.

Point clé : Le polymorphisme basé sur les interfaces permet d'utiliser des classes non liées de manière interchangeable en fonction d'un comportement partagé, rendant votre code plus flexible et découplé des implémentations spécifiques.

challenge icon

Défi

Facile

Construisons un système de stockage qui démontre le polymorphisme basé sur les interfaces. Vous allez créer différents backends de stockage — un système de fichiers et une base de données — qui n'ont rien en commun si ce n'est qu'ils implémentent tous deux la même interface de stockage. Une seule fonction fonctionnera avec n'importe quel type de stockage, illustrant comment les interfaces permettent le polymorphisme entre des classes complètement indépendantes.

Vous organiserez votre code sur quatre fichiers :

  • Storable.php — Définissez une interface Storable avec deux signatures de méthode : save($key, $data) et retrieve($key). Ce contrat garantit que tout système de stockage peut stocker et récupérer des données, quelle que soit la manière dont il est implémenté en interne.
  • FileStorage.php — Créez une classe FileStorage qui implémente Storable. Incluez le fichier de l'interface. La classe doit avoir une propriété privée $directory définie via le constructeur. Implémentez save($key, $data) pour retourner "Saving '$data' to file $this->directory/$key.txt". Implémentez retrieve($key) pour retourner "Reading from file $this->directory/$key.txt".
  • DatabaseStorage.php — Créez une classe DatabaseStorage qui implémente également Storable. Incluez le fichier de l'interface. La classe doit avoir une propriété privée $tableName définie via le constructeur. Implémentez save($key, $data) pour retourner "Inserting '$data' into table $this->tableName with key $key". Implémentez retrieve($key) pour retourner "Selecting from table $this->tableName where key = $key".
  • main.php — Incluez les deux fichiers de stockage. Créez une fonction appelée storeData qui accepte un paramètre Storable, une clé et des données. La fonction doit retourner le résultat de l'appel à save() avec la clé et les données. Créez une autre fonction appelée fetchData qui accepte un paramètre Storable et une clé, retournant le résultat de l'appel à retrieve().

Vous recevrez quatre entrées : un chemin de répertoire, un nom de table, une clé et des données à stocker. Créez un FileStorage avec le répertoire et un DatabaseStorage avec le nom de la table. Utilisez votre fonction storeData() pour sauvegarder les données en utilisant d'abord le stockage par fichier, puis le stockage par base de données. Affichez chaque résultat sur sa propre ligne. Utilisez ensuite fetchData() pour récupérer les données du stockage par fichier et affichez ce résultat.

Remarquez comment storeData() et fetchData() fonctionnent de manière identique avec les deux types de stockage — ils se soucient seulement que l'objet implémente Storable. Le système de fichiers et la base de données ont des implémentations internes complètement différentes, pourtant ils sont interchangeables grâce au contrat de l'interface.

Aide-mémoire

Les interfaces permettent le polymorphisme en permettant à des classes non liées d'être utilisées de manière interchangeable en fonction d'un comportement partagé plutôt que d'une hiérarchie d'héritage.

Toute classe implémentant une interface garantit qu'elle possède certaines méthodes, créant ainsi un contrat qui permet un comportement polymorphe :

<?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 fonction notify() accepte n'importe quel objet implémentant Notifiable, quelle que soit la hiérarchie des classes. Cela rend le code plus flexible et découplé des implémentations spécifiques.

Avantage clé : Le polymorphisme basé sur les interfaces regroupe des classes non liées par leurs capacités, et non par leur ascendance, permettant à des implémentations complètement différentes d'être utilisées de manière interchangeable via un contrat commun.

Essayez vous-même

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

// Lire les entrées
$directory = trim(fgets(STDIN));
$tableName = trim(fgets(STDIN));
$key = trim(fgets(STDIN));
$data = trim(fgets(STDIN));

// TODO: Créer une fonction storeData qui accepte un Storable, une clé et des données
// Elle doit retourner le résultat de l'appel à save() avec la clé et les données

// TODO: Créer une fonction fetchData qui accepte un Storable et une clé
// Elle doit retourner le résultat de l'appel à retrieve() avec la clé

// TODO: Créer une instance de FileStorage avec le répertoire

// TODO: Créer une instance de DatabaseStorage avec le nom de la table

// TODO: Utiliser storeData() pour sauvegarder les données avec le stockage par fichier, puis afficher le résultat

// TODO: Utiliser storeData() pour sauvegarder les données avec le stockage par base de données, puis afficher le résultat

// TODO: Utiliser fetchData() pour récupérer depuis le stockage par fichier, puis afficher le résultat
?>
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Programmation Orientée Objet