POO Avancée - Interfaces et Classes Abstraites

Approfondissez vos connaissances en POO avec les interfaces, les classes abstraites, les traits et autres concepts avancés.

Introduction à la POO avancée

Après avoir maîtrisé les bases de la POO avec les classes, les propriétés et les méthodes, il est temps d'explorer les concepts avancés qui font la puissance de la Programmation Orientée Objet en PHP.

Dans ce module, nous allons découvrir :

Prérequis : Pour bien comprendre ce module, vous devez déjà maîtriser les concepts de base de la POO présentés dans le module précédent.

Les Interfaces

Une interface définit un contrat qu'une classe doit respecter. Elle contient uniquement des déclarations de méthodes (sans implémentation) que les classes qui l'implémentent doivent définir.

Déclaration d'une Interface
interface Animal {
    public function manger();
    public function seDeplacer();
    public function emettreSon();
}

Caractéristiques des interfaces

Interface étendue et implémentation
interface Mammifere extends Animal {
    public function allaiter();
}

class Chien implements Mammifere {
    private $nom;
    
    public function __construct($nom) {
        $this->nom = $nom;
    }
    
    public function manger() {
        return "{$this->nom} mange des croquettes.";
    }
    
    public function seDeplacer() {
        return "{$this->nom} court sur ses quatre pattes.";
    }
    
    public function emettreSon() {
        return "{$this->nom} aboie : Wouaf !";
    }
    
    public function allaiter() {
        return "Une chienne peut allaiter ses chiots.";
    }
}

Les Classes Abstraites

Une classe abstraite est une classe qui ne peut pas être instanciée directement. Elle sert de modèle pour d'autres classes qui vont l'étendre. Elle peut contenir à la fois des méthodes concrètes (avec implémentation) et des méthodes abstraites (sans implémentation).

Déclaration d'une Classe Abstraite
abstract class EtreVivant {
    protected $nom;
    protected $age;
    
    public function __construct($nom, $age) {
        $this->nom = $nom;
        $this->age = $age;
    }
    
    // Méthode concrète avec implémentation
    public function getNom() {
        return $this->nom;
    }
    
    // Méthode abstraite sans implémentation
    abstract public function respirer();
}

Caractéristiques des classes abstraites

Extension d'une Classe Abstraite
class Chien extends EtreVivant {
    private $race;
    
    public function __construct($nom, $age, $race) {
        parent::__construct($nom, $age);
        $this->race = $race;
    }
    
    // Implémentation de la méthode abstraite
    public function respirer() {
        return "{$this->nom} respire par les poumons.";
    }
    
    public function getRace() {
        return $this->race;
    }
}

Exemple avec notre classe Chien :

Nom : Rex

Race : Berger Allemand

Action : Rex respire par les poumons.

Action : Rex aboie : Wouaf !

Interface vs Classe Abstraite

Les interfaces et les classes abstraites servent à des fins similaires mais ont des différences importantes :

Interface
  • Contient uniquement des déclarations de méthodes (sans implémentation)
  • Toutes les méthodes sont implicitement public
  • Une classe peut implémenter plusieurs interfaces
  • Définit un "contrat" que la classe doit respecter
  • Utilisée quand vous voulez définir un comportement commun que différentes classes non liées doivent implémenter
  • Pas d'état (propriétés) ni de code réutilisable
Classe Abstraite
  • Peut contenir des méthodes concrètes (avec implémentation) et des méthodes abstraites
  • Peut avoir des méthodes de tous les niveaux d'accès (public, protected, private)
  • Une classe ne peut étendre qu'une seule classe abstraite
  • Fournit un modèle partiellement implémenté
  • Utilisée quand vous voulez partager du code entre des classes étroitement liées
  • Peut avoir des propriétés et du code réutilisable

Conseil de choix :

  • Utilisez une interface lorsque différentes classes non liées doivent partager un comportement commun sans partager de code.
  • Utilisez une classe abstraite lorsque vous avez un groupe de classes liées qui doivent partager du code commun.

EtreVivant (abstract)

# nom: string
# age: int
+ __construct(nom: string, age: int)
+ getNom(): string
+ getAge(): int
+ respirer(): string (abstract)

↑

Chien

- race: string
+ __construct(nom: string, age: int, race: string)
+ respirer(): string
+ getRace(): string

↗

Mammifere

+ manger(): string
+ seDeplacer(): string
+ emettreSon(): string
+ allaiter(): string

Note: Mammifere étend l'interface Animal

Les Traits

Les traits sont un mécanisme permettant la réutilisation de code dans des langages à héritage simple comme PHP. Un trait est similaire à une classe, mais son seul but est de regrouper des fonctionnalités de manière cohérente dans une ou plusieurs classes.

Déclaration d'un Trait
trait Identifiable {
    private $id;
    
    public function genererIdentifiant() {
        $this->id = uniqid(get_class($this) . '_');
    }
    
    public function getId() {
        return $this->id;
    }
}

Utilisation des traits

Pour utiliser un trait dans une classe, on utilise le mot-clé use à l'intérieur de la déclaration de la classe :

Utilisation d'un Trait dans une Classe
class Chien extends EtreVivant implements Mammifere {
    // Utilisation du trait
    use Identifiable;
    
    private $race;
    
    public function __construct($nom, $age, $race) {
        parent::__construct($nom, $age);
        $this->race = $race;
        $this->genererIdentifiant(); // Méthode du trait
    }
    
    // Implémentations des méthodes...
}

Exemple avec notre trait Identifiable :

Identifiant du chien : Chien_69d00ffa018c7

Caractéristiques des traits

Résolution de conflits avec plusieurs traits
trait A {
    public function bonjour() {
        return "Bonjour de A!";
    }
}

trait B {
    public function bonjour() {
        return "Bonjour de B!";
    }
    
    public function salut() {
        return "Salut de B!";
    }
}

class C {
    // Résolution de conflits
    use A, B {
        B::bonjour insteadof A; // Utiliser bonjour de B
        A::bonjour as bonjourA; // Renommer bonjour de A
    }
}

Propriétés et Méthodes Statiques

Les propriétés et méthodes statiques appartiennent à la classe elle-même plutôt qu'à une instance spécifique. Elles sont accessibles sans avoir besoin d'instancier la classe.

Déclaration et utilisation de membres statiques
class Compteur {
    private static $compteur = 0;
    
    public static function incrementer() {
        self::$compteur++;
    }
    
    public static function getValeur() {
        return self::$compteur;
    }
    
    public static function reinitialiser() {
        self::$compteur = 0;
    }
}

Utilisation :

echo Compteur::getValeur(); // Affiche 0
Compteur::incrementer();
Compteur::incrementer();
echo Compteur::getValeur(); // Affiche 2
Compteur::reinitialiser();
echo Compteur::getValeur(); // Affiche 0

Exemple avec notre classe Compteur :

Valeur initiale : 0

Après un incrément : 1

Après un autre incrément : 2

Après réinitialisation : 0

Caractéristiques des membres statiques

Cas d'utilisation : Les membres statiques sont utiles pour représenter des données ou comportements qui sont liés à la classe dans son ensemble, comme des compteurs, des utilitaires, ou des instances uniques (pattern Singleton).

Classes et Méthodes Finales

Le mot-clé final empêche l'héritage des classes ou la redéfinition des méthodes dans les classes filles.

Classe finale
final class Configuration {
    private static $instance = null;
    private $parametres = [];
    
    private function __construct() {
        $this->parametres = [
            'debug' => true,
            'timezone' => 'Europe/Paris'
        ];
    }
    
    public static function getInstance() {
        if (self::$instance === null) {
            self::$instance = new Configuration();
        }
        return self::$instance;
    }
    
    // Méthodes pour accéder aux paramètres
}
Méthode finale
class Base {
    final public function methodeCritique() {
        return "Cette méthode ne peut pas être redéfinie";
    }
    
    public function methodeNormale() {
        return "Cette méthode peut être redéfinie";
    }
}

class Derivee extends Base {
    // Impossible de redéfinir methodeCritique
    
    public function methodeNormale() {
        return "Méthode redéfinie";
    }
}

Important : Utilisez final avec prudence car il limite l'extensibilité de votre code. Il est généralement utilisé pour sécuriser des parties critiques d'un système ou pour optimiser les performances en évitant les appels de méthodes virtuelles.

Méthodes Magiques

PHP fournit un ensemble de méthodes magiques qui sont invoquées automatiquement en réponse à certains événements ou actions sur un objet. Ces méthodes commencent par deux underscore (__).

Méthodes magiques courantes
class Exemple {
    private $donnees = [];
    
    // Appelée lors de la lecture d'une propriété non accessible
    public function __get($nom) {
        if (array_key_exists($nom, $this->donnees)) {
            return $this->donnees[$nom];
        }
        return null;
    }
    
    // Appelée lors de l'écriture dans une propriété non accessible
    public function __set($nom, $valeur) {
        $this->donnees[$nom] = $valeur;
    }
    
    // Appelée lors de la vérification de l'existence d'une propriété
    public function __isset($nom) {
        return isset($this->donnees[$nom]);
    }
    
    // Appelée lors de la suppression d'une propriété
    public function __unset($nom) {
        unset($this->donnees[$nom]);
    }
    
    // Appelée lors de l'appel d'une méthode non accessible
    public function __call($nom, $arguments) {
        echo "Appel de la méthode '$nom' avec les arguments: " 
             . implode(", ", $arguments);
    }
    
    // Appelée lors de l'appel d'une méthode statique non accessible
    public static function __callStatic($nom, $arguments) {
        echo "Appel de la méthode statique '$nom'";
    }
    
    // Appelée lors de la conversion de l'objet en chaîne
    public function __toString() {
        return "Exemple avec " . count($this->donnees) . " propriétés";
    }
    
    // Appelée lors du débogage avec var_dump
    public function __debugInfo() {
        return [
            'info' => "Objet de débogage",
            'donnees_count' => count($this->donnees)
        ];
    }
}

Autres méthodes magiques :

  • __construct() : Constructeur de la classe
  • __destruct() : Destructeur de la classe
  • __clone() : Appelée lors du clonage de l'objet
  • __invoke() : Permet d'utiliser l'objet comme une fonction
  • __sleep() et __wakeup() : Contrôlent la sérialisation
  • __serialize() et __unserialize() : Nouveaux contrôles de sérialisation (PHP 7.4+)

Design Patterns et POO Avancée

Les design patterns (patrons de conception) sont des solutions éprouvées à des problèmes récurrents en conception de logiciel. En combinant les concepts avancés de POO, vous pouvez implémenter ces patterns en PHP.

Exemples de design patterns en PHP

Pattern Singleton

Garantit qu'une classe n'a qu'une seule instance et fournit un point d'accès global à celle-ci.

class Singleton {
    private static $instance = null;
    
    private function __construct() { } // Constructeur privé
    private function __clone() { } // Clonage privé
    
    public static function getInstance() {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }
}
Pattern Factory

Crée des objets sans exposer la logique de création.

interface Produit {
    public function operation();
}

class ProduitConcretA implements Produit {
    public function operation() {
        return "Résultat de ProduitConcretA";
    }
}

class ProduitConcretB implements Produit {
    public function operation() {
        return "Résultat de ProduitConcretB";
    }
}

class Fabrique {
    public static function creerProduit($type) {
        if ($type === 'A') {
            return new ProduitConcretA();
        } else if ($type === 'B') {
            return new ProduitConcretB();
        }
        throw new Exception("Type de produit inconnu");
    }
}

Autres design patterns courants en PHP :

  • Observer : Pour implémenter des systèmes d'événements
  • Strategy : Pour définir une famille d'algorithmes interchangeables
  • Dependency Injection : Pour réduire le couplage entre les classes
  • Repository : Pour abstraire l'accès aux données
  • MVC : Pour séparer les préoccupations dans les applications web

Bonnes pratiques en POO avancée