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, tu as découvert le polymorphisme par héritage, où les classes enfants redéfinissent les méthodes parentes. Les interfaces offrent une autre manière puissante de mettre en œuvre le polymorphisme, en particulier lorsque tu travailles avec des classes sans lien qui partagent un comportement commun.
Avec les interfaces, le polymorphisme découle du contrat lui-même. Toute classe qui implémente une interface garantit qu’elle possède certaines méthodes, quelle que soit 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: HelloLa fonction notify() fonctionne avec n’importe quel objet Notifiable. Ces classes ne partagent pas de classe parente : elles sont complètement indépendantes, à l’exception du fait qu’elles implémentent la même interface. C’est le principal avantage par rapport au polymorphisme fondé sur l’héritage : vous pouvez regrouper des classes indépendantes selon leurs capacités plutôt que selon leur ascendance.
Point clé : Le polymorphisme fondé sur les interfaces permet d’utiliser des classes sans lien entre elles de manière interchangeable en fonction d’un comportement commun, ce qui rend votre code plus flexible et moins couplé à des implémentations spécifiques.
Défi
FacileConstruisons un système de stockage qui démontre le polymorphisme basé sur les interfaces. Tu vas créer différents systèmes 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, montrant ainsi comment les interfaces permettent le polymorphisme entre des classes totalement indépendantes.
Tu organiseras ton code sur quatre fichiers :
Storable.php: définis une interfaceStorableavec deux signatures de méthodes :save($key, $data)etretrieve($key). Ce contrat garantit que tout système de stockage peut enregistrer et récupérer des données, quelle que soit son implémentation interne.FileStorage.php: crée une classeFileStoragequi implémenteStorable. Inclus le fichier de l'interface. La classe doit avoir une propriété privée$directorydéfinie par le constructeur. Implémentesave($key, $data)pour retourner"Saving '[data]' to file [directory]/[key].txt". Implémenteretrieve($key)pour retourner"Reading from file [directory]/[key].txt".DatabaseStorage.php: crée une classeDatabaseStoragequi implémente égalementStorable. Inclus le fichier de l'interface. La classe doit avoir une propriété privée$tableNamedéfinie par le constructeur. Implémentesave($key, $data)pour retourner"Inserting '[data]' into table [tableName] with key [key]". Implémenteretrieve($key)pour retourner"Selecting from table [tableName] where key = [key]".main.php: inclus les deux fichiers de stockage. Crée une fonction appeléestoreDataqui accepte un paramètre de typeStorable, 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ée une autre fonction appeléefetchDataqui accepte un paramètre de typeStorableet une clé, et retourne le résultat de l'appel àretrieve().
Tu recevras quatre entrées : un chemin de répertoire, un nom de table, une clé et des données à stocker. Crée un FileStorage avec le répertoire et un DatabaseStorage avec le nom de la table. Utilise ta fonction storeData() pour enregistrer les données d'abord avec le stockage de fichiers, puis avec le stockage de base de données. Affiche chaque résultat sur sa propre ligne. Ensuite, utilise fetchData() pour récupérer les données depuis le stockage de fichiers et affiche ce résultat.
Remarque comme storeData() et fetchData() fonctionnent de manière identique avec les deux types de stockage. Elles se soucient uniquement du fait 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, mais ils sont interchangeables grâce au contrat de l'interface.
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, key et data
// Elle doit retourner le résultat de l'appel à save() avec la key et data
// TODO: Créer une fonction fetchData qui accepte un Storable et key
// Elle doit retourner le résultat de l'appel à retrieve() avec la key
// TODO: Créer une instance de FileStorage avec le directory
// TODO: Créer une instance de DatabaseStorage avec le table name
// TODO: Utiliser storeData() pour sauvegarder data avec le stockage fichier, puis afficher le résultat
// TODO: Utiliser storeData() pour sauvegarder data avec le stockage base de données, puis afficher le résultat
// TODO: Use fetchData() to retrieve from file storage, then print the result
?>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
1Fondamentaux de la POO
Fichiers externesIntroduction à la POOClasses et objetsLe mot-clé $thisMéthodesPropriétésConstructeur (__construct)Destructeur (__destruct)Récapitulatif - Calculatrice simple4Héritage
Héritage de baseLe mot-clé parent::Redéfinition des méthodesLe mot-clé finalClasses abstraitesRécapitulatif – Hiérarchie des employés7Encapsulation
Public, Protected, PrivateModificateurs d’accès en profondeurAccesseurs et mutateursDissimulation de l’informationPromotion des constructeurs (8.0)Récapitulatif – Système de fiches étudiant10Concepts avancés de la POO
Composition ou héritageInjection de dépendancesClasses anonymesÉnumérations (PHP 8.1)Fibres (PHP 8.1)Approfondissement du clonage d’objetsGénérateurs et itérateurs13Patrons de conception – Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Méthode modèlePatron ÉtatPatron CompositePatron Dépôt2Espaces de noms et chargement automatique
Introduction aux espaces de nomsLe mot-clé useNorme de chargement automatique PSR-4Autoloader de ComposerRécapitulatif - Projet organisé5Interfaces et contrats
Introduction aux interfacesImplémentation des interfacesImplémentation de plusieurs interfacesInterface ou classe abstraiteIndication de type avec les interfacesRécapitulatif - Calculateur de formes8Méthodes magiques
Introduction aux méthodes magiques__toString et __debugInfo__get, __set, __isset, __unset__call et __callStatic__clone et clonage d’objets__serialize et __unserializeRécapitulatif - Collection personnalisée3Propriétés de classe
Propriétés d’instance vs statiquesConstantes dans les classesMéthodes et propriétés statiquesPropriétés privées et protégéesPropriétés en lecture seule (PHP 8.1)Récapitulatif - Gestionnaire de comptes bancaires6Polymorphisme
Révision de la redéfinition des méthodesPolymorphisme via les interfacesAnnotations de type et types unionLiaison statique tardiveRécapitulatif - Processeur de paiements9Traits
Introduction aux traitsUtilisation de plusieurs traitsRésolution des conflits entre traitsMéthodes abstraites dans les traitsTraits par rapport à l’héritage12Patrons de conception – Partie 1
Introduction aux patrons de conceptionPatron SingletonPatron FactoryPatron ObserverPatron StrategyEntraînez-vous par vous-même : Compilateur PHP en ligne