Interface vs Classe Abstraite
Fait partie de la section Programmation Orientée Objet du Journey PHP de Coddy — leçon 30 sur 91.
Maintenant que vous avez appris à la fois les interfaces et les classes abstraites, clarifions quand utiliser chacune d'elles. Bien qu'elles puissent sembler similaires, elles servent des objectifs différents et possèdent des capacités distinctes.
| Caractéristique | Interface | Classe abstraite |
|---|---|---|
| Méthodes | Uniquement les signatures de méthode | Méthodes abstraites et concrètes |
| Propriétés | Uniquement des constantes | Peut avoir des propriétés |
| Héritage multiple | Une classe peut en implémenter plusieurs | Une classe ne peut en étendre qu'une seule |
| Constructeur | Non autorisé | Peut avoir un constructeur |
Utilisez une interface lorsque vous souhaitez définir un contrat que des classes non liées peuvent remplir. Par exemple, un Printer et un Logger peuvent tous deux implémenter une interface Writable, même s'ils ne partagent aucun ancêtre commun.
Utilisez une classe abstraite lorsque vous avez des classes apparentées qui partagent du code commun. Si vos classes Dog et Cat ont toutes deux besoin de la même implémentation de la méthode eat(), une classe abstraite Animal vous permet d'écrire ce code une seule fois.
Vous pouvez également combiner les deux approches. Une classe peut étendre une classe abstraite tout en implémentant plusieurs interfaces :
<?php
abstract class Animal {
protected $name;
public function eat() {
return $this->name . " is eating";
}
}
interface Swimmable {
public function swim();
}
class Duck extends Animal implements Swimmable {
public function __construct($name) {
$this->name = $name;
}
public function swim() {
return $this->name . " is swimming";
}
}
Point clé : Choisissez des interfaces pour définir des capacités à travers des classes non liées. Choisissez des classes abstraites lorsque vous avez besoin de partager du code entre des classes liées.
Défi
FacileConstruisons un système de véhicules qui démontre quand utiliser une classe abstraite par rapport à une interface — et comment combiner les deux approches efficacement.
Vous allez créer un système où les véhicules partagent des propriétés communes via l'héritage, mais peuvent également acquérir des capacités supplémentaires via des interfaces. Cela reflète les décisions de conception du monde réel : une voiture « est un » véhicule (héritage), mais elle « a aussi la capacité » d'être ravitaillable en carburant (interface).
Organisez votre code sur quatre fichiers :
Refuelable.php— Définissez une interfaceRefuelableavec une signature de méthode unique :refuel($amount). Cette capacité peut s'appliquer à n'importe quelle classe nécessitant un ravitaillement, quelle que soit sa hiérarchie d'héritage.Vehicle.php— Définissez une classe abstraiteVehiclequi fournit des fonctionnalités partagées pour tous les véhicules. Elle doit avoir une propriété protégée$brandet une propriété protégée$fuelLevel(commençant à 0). Le constructeur accepte le nom de la marque. Incluez une méthode concrètegetBrand()qui retourne la marque, une méthode concrètegetFuelLevel()qui retourne le niveau de carburant actuel, et une méthode abstraitedrive()que les classes enfants doivent implémenter.Car.php— Créez une classeCarqui étendVehicleet implémenteRefuelable. Incluez les deux fichiers requis en haut. Le constructeur accepte la marque et la transmet au constructeur parent. Implémentezdrive()pour retourner"[brand] car is driving". Implémentezrefuel($amount)pour ajouter le montant au niveau de carburant et retourner"[brand] refueled to [fuelLevel] liters".main.php— Incluez le fichier Car. Vous recevrez deux entrées : un nom de marque et une quantité de carburant (convertissez en entier). Créez uneCaravec la marque. Affichez le résultat dedrive()sur la première ligne, puis affichez le résultat derefuel()avec la quantité de carburant sur la deuxième ligne.
Ce défi illustre la distinction clé : la classe abstraite Vehicle partage du code et établit une relation de type « est un », tandis que l'interface Refuelable ajoute une capacité qui pourrait s'appliquer à d'autres classes non liées (comme un générateur ou un bateau) sans exiger qu'elles soient des véhicules.
Aide-mémoire
Les interfaces et les classes abstraites servent des objectifs différents :
| Caractéristique | Interface | Classe Abstraite |
|---|---|---|
| Méthodes | Uniquement les signatures de méthodes | Méthodes abstraites et concrètes |
| Propriétés | Uniquement des constantes | Peut avoir des propriétés |
| Héritage multiple | Une classe peut en implémenter plusieurs | Une classe ne peut en étendre qu'une seule |
| Constructeur | Non autorisé | Peut avoir un constructeur |
Utilisez une interface pour définir un contrat pour des classes non liées. Utilisez une classe abstraite pour partager du code commun entre des classes liées.
Une classe peut étendre une classe abstraite tout en implémentant plusieurs interfaces :
<?php
abstract class Animal {
protected $name;
public function eat() {
return $this->name . " is eating";
}
}
interface Swimmable {
public function swim();
}
class Duck extends Animal implements Swimmable {
public function __construct($name) {
$this->name = $name;
}
public function swim() {
return $this->name . " is swimming";
}
}
Essayez vous-même
<?php
require_once 'Car.php';
// Lire l'entrée
$brand = trim(fgets(STDIN));
$fuelAmount = intval(trim(fgets(STDIN)));
// TODO: Créer une voiture (Car) avec la marque
// TODO: Afficher le résultat de drive() sur la première ligne
// TODO: Afficher le résultat de refuel() avec la quantité de carburant sur la deuxième ligne
?>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
1Les bases de la POO
Fichiers externesIntroduction à la POOClasses vs ObjetsLe mot-clé $thisMéthodesPropriétésConstructeur (__construct)Destructeur (__destruct)Récapitulatif - Calculatrice simple4Héritage
Héritage de baseLe mot-clé parent::Redéfinition de méthodeLe mot-clé finalClasses abstraitesRécapitulatif - Hiérarchie des employés7Encapsulation
Public, Protégé, PrivéModificateurs d'accès en détailGetters et SettersMasquage d'informationPromotion de constructeur (8.0)Récapitulatif - Système de gestion d'étudiants10Concepts avancés de la POO
Composition vs HéritageInjection de dépendancesClasses anonymesÉnumérations (PHP 8.1)Fibers (PHP 8.1)Clonage d'objets : analyse approfondieGénérateurs et Itérateurs13Patrons de conception Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Méthode ModèlePatron ÉtatPatron CompositePatron Repository2Espaces de noms et Autoloading
Introduction aux espaces de nomsLe mot-clé useStandard d'Autoloading PSR-4L'Autoloader ComposerRécapitulatif - Projet organisé5Interfaces et Contrats
Introduction aux InterfacesImplémentation d'InterfacesImplémentation d'Interfaces MultiplesInterface vs Classe AbstraiteType Hinting avec les InterfacesRécapitulatif - Calculateur de Formes8Méthodes magiques
Introduction aux méthodes magiques__toString & __debugInfo__get, __set, __isset, __unset__call & __callStatic__clone & Clonage d'objets__serialize & __unserializeRécapitulatif - Collection personnalisée3Propriétés de classe
Propriétés d'instance vs Propriétés statiquesConstantes dans les classesMéthodes et propriétés statiquesPropriétés privées et protégéesPropriétés readonly (PHP 8.1)Récapitulatif - Gestionnaire de compte bancaire6Polymorphisme
Retour sur la redéfinition de méthodePolymorphisme via les interfacesType Hinting et Types d'unionLiaison statique tardiveRécapitulatif - Processeur de paiement9Traits
Introduction aux TraitsUtilisation de plusieurs TraitsRésolution des conflits de TraitsMéthodes abstraites dans les TraitsTraits vs Héritage12Patrons de conception Partie 1
Introduction aux patrons de conceptionPatron SingletonPatron FabriquePatron ObservateurPatron Stratégie