Cours de Génie Logiciel
ThĂšme :
Junior
1 vues ·6/13 leçons

📚 Cours Complet : Programmation OrientĂ©e Objet

2 crédits (30 heures) - CM, TD, TP, TPE


📘 Chapitre 1 : Introduction aux concepts orientĂ©s objets

1.1 Définition et Philosophie

🎯 DĂ©finition : La Programmation OrientĂ©e Objet (POO) est un paradigme de programmation qui organise le code autour d’objets plutĂŽt que de fonctions et de logique procĂ©durale.

💡 Analogie de la vie rĂ©elle : Imaginez une voiture :

  • Elle a des caractĂ©ristiques : couleur, marque, vitesse
  • Elle a des comportements : dĂ©marrer, accĂ©lĂ©rer, freiner
  • Elle cache sa complexitĂ© : vous n’avez pas besoin de savoir comment fonctionne le moteur pour conduire

1.2 Les 4 piliers de la POO

Pilier Description Exemple Réel
Encapsulation Regrouper données et méthodes Un téléphone cache ses circuits internes
HĂ©ritage CrĂ©er Ă  partir de l’existant Une voiture Ă©lectrique hĂ©rite des propriĂ©tĂ©s d’une voiture
Polymorphisme Plusieurs formes Un bouton “play” fonctionne pour musique, vidĂ©o, etc.
Abstraction Simplifier la complexité Un volant = interface simple pour systÚme complexe

1.3 Avantages de la POO

<?php
declare(strict_types=1);

// ❌ SANS POO - Code procĂ©dural dĂ©sorganisĂ©
$voiture1_marque = "Toyota";
$voiture1_vitesse = 0;
$voiture2_marque = "Peugeot";
$voiture2_vitesse = 0;

function accelerer_voiture(&$vitesse, $augmentation) {
    $vitesse += $augmentation;
}

// ✅ AVEC POO - Code organisĂ© et rĂ©utilisable
class Voiture {
    private string $marque;
    private int $vitesse = 0;

    public function __construct(string $marque) {
        $this->marque = $marque;
    }

    public function accelerer(int $augmentation): void {
        $this->vitesse += $augmentation;
    }
}

$voiture1 = new Voiture("Toyota");
$voiture2 = new Voiture("Peugeot");
?>

📘 Chapitre 2 : Objets et Classes

2.1 Définitions Fondamentales

đŸ—ïž Classe : Un plan, un modĂšle, un moule pour crĂ©er des objets 📩 Objet : Une instance concrĂšte créée Ă  partir d’une classe 🔧 Attribut/PropriĂ©tĂ© : Une caractĂ©ristique de l’objet ⚡ MĂ©thode : Une action que l’objet peut effectuer

💡 Analogie :

  • Classe = Plan d’architecte d’une maison
  • Objet = Maison construite selon ce plan
  • Attributs = Nombre de chambres, couleur, surface
  • MĂ©thodes = Ouvrir la porte, allumer les lumiĂšres

2.2 Syntaxe de base en PHP 8.4

<?php
declare(strict_types=1);

// DÉCLARATION D'UNE CLASSE
class Personne {
    // ATTRIBUTS (Propriétés)
    public string $nom;        // Public : accessible partout
    private int $age;          // Private : accessible uniquement dans la classe
    protected string $adresse; // Protected : accessible dans la classe et ses enfants

    // CONSTRUCTEUR - Méthode spéciale appelée à la création
    public function __construct(string $nom, int $age, string $adresse) {
        $this->nom = $nom;      // $this représente l'objet actuel
        $this->age = $age;
        $this->adresse = $adresse;
    }

    // MÉTHODES - Actions de l'objet
    public function sePresenter(): string {
        return "Je m'appelle {$this->nom} et j'ai {$this->age} ans.";
    }

    public function vieillir(): void {
        $this->age++;
    }

    // GETTER - Obtenir une valeur privée
    public function getAge(): int {
        return $this->age;
    }

    // SETTER - Modifier une valeur privée avec validation
    public function setAge(int $nouvelAge): void {
        if ($nouvelAge < 0 || $nouvelAge > 150) {
            throw new InvalidArgumentException("Âge invalide");
        }
        $this->age = $nouvelAge;
    }
}

// CRÉATION D'OBJETS (Instanciation)
$personne1 = new Personne("Alice", 25, "Paris");
$personne2 = new Personne("Bob", 30, "Lyon");

// UTILISATION DES OBJETS
echo $personne1->sePresenter() . PHP_EOL;  // Appel de méthode
echo "Nom : " . $personne1->nom . PHP_EOL;  // AccÚs à propriété publique
// echo $personne1->age;  // ERREUR ! age est privé

$personne1->vieillir();
echo "Nouvel Ăąge : " . $personne1->getAge() . PHP_EOL;
?>

2.3 Exemple concret : SystĂšme Bancaire

<?php
declare(strict_types=1);

class CompteBancaire {
    // Propriétés
    private string $numero;
    private string $titulaire;
    private float $solde;
    private array $historique = [];

    // PHP 8.1+ : propriĂ©tĂ© readonly (ne peut ĂȘtre modifiĂ©e aprĂšs initialisation)
    public readonly DateTime $dateOuverture;

    // Constructeur
    public function __construct(string $titulaire, float $depotInitial = 0) {
        $this->numero = $this->genererNumero();
        $this->titulaire = $titulaire;
        $this->solde = $depotInitial;
        $this->dateOuverture = new DateTime();

        if ($depotInitial > 0) {
            $this->ajouterHistorique("Ouverture avec dépÎt", $depotInitial);
        }
    }

    // Méthode privée (utilisée uniquement en interne)
    private function genererNumero(): string {
        return 'CB' . date('Y') . rand(10000, 99999);
    }

    private function ajouterHistorique(string $operation, float $montant): void {
        $this->historique[] = [
            'date' => new DateTime(),
            'operation' => $operation,
            'montant' => $montant,
            'solde' => $this->solde
        ];
    }

    // Méthodes publiques (interface de la classe)
    public function deposer(float $montant): void {
        if ($montant <= 0) {
            throw new InvalidArgumentException("Le montant doit ĂȘtre positif");
        }

        $this->solde += $montant;
        $this->ajouterHistorique("DépÎt", $montant);
        echo "✅ DĂ©pĂŽt de {$montant}€ effectuĂ©\n";
    }

    public function retirer(float $montant): bool {
        if ($montant <= 0) {
            throw new InvalidArgumentException("Le montant doit ĂȘtre positif");
        }

        if ($montant > $this->solde) {
            echo "❌ Solde insuffisant\n";
            return false;
        }

        $this->solde -= $montant;
        $this->ajouterHistorique("Retrait", -$montant);
        echo "✅ Retrait de {$montant}€ effectuĂ©\n";
        return true;
    }

    public function consulterSolde(): float {
        return $this->solde;
    }

    public function afficherReleve(): void {
        echo "\n=== RELEVÉ DE COMPTE ===\n";
        echo "Compte N° : {$this->numero}\n";
        echo "Titulaire : {$this->titulaire}\n";
        echo "Solde actuel : {$this->solde}€\n";
        echo "\nDerniÚres opérations :\n";

        foreach (array_slice($this->historique, -5) as $op) {
            printf("  %s - %s : %+.2f€ (Solde: %.2f€)\n",
                $op['date']->format('d/m/Y H:i'),
                $op['operation'],
                $op['montant'],
                $op['solde']
            );
        }
    }
}

// Utilisation
$compte = new CompteBancaire("Jean Dupont", 1000);
$compte->deposer(500);
$compte->retirer(200);
$compte->retirer(2000);  // Échec
$compte->afficherReleve();
?>

📘 Chapitre 3 : Encapsulation et masquage d’information

3.1 Définition et Principes

🔒 Encapsulation : MĂ©canisme qui consiste Ă  :

  1. Regrouper les données et les méthodes dans une classe
  2. Cacher les dĂ©tails d’implĂ©mentation
  3. ContrĂŽler l’accĂšs aux donnĂ©es

💡 Analogie : Un distributeur automatique

  • Vous voyez les boutons (interface publique)
  • Vous ne voyez pas les mĂ©canismes internes (implĂ©mentation privĂ©e)
  • Vous interagissez uniquement via les boutons

3.2 Niveaux de visibilité

<?php
declare(strict_types=1);

class Voiture {
    // PUBLIC : Accessible de partout
    public string $couleur = "rouge";

    // PRIVATE : Accessible uniquement dans cette classe
    private float $niveauEssence = 50.0;

    // PROTECTED : Accessible dans cette classe et ses enfants
    protected int $kilometrage = 0;

    // Méthodes publiques pour interagir avec les données privées
    public function fairePlein(): void {
        $this->niveauEssence = 100.0;  // OK : accĂšs interne
    }

    public function getNiveauEssence(): float {
        return $this->niveauEssence;
    }

    public function rouler(int $km): void {
        if ($this->niveauEssence <= 0) {
            echo "⛜ RĂ©servoir vide !\n";
            return;
        }

        $this->kilometrage += $km;
        $this->niveauEssence -= $km * 0.08;  // Consommation

        if ($this->niveauEssence < 0) {
            $this->niveauEssence = 0;
        }
    }
}

// Utilisation
$maVoiture = new Voiture();

// ✅ OK : propriĂ©tĂ© publique
$maVoiture->couleur = "bleu";
echo "Couleur : " . $maVoiture->couleur . PHP_EOL;

// ❌ ERREUR : propriĂ©tĂ© privĂ©e
// $maVoiture->niveauEssence = 100;  // Fatal Error

// ✅ OK : utilisation de l'interface publique
$maVoiture->fairePlein();
echo "Essence : " . $maVoiture->getNiveauEssence() . "%\n";

$maVoiture->rouler(200);
echo "Essence aprĂšs trajet : " . $maVoiture->getNiveauEssence() . "%\n";
?>

3.3 Exemple pratique : Mot de passe sécurisé

<?php
declare(strict_types=1);

class Utilisateur {
    private string $nom;
    private string $email;
    private string $motDePasseHash;  // Jamais stocké en clair !

    public function __construct(string $nom, string $email, string $motDePasse) {
        $this->nom = $nom;
        $this->setEmail($email);
        $this->setMotDePasse($motDePasse);
    }

    // Validation et encapsulation pour l'email
    private function setEmail(string $email): void {
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new InvalidArgumentException("Email invalide");
        }
        $this->email = $email;
    }

    // Le mot de passe n'est jamais accessible directement
    private function setMotDePasse(string $motDePasse): void {
        if (strlen($motDePasse) < 8) {
            throw new InvalidArgumentException("Le mot de passe doit faire au moins 8 caractĂšres");
        }
        // Hashage sécurisé (jamais en clair !)
        $this->motDePasseHash = password_hash($motDePasse, PASSWORD_DEFAULT);
    }

    // Vérification sans exposer le hash
    public function verifierMotDePasse(string $motDePasse): bool {
        return password_verify($motDePasse, $this->motDePasseHash);
    }

    public function changerMotDePasse(string $ancienMdp, string $nouveauMdp): bool {
        if (!$this->verifierMotDePasse($ancienMdp)) {
            echo "❌ Ancien mot de passe incorrect\n";
            return false;
        }

        $this->setMotDePasse($nouveauMdp);
        echo "✅ Mot de passe changĂ© avec succĂšs\n";
        return true;
    }

    // Getters pour les données non sensibles
    public function getNom(): string {
        return $this->nom;
    }

    public function getEmail(): string {
        return $this->email;
    }

    // PAS de getter pour le mot de passe !
}

// Utilisation
$user = new Utilisateur("Alice", "alice@example.com", "MonSuperMotDePasse123");

echo "Utilisateur : " . $user->getNom() . PHP_EOL;
echo "Email : " . $user->getEmail() . PHP_EOL;

// Impossible d'accéder au mot de passe
// echo $user->motDePasseHash;  // ERREUR

// Vérification sécurisée
if ($user->verifierMotDePasse("MonSuperMotDePasse123")) {
    echo "✅ Connexion rĂ©ussie\n";
}

$user->changerMotDePasse("MauvaisMdp", "NouveauMdp456");  // Échec
$user->changerMotDePasse("MonSuperMotDePasse123", "NouveauMdp456");  // SuccĂšs
?>

📘 Chapitre 4 : AgrĂ©gation et DĂ©composition

4.1 Définitions et Différences

🔗 Association : Relation entre classes 📎 AgrĂ©gation : Relation “utilise” ou “a un” (faible) 🔧 Composition : Relation “fait partie de” (forte)

Aspect Agrégation Composition
Dépendance Faible Forte
Cycle de vie Indépendant Dépendant
Exemple École ← → Professeur Voiture ← → Moteur
Symbole UML Losange vide ◇ Losange plein ◆

4.2 Agrégation : Relation faible

<?php
declare(strict_types=1);

// Les livres peuvent exister sans bibliothĂšque
class Livre {
    private string $titre;
    private string $auteur;
    private string $isbn;

    public function __construct(string $titre, string $auteur, string $isbn) {
        $this->titre = $titre;
        $this->auteur = $auteur;
        $this->isbn = $isbn;
    }

    public function getTitre(): string {
        return $this->titre;
    }

    public function getInfo(): string {
        return "{$this->titre} par {$this->auteur} (ISBN: {$this->isbn})";
    }
}

// La bibliothĂšque agrĂšge des livres existants
class Bibliotheque {
    private string $nom;
    /** @var Livre[] */
    private array $livres = [];

    public function __construct(string $nom) {
        $this->nom = $nom;
    }

    // Agrégation : on ajoute un livre existant
    public function ajouterLivre(Livre $livre): void {
        $this->livres[] = $livre;
        echo "📚 '{$livre->getTitre()}' ajoutĂ© Ă  {$this->nom}\n";
    }

    public function retirerLivre(Livre $livre): void {
        $index = array_search($livre, $this->livres);
        if ($index !== false) {
            unset($this->livres[$index]);
            $this->livres = array_values($this->livres);
            echo "đŸ“€ '{$livre->getTitre()}' retirĂ© de {$this->nom}\n";
        }
    }

    public function listerLivres(): void {
        echo "\n📖 Livres dans {$this->nom}:\n";
        foreach ($this->livres as $livre) {
            echo "  - " . $livre->getInfo() . "\n";
        }
    }

    public function __destruct() {
        echo "đŸ›ïž Fermeture de {$this->nom}\n";
        // Les livres continuent d'exister !
    }
}

// Exemple d'utilisation
$livre1 = new Livre("1984", "George Orwell", "978-0452284234");
$livre2 = new Livre("Le Petit Prince", "Antoine de Saint-Exupéry", "978-0156012195");
$livre3 = new Livre("Harry Potter", "J.K. Rowling", "978-0439708180");

$biblio1 = new Bibliotheque("BibliothĂšque Centrale");
$biblio2 = new Bibliotheque("BibliothĂšque de Quartier");

// Un livre peut ĂȘtre dans plusieurs bibliothĂšques (prĂȘt inter-bibliothĂšques)
$biblio1->ajouterLivre($livre1);
$biblio1->ajouterLivre($livre2);

$biblio2->ajouterLivre($livre2);  // Le mĂȘme livre dans une autre bibliothĂšque
$biblio2->ajouterLivre($livre3);

$biblio1->listerLivres();
$biblio2->listerLivres();

// Les livres existent indépendamment des bibliothÚques
unset($biblio1);
echo "\n📕 Le livre '{$livre1->getTitre()}' existe toujours aprùs fermeture de la bibliothùque\n";
?>

4.3 Composition : Relation forte

<?php
declare(strict_types=1);

// Le processeur n'existe pas sans l'ordinateur
class Processeur {
    private string $modele;
    private float $frequence;
    private int $coeurs;

    public function __construct(string $modele, float $frequence, int $coeurs) {
        $this->modele = $modele;
        $this->frequence = $frequence;
        $this->coeurs = $coeurs;
        echo "🔧 Processeur {$modele} installĂ©\n";
    }

    public function calculer(): void {
        echo "⚡ Processeur {$this->modele} calcule à {$this->frequence}GHz\n";
    }

    public function __destruct() {
        echo "đŸ—‘ïž Processeur {$this->modele} dĂ©truit\n";
    }
}

// La RAM n'existe pas sans l'ordinateur
class Memoire {
    private int $capacite;
    private string $type;

    public function __construct(int $capacite, string $type = "DDR4") {
        $this->capacite = $capacite;
        $this->type = $type;
        echo "🔧 MĂ©moire {$capacite}GB {$type} installĂ©e\n";
    }

    public function stocker(): void {
        echo "đŸ’Ÿ Stockage en mĂ©moire {$this->type} ({$this->capacite}GB)\n";
    }

    public function __destruct() {
        echo "đŸ—‘ïž MĂ©moire dĂ©truite\n";
    }
}

// L'ordinateur crée et possÚde ses composants
class Ordinateur {
    private string $marque;
    private Processeur $processeur;  // Composition
    private Memoire $memoire;        // Composition

    public function __construct(string $marque, string $cpu, float $freq, int $ram) {
        $this->marque = $marque;
        echo "đŸ–„ïž Construction de l'ordinateur {$marque}\n";

        // L'ordinateur CRÉE ses composants (composition)
        $this->processeur = new Processeur($cpu, $freq, 8);
        $this->memoire = new Memoire($ram);

        echo "✅ Ordinateur {$marque} assemblĂ©\n\n";
    }

    public function demarrer(): void {
        echo "🚀 DĂ©marrage de {$this->marque}\n";
        $this->processeur->calculer();
        $this->memoire->stocker();
    }

    public function __destruct() {
        echo "\n❌ Destruction de l'ordinateur {$this->marque}\n";
        // Les composants sont automatiquement détruits
    }
}

// Utilisation
echo "=== CRÉATION ===\n";
$monPC = new Ordinateur("Dell", "Intel i7", 3.8, 16);

echo "=== UTILISATION ===\n";
$monPC->demarrer();

echo "\n=== DESTRUCTION ===\n";
unset($monPC);  // L'ordinateur ET ses composants sont détruits

// On ne peut pas créer un processeur seul (il fait partie de l'ordinateur)
// $cpu = new Processeur("AMD", 3.5, 6);  // N'a pas de sens sans ordinateur
?>

📘 Chapitre 5 : GĂ©nĂ©ralisation et SpĂ©cialisation

5.1 Concepts

âŹ†ïž GĂ©nĂ©ralisation : Extraire les caractĂ©ristiques communes âŹ‡ïž SpĂ©cialisation : CrĂ©er des versions plus spĂ©cifiques

💡 Analogie :

        Animal (général)
           ↑
    ┌──────┌──────┐
    ↑      ↑      ↑
MammifĂšre Oiseau Poisson
    ↑
┌───┌───┐
↑   ↑   ↑
Chat Chien Cheval (spécifique)

5.2 Implémentation de la hiérarchie

<?php
declare(strict_types=1);

// NIVEAU 1 : Classe la plus générale
abstract class Vehicule {
    protected string $marque;
    protected string $modele;
    protected int $annee;
    protected float $prix;

    public function __construct(string $marque, string $modele, int $annee, float $prix) {
        $this->marque = $marque;
        $this->modele = $modele;
        $this->annee = $annee;
        $this->prix = $prix;
    }

    // Méthode commune à tous les véhicules
    public function afficherInfo(): void {
        echo "📋 {$this->marque} {$this->modele} ({$this->annee}) - {$this->prix}€\n";
    }

    // Méthodes abstraites à implémenter
    abstract public function demarrer(): void;
    abstract public function arreter(): void;
    abstract public function calculerTaxe(): float;
}

// NIVEAU 2 : Spécialisation par type de terrain
abstract class VehiculeTerrestre extends Vehicule {
    protected int $nombreRoues;

    public function __construct(string $marque, string $modele, int $annee, float $prix, int $roues) {
        parent::__construct($marque, $modele, $annee, $prix);
        $this->nombreRoues = $roues;
    }

    public function rouler(): void {
        echo "🛞 Roule sur {$this->nombreRoues} roues\n";
    }
}

abstract class VehiculeAerien extends Vehicule {
    protected float $altitudeMax;

    public function __construct(string $marque, string $modele, int $annee, float $prix, float $altitude) {
        parent::__construct($marque, $modele, $annee, $prix);
        $this->altitudeMax = $altitude;
    }

    public function voler(): void {
        echo "✈ Vole jusqu'Ă  {$this->altitudeMax}m d'altitude\n";
    }
}

// NIVEAU 3 : Classes concrÚtes spécialisées
class Voiture extends VehiculeTerrestre {
    private int $nombrePortes;
    private string $typeCarburant;

    public function __construct(
        string $marque,
        string $modele,
        int $annee,
        float $prix,
        int $portes,
        string $carburant
    ) {
        parent::__construct($marque, $modele, $annee, $prix, 4); // 4 roues
        $this->nombrePortes = $portes;
        $this->typeCarburant = $carburant;
    }

    public function demarrer(): void {
        echo "🚗 La voiture dĂ©marre (moteur {$this->typeCarburant})\n";
    }

    public function arreter(): void {
        echo "🚗 La voiture s'arrĂȘte\n";
    }

    public function calculerTaxe(): float {
        $taxeBase = 200;
        if ($this->typeCarburant === "electrique") {
            return 0;  // Pas de taxe pour électrique
        }
        return $taxeBase + ($this->annee < 2015 ? 100 : 0);
    }
}

class Moto extends VehiculeTerrestre {
    private int $cylindree;

    public function __construct(
        string $marque,
        string $modele,
        int $annee,
        float $prix,
        int $cylindree
    ) {
        parent::__construct($marque, $modele, $annee, $prix, 2); // 2 roues
        $this->cylindree = $cylindree;
    }

    public function demarrer(): void {
        echo "đŸïž La moto dĂ©marre (cylindrĂ©e {$this->cylindree}cc)\n";
    }

    public function arreter(): void {
        echo "đŸïž La moto s'arrĂȘte\n";
    }

    public function calculerTaxe(): float {
        return $this->cylindree > 125 ? 150 : 50;
    }

    public function faireLaRoue(): void {
        echo "đŸïž La moto fait une roue arriĂšre !\n";
    }
}

class Avion extends VehiculeAerien {
    private int $nombrePassagers;

    public function __construct(
        string $marque,
        string $modele,
        int $annee,
        float $prix,
        int $passagers
    ) {
        parent::__construct($marque, $modele, $annee, $prix, 10000);
        $this->nombrePassagers = $passagers;
    }

    public function demarrer(): void {
        echo "✈ L'avion dĂ©marre ses moteurs\n";
    }

    public function arreter(): void {
        echo "✈ L'avion coupe ses moteurs\n";
    }

    public function calculerTaxe(): float {
        return $this->nombrePassagers * 100;
    }

    public function decoller(): void {
        echo "đŸ›« L'avion dĂ©colle avec {$this->nombrePassagers} passagers\n";
    }
}

// Utilisation de la hiérarchie
echo "=== PARC DE VÉHICULES ===\n\n";

$voiture = new Voiture("Tesla", "Model 3", 2023, 45000, 4, "electrique");
$moto = new Moto("Yamaha", "MT-07", 2022, 8000, 700);
$avion = new Avion("Boeing", "737", 2020, 50000000, 180);

// Méthodes communes (héritage)
$voiture->afficherInfo();
$voiture->demarrer();
$voiture->rouler();
echo "Taxe: {$voiture->calculerTaxe()}€\n\n";

$moto->afficherInfo();
$moto->demarrer();
$moto->rouler();
$moto->faireLaRoue();  // Méthode spécifique
echo "Taxe: {$moto->calculerTaxe()}€\n\n";

$avion->afficherInfo();
$avion->demarrer();
$avion->decoller();  // Méthode spécifique
$avion->voler();
echo "Taxe: {$avion->calculerTaxe()}€\n";
?>

📘 Chapitre 6 : HĂ©ritage

6.1 Définition et Types

🧬 HĂ©ritage : MĂ©canisme permettant Ă  une classe (enfant) d’hĂ©riter des propriĂ©tĂ©s et mĂ©thodes d’une autre classe (parent).

Types d’hĂ©ritage :

  • Simple : Une classe hĂ©rite d’une seule classe parent
  • Multiple : Une classe hĂ©rite de plusieurs classes (non supportĂ© en PHP)
  • Multiniveau : A → B → C (chaĂźne d’hĂ©ritage)
  • HiĂ©rarchique : Plusieurs classes hĂ©ritent d’une mĂȘme classe parent

6.2 Syntaxe et Mécanismes

<?php
declare(strict_types=1);

// CLASSE PARENT (Base)
class Animal {
    protected string $nom;      // protected = accessible aux enfants
    protected int $age;
    protected float $poids;
    private string $id;         // private = NON accessible aux enfants

    public function __construct(string $nom, int $age, float $poids) {
        $this->nom = $nom;
        $this->age = $age;
        $this->poids = $poids;
        $this->id = uniqid('ANIMAL_');
    }

    public function manger(float $quantite): void {
        $this->poids += $quantite * 0.1;
        echo "{$this->nom} mange et prend du poids\n";
    }

    public function dormir(): void {
        echo "{$this->nom} dort... đŸ’€\n";
    }

    public function vieillir(): void {
        $this->age++;
        echo "{$this->nom} a maintenant {$this->age} ans\n";
    }

    public function getInfo(): string {
        return "{$this->nom} - {$this->age} ans - {$this->poids}kg";
    }
}

// CLASSE ENFANT 1 : Héritage simple
class Chien extends Animal {  // extends = hérite de
    private string $race;
    private bool $dresse;

    public function __construct(string $nom, int $age, float $poids, string $race) {
        // parent:: appelle le constructeur du parent
        parent::__construct($nom, $age, $poids);
        $this->race = $race;
        $this->dresse = false;
    }

    // NOUVELLE méthode spécifique aux chiens
    public function aboyer(): void {
        echo "{$this->nom} aboie: WOUAF WOUAF! 🐕\n";
    }

    // REDÉFINITION (Override) d'une mĂ©thode parent
    public function manger(float $quantite): void {
        // On peut appeler la méthode parent
        parent::manger($quantite);
        echo "Le chien {$this->nom} remue la queue\n";
    }

    // EXTENSION d'une méthode parent
    public function getInfo(): string {
        $infoBase = parent::getInfo();
        return $infoBase . " - Race: {$this->race}" .
               ($this->dresse ? " (Dressé)" : " (Non dressé)");
    }

    public function dresser(): void {
        $this->dresse = true;
        echo "{$this->nom} est maintenant dressé\n";
    }
}

// CLASSE ENFANT 2
class Chat extends Animal {
    private int $vies = 9;

    public function miauler(): void {
        echo "{$this->nom} miaule: MIAOU! 🐈\n";
    }

    public function ronronner(): void {
        echo "{$this->nom} ronronne de plaisir... 😊\n";
    }

    // Redéfinition spécifique
    public function dormir(): void {
        echo "{$this->nom} fait une sieste au soleil ☀\n";
    }

    public function perdreUneVie(): void {
        if ($this->vies > 0) {
            $this->vies--;
            echo "{$this->nom} perd une vie! Reste: {$this->vies}\n";
        }
    }
}

// CLASSE ENFANT 3 : Héritage multiniveau
class ChienPolicier extends Chien {
    private string $matricule;
    private array $competences = [];

    public function __construct(string $nom, int $age, float $poids, string $race) {
        parent::__construct($nom, $age, $poids, $race);
        $this->matricule = 'K9_' . rand(1000, 9999);
        $this->dresser();  // Automatiquement dressé
        $this->competences = ['Recherche', 'Attaque', 'Détection'];
    }

    public function rechercher(): void {
        echo "🔍 {$this->nom} (Matricule: {$this->matricule}) recherche...\n";
    }

    public function detecterDrogue(): bool {
        $trouve = rand(0, 1) === 1;
        echo $trouve ? "🚹 Drogue dĂ©tectĂ©e!\n" : "✅ RAS\n";
        return $trouve;
    }

    public function getInfo(): string {
        return parent::getInfo() . " - Chien policier #{$this->matricule}";
    }
}

// UTILISATION
echo "=== DÉMONSTRATION HÉRITAGE ===\n\n";

// Création d'animaux
$chien = new Chien("Max", 3, 25, "Labrador");
$chat = new Chat("Felix", 2, 4);
$chienK9 = new ChienPolicier("Rex", 5, 30, "Berger Allemand");

// Méthodes héritées d'Animal
echo "--- Méthodes héritées ---\n";
$chien->vieillir();      // Hérité d'Animal
$chat->vieillir();       // Hérité d'Animal
$chienK9->vieillir();    // Hérité d'Animal via Chien

// Méthodes redéfinies
echo "\n--- Méthodes redéfinies ---\n";
$chien->manger(0.5);     // Version Chien
$chat->dormir();         // Version Chat
$chienK9->manger(0.5);   // Version Chien (hérité)

// Méthodes spécifiques
echo "\n--- Méthodes spécifiques ---\n";
$chien->aboyer();        // Spécifique à Chien
$chat->miauler();        // Spécifique à Chat
$chienK9->rechercher();  // Spécifique à ChienPolicier

// Informations complĂštes
echo "\n--- Informations ---\n";
echo $chien->getInfo() . "\n";
echo $chat->getInfo() . "\n";
echo $chienK9->getInfo() . "\n";

// Test instanceof
echo "\n--- Tests de type ---\n";
echo "Max est un Animal? " . ($chien instanceof Animal ? "Oui" : "Non") . "\n";
echo "Max est un Chien? " . ($chien instanceof Chien ? "Oui" : "Non") . "\n";
echo "Max est un Chat? " . ($chien instanceof Chat ? "Oui" : "Non") . "\n";
echo "Rex est un ChienPolicier? " . ($chienK9 instanceof ChienPolicier ? "Oui" : "Non") . "\n";
?>

📘 Chapitre 7 : Polymorphisme et Liaison Dynamique

7.1 Définition du Polymorphisme

🎭 Polymorphisme : Du grec “poly” (plusieurs) et “morphe” (forme). CapacitĂ© d’un objet Ă  prendre plusieurs formes.

Types de polymorphisme :

  1. Surcharge (Overloading) - MĂȘme nom, paramĂštres diffĂ©rents
  2. Redéfinition (Overriding) - Remplacement de méthode parent
  3. Polymorphisme d’interface - ImplĂ©mentation d’interfaces

7.2 Liaison Dynamique

<?php
declare(strict_types=1);

// INTERFACE : Contrat que les classes doivent respecter
interface Sonore {
    public function emettreSon(): string;
}

interface Deplacable {
    public function seDeplacer(): void;
    public function calculerVitesse(): float;
}

// CLASSE ABSTRAITE de base
abstract class AnimalPolymorphe implements Sonore, Deplacable {
    protected string $nom;
    protected float $vitesseMax;

    public function __construct(string $nom, float $vitesseMax) {
        $this->nom = $nom;
        $this->vitesseMax = $vitesseMax;
    }

    // Méthode concrÚte commune
    public function presenter(): void {
        echo "Je suis {$this->nom}, ";
        echo "mon cri: " . $this->emettreSon() . ", ";
        echo "ma vitesse: {$this->calculerVitesse()} km/h\n";
    }

    // Méthodes abstraites à implémenter
    abstract public function manger(): void;
}

// IMPLÉMENTATIONS CONCRÈTES
class ChienPolymorphe extends AnimalPolymorphe {
    public function __construct(string $nom) {
        parent::__construct($nom, 40);
    }

    public function emettreSon(): string {
        return "Wouaf wouaf!";
    }

    public function seDeplacer(): void {
        echo "{$this->nom} court sur ses 4 pattes\n";
    }

    public function calculerVitesse(): float {
        return $this->vitesseMax;
    }

    public function manger(): void {
        echo "{$this->nom} mange des croquettes\n";
    }
}

class ChatPolymorphe extends AnimalPolymorphe {
    public function __construct(string $nom) {
        parent::__construct($nom, 48);
    }

    public function emettreSon(): string {
        return "Miaou!";
    }

    public function seDeplacer(): void {
        echo "{$this->nom} se déplace furtivement\n";
    }

    public function calculerVitesse(): float {
        return $this->vitesseMax * 0.8; // Les chats sont plus lents en moyenne
    }

    public function manger(): void {
        echo "{$this->nom} mange du poisson\n";
    }
}

class OiseauPolymorphe extends AnimalPolymorphe {
    private float $altitudeVol;

    public function __construct(string $nom, float $altitudeVol) {
        parent::__construct($nom, 60);
        $this->altitudeVol = $altitudeVol;
    }

    public function emettreSon(): string {
        return "Cui cui!";
    }

    public function seDeplacer(): void {
        echo "{$this->nom} vole Ă  {$this->altitudeVol}m d'altitude\n";
    }

    public function calculerVitesse(): float {
        return $this->vitesseMax * 1.5; // Plus rapide en vol
    }

    public function manger(): void {
        echo "{$this->nom} picore des graines\n";
    }
}

// CLASSE UTILISANT LE POLYMORPHISME
class Zoo {
    /** @var AnimalPolymorphe[] */
    private array $animaux = [];

    public function ajouterAnimal(AnimalPolymorphe $animal): void {
        $this->animaux[] = $animal;
    }

    // Polymorphisme : traite tous les animaux de la mĂȘme façon
    public function nourrir(): void {
        echo "\n=== HEURE DU REPAS ===\n";
        foreach ($this->animaux as $animal) {
            $animal->manger();  // Liaison dynamique
        }
    }

    public function faireSpectacle(): void {
        echo "\n=== SPECTACLE DU ZOO ===\n";
        foreach ($this->animaux as $animal) {
            $animal->presenter();
            $animal->seDeplacer();
            echo "---\n";
        }
    }

    // Polymorphisme avec interface
    public function concert(): void {
        echo "\n=== CONCERT D'ANIMAUX ===\n";
        foreach ($this->animaux as $animal) {
            if ($animal instanceof Sonore) {  // Vérification d'interface
                echo get_class($animal) . ": " . $animal->emettreSon() . "\n";
            }
        }
    }

    public function courseDeVitesse(): void {
        echo "\n=== COURSE DE VITESSE ===\n";

        // Trier par vitesse (polymorphisme)
        $animauxTries = $this->animaux;
        usort($animauxTries, function($a, $b) {
            return $b->calculerVitesse() <=> $a->calculerVitesse();
        });

        foreach ($animauxTries as $position => $animal) {
            echo ($position + 1) . ". " . get_class($animal) .
                 " - " . $animal->calculerVitesse() . " km/h\n";
        }
    }
}

// DÉMONSTRATION DU POLYMORPHISME
$zoo = new Zoo();

// Ajout d'animaux de types différents
$zoo->ajouterAnimal(new ChienPolymorphe("Rex"));
$zoo->ajouterAnimal(new ChatPolymorphe("Mimi"));
$zoo->ajouterAnimal(new OiseauPolymorphe("Piou", 100));
$zoo->ajouterAnimal(new ChienPolymorphe("Max"));

// Le zoo traite tous les animaux de maniĂšre polymorphe
$zoo->faireSpectacle();
$zoo->nourrir();
$zoo->concert();
$zoo->courseDeVitesse();

// FONCTION POLYMORPHE
function faireCrier(Sonore $creature): void {
    echo "La créature fait: " . $creature->emettreSon() . "\n";
}

echo "\n=== TEST FONCTION POLYMORPHE ===\n";
faireCrier(new ChienPolymorphe("Test"));
faireCrier(new ChatPolymorphe("Test2"));
?>

7.3 Exemple Avancé : SystÚme de Paiement

<?php
declare(strict_types=1);

// INTERFACE pour stratégie de paiement
interface MethodePaiement {
    public function payer(float $montant): bool;
    public function verifierDisponibilite(float $montant): bool;
    public function obtenirFrais(float $montant): float;
}

// CLASSE ABSTRAITE
abstract class Paiement implements MethodePaiement {
    protected string $titulaire;
    protected float $limite;

    public function __construct(string $titulaire, float $limite) {
        $this->titulaire = $titulaire;
        $this->limite = $limite;
    }

    public function verifierDisponibilite(float $montant): bool {
        return $montant <= $this->limite;
    }

    // Template Method Pattern
    public final function effectuerTransaction(float $montant): bool {
        echo "\n💳 Tentative de paiement de {$montant}€\n";

        // Vérifications
        if (!$this->verifierDisponibilite($montant)) {
            echo "❌ Montant supĂ©rieur Ă  la limite\n";
            return false;
        }

        // Calcul des frais
        $frais = $this->obtenirFrais($montant);
        $total = $montant + $frais;

        echo "📊 Montant: {$montant}€ + Frais: {$frais}€ = Total: {$total}€\n";

        // Paiement (liaison dynamique)
        return $this->payer($total);
    }
}

// IMPLÉMENTATIONS CONCRÈTES
class CarteCredit extends Paiement {
    private string $numero;

    public function __construct(string $titulaire, string $numero, float $limite = 5000) {
        parent::__construct($titulaire, $limite);
        $this->numero = substr($numero, -4); // Garder seulement les 4 derniers chiffres
    }

    public function payer(float $montant): bool {
        echo "💳 Paiement par carte ****{$this->numero}\n";
        echo "✅ Transaction approuvĂ©e\n";
        return true;
    }

    public function obtenirFrais(float $montant): float {
        return $montant * 0.02; // 2% de frais
    }
}

class PayPal extends Paiement {
    private string $email;

    public function __construct(string $titulaire, string $email) {
        parent::__construct($titulaire, 10000);
        $this->email = $email;
    }

    public function payer(float $montant): bool {
        echo "đŸ…żïž Paiement PayPal ({$this->email})\n";
        echo "📧 Email de confirmation envoyĂ©\n";
        echo "✅ Paiement effectuĂ©\n";
        return true;
    }

    public function obtenirFrais(float $montant): float {
        return $montant > 100 ? 3.0 : 0; // 3€ de frais si > 100€
    }
}

class Virement extends Paiement {
    private string $iban;

    public function __construct(string $titulaire, string $iban) {
        parent::__construct($titulaire, 50000);
        $this->iban = $iban;
    }

    public function payer(float $montant): bool {
        echo "🏩 Virement bancaire IBAN: {$this->iban}\n";
        echo "⏳ Traitement en 1-2 jours ouvrĂ©s\n";
        echo "✅ Ordre de virement envoyĂ©\n";
        return true;
    }

    public function obtenirFrais(float $montant): float {
        return 0; // Pas de frais
    }
}

// UTILISATION DU POLYMORPHISME
class Boutique {
    private string $nom;

    public function __construct(string $nom) {
        $this->nom = $nom;
    }

    // Méthode polymorphe : accepte n'importe quelle méthode de paiement
    public function traiterCommande(float $montant, MethodePaiement $methode): void {
        echo "\nđŸ›ïž === {$this->nom} ===\n";
        echo "📩 Commande de {$montant}€\n";

        if ($methode->effectuerTransaction($montant)) {
            echo "🎉 Commande confirmĂ©e!\n";
        } else {
            echo "😔 Commande annulĂ©e\n";
        }
    }
}

// Démonstration
$boutique = new Boutique("TechShop");

// Différentes méthodes de paiement
$carte = new CarteCredit("Jean Dupont", "1234567812345678");
$paypal = new PayPal("Marie Martin", "marie@example.com");
$virement = new Virement("Paul Durand", "FR7612345678901234567890123");

// Polymorphisme en action : mĂȘme mĂ©thode, comportements diffĂ©rents
$boutique->traiterCommande(150, $carte);
$boutique->traiterCommande(250, $paypal);
$boutique->traiterCommande(1500, $virement);

// Test avec limite dépassée
$boutique->traiterCommande(6000, $carte);  // Échec : limite dĂ©passĂ©e
?>

PrĂȘt Ă  passer Ă  la suite ?

Vous avez terminĂ© cette leçon sur 📚 Cours Complet : Programmation OrientĂ©e Objet. Continuez votre apprentissage avec la prochaine leçon ou explorez d'autres formations.