Cours de Génie Logiciel
Thème :
Junior
1 vues ·5/13 leçons

Programmation Orientée Objet

Cours Complet - 2 Crédits (30 heures)

CM, TD, TP, TPE


1 - INTRODUCTION AUX CONCEPTS ORIENTÉS OBJETS

1.1- 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. Un objet combine données (attributs) et comportements (méthodes) dans une seule entité.

💡 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

Pourquoi la POO ?

Avant la POO (Programmation procédurale) :

  • Code organisé en fonctions séparées
  • Données et traitements séparés
  • Difficile à maintenir sur de gros projets
  • Risque de conflits de noms
  • Code difficilement réutilisable

Avec la POO :

  • Code organisé en objets autonomes
  • Données et comportements liés ensemble
  • Maintenance facilitée
  • Pas de conflits (encapsulation)
  • Réutilisation naturelle du code

1.2- Les Paradigmes de Programmation

Programmation Procédurale

<?php
declare(strict_types=1);
// Approche procédurale

$nom = "eva";
$age = 25;
$email = "eva@example.com";

function afficherUtilisateur($nom, $age, $email) {
    echo "$nom, $age ans, $email";
}

function envoyerEmail($email, $message) {
    // Envoyer email
}

afficherUtilisateur($nom, $age, $email);
envoyerEmail($email, "Bonjour");
?>

Problèmes :

  • Les données traînent partout
  • Aucun lien logique entre données et fonctions
  • Difficile de savoir quelles fonctions utilisent quelles données

Programmation Orientée Objet

<?php
declare(strict_types=1);
// Approche POO

class Utilisateur {
    private string $nom;
    private int $age;
    private string $email;

    public function __construct(string $nom, int $age, string $email) {
        $this->nom = $nom;
        $this->age = $age;
        $this->email = $email;
    }

    public function afficher(): void {
        echo "{$this->nom}, {$this->age} ans, {$this->email}";
    }

    public function envoyerEmail(string $message): void {
        // Envoyer email à $this->email
    }
}

$eva = new Utilisateur("eva", 25, "eva@example.com");
$eva->afficher();
$eva->envoyerEmail("Bonjour");

Avantages :

  • Tout ce qui concerne un utilisateur est dans la classe Utilisateur
  • Les données sont protégées (private)
  • Le code est organisé et logique

1.3- Concepts Fondamentaux de la POO

Les 4 Piliers de la POO

  1. Encapsulation : Regrouper données et méthodes, cacher les détails
  2. Abstraction : Simplifier la complexité en ne montrant que l’essentiel
  3. Héritage : Réutiliser du code d’une classe parente
  4. Polymorphisme : Même interface, comportements différents

Ou encore

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.4- Exemple du Monde Réel : Une Voiture

Dans le monde réel :

  • Une voiture est un objet physique
  • Elle a des caractéristiques : couleur, marque, modèle, vitesse
  • Elle a des comportements : démarrer, accélérer, freiner, klaxonner

Avantages de la POO

<?php
declare(strict_types=1);


// ❌ SANS POO - Code procédural désorganisé
$voiture1_marque: string = "Toyota";
$voiture1_vitesse: int = 0;
$voiture2_marque: string = "Peugeot";
$voiture2_vitesse: int = 0;

function accelerer_voiture(int &$vitesse, int $augmentation): void {
    $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");
?>



1.5- Vocabulaire Essentiel

Terme Définition Exemple
Classe Modèle, plan, blueprint class Voiture
Objet Instance concrète d’une classe $maVoiture = new Voiture()
Attribut Variable dans une classe (caractéristique) private string $couleur
Méthode Fonction dans une classe (comportement) public function demarrer()
Instance Un objet créé à partir d’une classe $voiture1, $voiture2
Instanciation Action de créer un objet new Voiture()

2 - OBJETS ET CLASSES

2.1- Définition d’une Classe

Une classe est un modèle (template) qui décrit :

  • Les attributs (données) que les objets auront
  • Les méthodes (comportements) que les objets pourront exécuter

🏗️ 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

Syntaxe PHP 8.4

<?php
declare(strict_types=1);


class NomDeLaClasse {
    // Attributs (propriétés)
    private type $attribut1;
    protected type $attribut2;
    public type $attribut3;

    // Constructeur
    public function __construct(parameters) {
        // Initialisation
    }

    // Méthodes
    public function nomMethode(parameters): returnType {
        // Code
    }
}

?>

Convention de Nommage

  • Classe : PascalCase (Utilisateur, CompteBancaire, VoitureElectrique)
  • Méthodes : camelCase (calculerTotal, afficherDetails, envoyerEmail)
  • Attributs : camelCase ($nom, $dateNaissance, $soldeCompte)

2.2 Définition d’un Objet

Un objet est une instance concrète d’une classe. C’est une entité qui existe en mémoire avec ses propres valeurs d’attributs.

Syntaxe

//Syntaxe

$nomVariable = new NomDeLaClasse(arguments);

Exemple Réel : Gestion d’Employés

<?php
declare(strict_types=1);

class Employe {
    private string $nom;
    private string $prenom;
    private float $salaire;
    private string $poste;

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

    public function afficherFiche(): void {
        echo "===== FICHE EMPLOYÉ =====\n";
        echo "Nom : {$this->nom}\n";
        echo "Prénom : {$this->prenom}\n";
        echo "Poste : {$this->poste}\n";
        echo "Salaire : {$this->salaire} FCFA\n";
        echo "========================\n";
    }

    public function augmenterSalaire(float $pourcentage): void {
        $ancienSalaire = $this->salaire;
        $this->salaire += ($this->salaire * $pourcentage / 100);
        echo "{$this->prenom} : Augmentation de {$pourcentage}%\n";
        echo "Ancien salaire : {$ancienSalaire} FCFA\n";
        echo "Nouveau salaire : {$this->salaire} FCFA\n";
    }
}

// Création d'objets (instances)
$employe1 = new Employe("Dupont", "eva", 2500, "Développeuse");
$employe2 = new Employe("Martin", "Bob", 3000, "Chef de projet");
$employe3 = new Employe("Durand", "Clara", 2200, "Designer");

// Utilisation
$employe1->afficherFiche();
$employe1->augmenterSalaire(10);

$employe2->afficherFiche();

?>

Sortie :

===== FICHE EMPLOYÉ =====
Nom : Dupont
Prénom : eva
Poste : Développeuse
Salaire : 2500 FCFA
========================
eva : Augmentation de 10%
Ancien salaire : 2500 FCFA
Nouveau salaire : 2750 FCFA
===== FICHE EMPLOYÉ =====
Nom : Martin
Prénom : Bob
Poste : Chef de projet
Salaire : 3000 FCFA
========================

Syntaxe de création d’objet

$nomVariable = new NomDeLaClasse(arguments);
  • new : Mot-clé pour créer une nouvelle instance
  • NomDeLaClasse : Le nom de la classe à instancier
  • arguments : Paramètres passés au constructeur
  • $nomVariable : Variable qui référence l’objet créé

Exemple Réel : Gestion d’Employés

La Classe Employe (Le plan)

class Employe {
    private string $nom;
    private string $prenom;
    private float $salaire;
    private string $poste;

Propriétés private : Données encapsulées (protégées), accessibles seulement via les méthodes de la classe.


Le Constructeur

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

$this : Référence à l’objet courant. Permet d’accéder à ses propriétés et méthodes.


Méthode afficherFiche()

public function afficherFiche(): void {
    echo "===== FICHE EMPLOYÉ =====\n";
    echo "Nom : {$this->nom}\n";
    echo "Prénom : {$this->prenom}\n";
    echo "Poste : {$this->poste}\n";
    echo "Salaire : {$this->salaire} FCFA\n";
    echo "========================\n";
}

Affiche les informations de l’employé courant ($this).


Méthode augmenterSalaire()

public function augmenterSalaire(float $pourcentage): void {
    $ancienSalaire = $this->salaire;
    $this->salaire += ($this->salaire * $pourcentage / 100);
    echo "{$this->prenom} : Augmentation de {$pourcentage}%\n";
    echo "Ancien salaire : {$ancienSalaire} FCFA\n";
    echo "Nouveau salaire : {$this->salaire} FCFA\n";
}

Modifie le salaire de l’employé spécifique sur lequel la méthode est appelée.


Création des Objets (Instances)

$employe1 = new Employe("Dupont", "eva", 2500, "Développeuse");
$employe2 = new Employe("Martin", "Bob", 3000, "Chef de projet");
$employe3 = new Employe("Durand", "Clara", 2200, "Designer");

3 objets distincts créés, chacun avec ses propres valeurs :

  • $employe1 : Dupont Eva, 2500 FCFA, Développeuse
  • $employe2 : Martin Bob, 3000 FCFA, Chef de projet
  • $employe3 : Durand Clara, 2200 FCFA, Designer

Utilisation des Objets

$employe1->afficherFiche();
$employe1->augmenterSalaire(10);

$employe2->afficherFiche();

-> : Opérateur d’accès aux méthodes et propriétés d’un objet.


✅ Ce que démontre cet exemple :

  1. Encapsulation : Les données sont protégées (private)
  2. Abstraction : Interface simple (afficherFiche(), augmenterSalaire())
  3. Indépendance : Chaque objet a son propre état
  4. Réutilisabilité : Même classe pour tous les employés

Résumé

Concept Explication
Classe Plan/moule (Employe)
Objet Instance concrète ($employe1, $employe2)
new Crée une nouvelle instance
$this Référence à l’objet courant
-> Accède aux méthodes/propriétés

Les objets permettent de modéliser le monde réel de manière organisée et maintenable ! 🚀

2.3 Attributs (Propriétés)

Les attributs sont des variables définies dans une classe qui représentent l’état d’un objet.

Types d’Attributs

<?php
declare(strict_types=1);



class Produit {
    // Attributs avec types (PHP 8.4)
    private string $nom;              // Texte
    private float $prix;              // Nombre décimal
    private int $quantite;            // Nombre entier
    private bool $enStock;            // Vrai/Faux
    private ?string $description;     // Texte ou null
    private array $categories;        // Tableau
}

?>

Absolument ! Voici une explication détaillée uniquement des attributs de la classe Produit :


📊 Attributs de la Classe Produit

🔒 Visibilité private

Tous les attributs sont déclarés private :

  • ✅ Encapsulation : Protection des données internes
  • ✅ Contrôle d’accès : Accessibles seulement depuis la classe elle-même
  • ✅ Sécurité : Empêche les modifications externes directes

Détail de chaque Attribut

private string $nom

  • Type : string (chaîne de caractères)
  • Contenu : Texte
  • Usage : Nom du produit
  • Exemple : "Ordinateur Portable", "Cafetière"

private float $prix

  • Type : float (nombre à virgule flottante)
  • Contenu : Nombre décimal
  • Usage : Prix du produit
  • Exemple : 299.99, 15.50

private int $quantite

  • Type : int (entier)
  • Contenu : Nombre entier
  • Usage : Quantité disponible en stock
  • Exemple : 5, 0, 100

private bool $enStock

  • Type : bool (booléen)
  • Contenu : Vrai ou Faux
  • Usage : Indique la disponibilité
  • Exemple : true (disponible), false (rupture)

private ?string $description

  • Type : ?string (chaîne nullable)
  • Contenu : Texte ou null
  • Usage : Description optionnelle
  • Exemple : "Écran 15 pouces, 8GB RAM" ou null

private array $categories

  • Type : array (tableau)
  • Contenu : Liste de valeurs
  • Usage : Catégories du produit
  • Exemple : ["électronique", "informatique"]

Points Importants

Typage Fort

  • Chaque attribut a un type spécifique
  • PHP vérifie le type lors des assignations
  • Prévention des erreurs de type

Attribut Nullable

  • ?string = peut être string ou null
  • Utile pour les données optionnelles

Attribut Booléen

  • $enStock représente un état binaire
  • Parfait pour les flags true/false

Attribut Tableau

  • $categories peut contenir multiple valeurs
  • Flexible pour les classifications multiples

Résumé des Types

Attribut Type Contenu
$nom string Texte
$prix float Nombre décimal
$quantite int Nombre entier
$enStock bool Vrai/Faux
$description ?string Texte ou null
$categories array Tableau

En résumé : Ces attributs définissent parfaitement les caractéristiques essentielles d’un produit dans un système e-commerce !

La Pseudo-Variable $this

$this représente l’instance actuelle de l’objet.

<?php
declare(strict_types=1);



class CompteBancaire {
    private float $solde;

    public function __construct(float $soldeInitial) {
        $this->solde = $soldeInitial;  // $this = cet objet précis
    }

    public function deposer(float $montant): void {
        $this->solde += $montant;  // Accès à l'attribut de CET objet
    }

    public function retirer(float $montant): void {
        if ($this->solde >= $montant) {
            $this->solde -= $montant;
        }
    }

    public function afficherSolde(): void {
        echo "Solde : {$this->solde} FCFA\n";
    }
}

$compte1 = new CompteBancaire(1000);
$compte2 = new CompteBancaire(500);

$compte1->deposer(200);   // $this = $compte1
$compte2->retirer(100);    // $this = $compte2

$compte1->afficherSolde(); // Solde : 1200 FCFA
$compte2->afficherSolde(); // Solde : 400 FCFA

?>

2.4 Méthodes

Les méthodes sont des fonctions définies dans une classe qui représentent les comportements d’un objet.

Syntaxe

<?php
declare(strict_types=1);

class NomClasse {
    public function nomMethode(type $param): returnType {
        // Code
        return $resultat;
    }
}

?>

Types de Méthodes

<?php
declare(strict_types=1);


class Calculatrice {
    private float $resultat = 0;

    // Méthode sans paramètres, sans retour
    public function reset(): void {
        $this->resultat = 0;
    }

    // Méthode avec paramètres, sans retour
    public function ajouter(float $nombre): void {
        $this->resultat += $nombre;
    }

    // Méthode avec paramètres et retour
    public function multiplier(float $a, float $b): float {
        return $a * $b;
    }

    // Méthode getter (retour simple)
    public function obtenirResultat(): float {
        return $this->resultat;
    }
}

?>

Syntaxe Générale

public function nomMethode(type $param): returnType {
    // Code
    return $resultat;
}
  • public : Visibilité (accès)
  • function : Mot-clé pour définir une méthode
  • nomMethode : Nom de la méthode
  • (type $param) : Paramètres typés
  • : returnType : Type de retour
  • return : Valeur retournée

Types de Méthodes dans l’Exemple

Méthode sans paramètres, sans retour

public function reset(): void {
    $this->resultat = 0;
}
  • void : Ne retourne aucune valeur
  • Action : Réinitialise l’état interne
  • Usage : Modifie l’objet sans retourner de données

Méthode avec paramètres, sans retour

public function ajouter(float $nombre): void {
    $this->resultat += $nombre;
}
  • Paramètre : float $nombre (nombre à ajouter)
  • void : Pas de retour
  • Action : Modifie l’attribut $resultat
  • Usage : Opération qui change l’état de l’objet

Méthode avec paramètres et retour

public function multiplier(float $a, float $b): float {
    return $a * $b;
}
  • Paramètres : float $a, float $b (nombres à multiplier)
  • Retour : float (résultat de la multiplication)
  • Action : Calcule et retourne un résultat
  • Usage : Fonction utilitaire qui ne modifie pas l’objet

Méthode Getter (accesseur)

public function obtenirResultat(): float {
    return $this->resultat;
}
  • Aucun paramètre
  • Retour : float (valeur de l’attribut)
  • Action : Fournit l’accès à une propriété private
  • Usage : Lecture sécurisée des données encapsulées

Points Clés

Encapsulation

  • Les méthodes permettent d’accéder/modifier les attributs private
  • Contrôle total sur les modifications des données

Typage Fort

  • Paramètres typés : float $nombre
  • Retour typé : : float, : void
  • Prévention des erreurs de type

$this Mot-Clé

  • $this référence l’objet courant
  • Permet d’accéder aux attributs et méthodes de l’instance

Void vs Return

  • void : Méthode qui effectue une action sans retour
  • return : Méthode qui calcule et retourne une valeur

Résumé des Types de Méthodes

Type Paramètres Retour Usage
Modificateur Optionnels void Modifie l’état
Accesseur Aucun Type Lit les données
Utilitaire Multiple Type Calcule et retourne

En résumé : Les méthodes définissent ce que l’objet peut faire et comment interagir avec ses données ! 🚀

2.5 Constructeur

Le constructeur est une méthode spéciale appelée automatiquement lors de la création d’un objet. Il sert à initialiser les attributs.

Syntaxe Classique

<?php
declare(strict_types=1);

class Livre {
    private string $titre;
    private string $auteur;
    private int $anneePublication;

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

$livre = new Livre("1984", "George Orwell", 1949);

?>

Constructor Property Promotion (PHP 8.0+)

Syntaxe moderne qui déclare et initialise en une ligne :

<?php
declare(strict_types=1);

class Livre {
    public function __construct(
        private string $titre,
        private string $auteur,
        private int $anneePublication
    ) {
        // Rien à faire ! Les attributs sont déclarés et initialisés automatiquement
    }

    public function afficher(): void {
        echo "{$this->titre} par {$this->auteur} ({$this->anneePublication})\n";
    }
}

$livre1 = new Livre("1984", "George Orwell", 1949);
$livre2 = new Livre("Le Petit Prince", "Antoine de Saint-Exupéry", 1943);

$livre1->afficher();
$livre2->afficher();

?>

Caractéristiques :

  • Nom : __construct (méthode magique)
  • Appel automatique à l’instanciation avec new
  • Initialise les attributs avec les valeurs fournies
  • Paramètres typés pour la sécurité

Processus :

$livre = new Livre("1984", "George Orwell", 1949);
  1. Création de l’objet avec new
  2. Appel automatique de __construct()
  3. Assignation des valeurs aux attributs

🚀 Constructor Property Promotion (PHP 8.0+)

Syntaxe Moderne :

public function __construct(
    private string $titre,
    private string $auteur,
    private int $anneePublication
) {
    // Rien à faire ! Les attributs sont déclarés et initialisés automatiquement
}

Avantages :

  • ✅ Déclaration + initialisation en une seule étape
  • ✅ Code plus concis (moins de répétition)
  • ✅ Maintenance simplifiée
  • ✅ Modernité (PHP 8.0+)

Équivalence :

Ancienne syntaxe :

private string $titre;
private string $auteur;
private int $anneePublication;

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

Nouvelle syntaxe :

public function __construct(
    private string $titre,
    private string $auteur,
    private int $anneePublication
) {}

Points Clés du Constructeur

Encapsulation :

  • Initialise les attributs private de manière contrôlée
  • Garantit que l’objet est dans un état valide dès sa création

Typage Fort :

  • Paramètres typés : string $titre, int $annee
  • Prévention des erreurs dès l’instanciation

⚡ Obligation de fournir les valeurs :

// ✅ Correct :
$livre = new Livre("1984", "George Orwell", 1949);

// ❌ Erreur (paramètres manquants) :
$livre = new Livre(); // Erreur !

Différences entre les Deux Syntaxes

Aspect Syntaxe Classique Property Promotion
Déclaration attributs Séparée Directe dans les paramètres
Initialisation Manuelle avec $this-> Automatique
Lignes de code Plus nombreuses Réduites
PHP requis 5.4+ 8.0+

💡 Bonnes Pratiques

✅ Toujours typer les paramètres

public function __construct(
    private string $titre,
    private string $auteur,
    private int $anneePublication
) {}

✅ Utiliser la promotion si possible (PHP 8.0+)

  • Code plus lisible
  • Moins d’erreurs potentielles

✅ Validation dans le constructeur (optionnel)

public function __construct(
    private string $titre,
    private string $auteur,
    private int $anneePublication
) {
    if ($anneePublication < 0) {
        throw new InvalidArgumentException("L'année doit être positive");
    }
}

En résumé : Le constructeur est indispensable pour créer des objets valides et bien initialisés !

2.6 Exemple Réel Complet : Système de Gestion de Bibliothèque

<?php
declare(strict_types=1);

class Livre {
    public function __construct(
        private string $isbn,
        private string $titre,
        private string $auteur,
        private int $anneePublication,
        private bool $disponible = true
    ) {}

    public function emprunter(): bool {
        if ($this->disponible) {
            $this->disponible = false;
            echo "✓ '{$this->titre}' emprunté avec succès.\n";
            return true;
        }

        echo "✗ '{$this->titre}' n'est pas disponible.\n";
        return false;
    }

    public function retourner(): void {
        $this->disponible = true;
        echo "✓ '{$this->titre}' retourné à la bibliothèque.\n";
    }

    public function afficherInfos(): void {
        $statut = $this->disponible ? "Disponible" : "Emprunté";
        echo "─────────────────────────\n";
        echo "Titre : {$this->titre}\n";
        echo "Auteur : {$this->auteur}\n";
        echo "Année : {$this->anneePublication}\n";
        echo "ISBN : {$this->isbn}\n";
        echo "Statut : {$statut}\n";
        echo "─────────────────────────\n";
    }
}

// Utilisation
$livre1 = new Livre("978-2-07-036822-8", "1984", "George Orwell", 1949);
$livre2 = new Livre("978-2-07-061275-8", "Le Petit Prince", "A. de Saint-Exupéry", 1943);

$livre1->afficherInfos();
$livre1->emprunter();
$livre1->afficherInfos();
$livre1->emprunter();  // Déjà emprunté
$livre1->retourner();
$livre1->afficherInfos();

?>

Sortie :

─────────────────────────
Titre : 1984
Auteur : George Orwell
Année : 1949
ISBN : 978-2-07-036822-8
Statut : Disponible
─────────────────────────
✓ '1984' emprunté avec succès.
─────────────────────────
Titre : 1984
Auteur : George Orwell
Année : 1949
ISBN : 978-2-07-036822-8
Statut : Emprunté
─────────────────────────
✗ '1984' n'est pas disponible.
✓ '1984' retourné à la bibliothèque.
─────────────────────────
Titre : 1984
Auteur : George Orwell
Année : 1949
ISBN : 978-2-07-036822-8
Statut : Disponible
─────────────────────────

<?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, "Bafoussam");
$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}FCFA 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}FCFA 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}FCFA\n";
        echo "\nDernières opérations :\n";

        foreach (array_slice($this->historique, -5) as $op) {
            printf("  %s - %s : %+.2fFCFA (Solde: %.2fFCFA)\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();

?>

3 - ENCAPSULATION ET MASQUAGE D’INFORMATION

3.1 Définition de l’Encapsulation

L’encapsulation est le principe de regrouper les données (attributs) et les comportements (méthodes) dans une même classe, et de contrôler l’accès à ces données.

Les 3 Objectifs de l’Encapsulation

  1. Protection des données : Empêcher la modification non contrôlée
  2. Cohérence : Garantir que l’état de l’objet reste valide
  3. Abstraction : Cacher la complexité interne

Ou encore :

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.1- Les Niveaux de Visibilité PHP

PHP propose 3 niveaux de visibilité :

Visibilité Accès depuis la classe Accès depuis les enfants Accès depuis l’extérieur
private ✅ Oui ❌ Non ❌ Non
protected ✅ Oui ✅ Oui ❌ Non
public ✅ Oui ✅ Oui ✅ Oui

3.2.2- Niveaux d’accès

Modificateur PHP C++ Java Description
Public public public public Accessible partout
Private private private private Accessible uniquement dans la classe
Protected protected protected protected Accessible dans la classe et ses dérivées
Package - - (défaut) Accessible dans le package

Syntaxe

<?php
declare(strict_types=1);

class Exemple {
    private string $attributPrive;      // Accessible uniquement dans cette classe
    protected string $attributProtege;  // Accessible dans cette classe et ses enfants
    public string $attributPublic;      // Accessible partout

    private function methodePrive(): void {}
    protected function methodeProtegee(): void {}
    public function methodePublic(): void {}
}

?>

3.3 Pourquoi Masquer l’Information ?

Exemple Sans Encapsulation (Mauvais)

<?php
declare(strict_types=1);

class CompteBancaire {
    public float $solde;  // Public = DANGER !

    public function __construct(float $soldeInitial) {
        $this->solde = $soldeInitial;
    }
}

$compte = new CompteBancaire(1000);

// PROBLÈME : N'importe qui peut modifier directement !
$compte->solde = -5000;  // Solde négatif impossible dans la réalité
$compte->solde = 999999999;  // Fraude possible
echo "Solde : {$compte->solde} FCFA\n";  // Solde : 999999999 FCFA

?>

Problèmes :

  • Aucun contrôle sur les valeurs
  • Impossible de valider
  • Incohérence possible

Exemple Avec Encapsulation (Bon)

<?php
declare(strict_types=1);


class CompteBancaire {
    private float $solde;  // Private = Protégé

    public function __construct(float $soldeInitial) {
        if ($soldeInitial < 0) {
            throw new InvalidArgumentException("Le solde initial ne peut pas être négatif");
        }
        $this->solde = $soldeInitial;
    }

    public function deposer(float $montant): void {
        if ($montant <= 0) {
            throw new InvalidArgumentException("Le montant doit être positif");
        }
        $this->solde += $montant;
        echo "✓ Dépôt de {$montant} FCFA effectué.\n";
    }

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

        if ($this->solde < $montant) {
            echo "✗ Solde insuffisant. Opération refusée.\n";
            return false;
        }

        $this->solde -= $montant;
        echo "✓ Retrait de {$montant} FCFA effectué.\n";
        return true;
    }

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

$compte = new CompteBancaire(1000);

// Maintenant, toutes les opérations sont contrôlées
$compte->deposer(500);           // ✓ Dépôt de 500 FCFA effectué.
$compte->retirer(200);           // ✓ Retrait de 200 FCFA effectué.
$compte->retirer(2000);          // ✗ Solde insuffisant. Opération refusée.

// $compte->solde = 999999;      // ERREUR : Cannot access private property
echo "Solde actuel : {$compte->obtenirSolde()} FCFA\n";  // Solde actuel : 1300 FCFA

?>

Avantages :

  • Contrôle total sur les modifications
  • Validation automatique
  • Cohérence garantie
  • Sécurité renforcée

3.4- Getters et Setters

Les getters et setters sont des méthodes publiques qui permettent de lire (get) et modifier (set) les attributs privés de manière contrôlée.

Syntaxe Classique

<?php
declare(strict_types=1);


class Personne {
    private string $nom;
    private int $age;

    // Getter pour $nom
    public function getNom(): string {
        return $this->nom;
    }

    // Setter pour $nom
    public function setNom(string $nom): void {
        if (strlen($nom) < 2) {
            throw new InvalidArgumentException("Le nom doit contenir au moins 2 caractères");
        }
        $this->nom = $nom;
    }

    // Getter pour $age
    public function getAge(): int {
        return $this->age;
    }

    // Setter pour $age avec validation
    public function setAge(int $age): void {
        if ($age < 0 || $age > 150) {
            throw new InvalidArgumentException("Âge invalide");
        }
        $this->age = $age;
    }
}

$personne = new Personne();
$personne->setNom("eva");  // ✓ Valide
$personne->setAge(25);       // ✓ Valide

// $personne->setNom("A");   // ✗ Exception : Le nom doit contenir au moins 2 caractères
// $personne->setAge(200);   // ✗ Exception : Âge invalide

echo $personne->getNom();    // eva
echo $personne->getAge();    // 25

?>

3.5- Property Hooks (PHP 8.4)

Nouveauté PHP 8.4 : Alternative moderne aux getters/setters.

Syntaxe

<?php
declare(strict_types=1);

class Personne {
    public string $nom {
        get => strtoupper($this->nom);  // Lecture : toujours en majuscules
        set => trim($value) ?: throw new ValueError("Nom vide");  // Écriture : validée
    }

    public int $age {
        set {
            if ($value < 0 || $value > 150) {
                throw new ValueError("Âge invalide");
            }
            $this->age = $value;
        }
    }
}

$personne = new Personne();
$personne->nom = "  eva  ";  // Trim automatique
echo $personne->nom;           // eva (majuscules automatiques)

$personne->age = 25;           // ✓ OK
// $personne->age = 200;       // ✗ Exception : Âge invalide

?>

3.6- Asymmetric Visibility (PHP 8.4)

Nouveauté PHP 8.4 : Visibilité différente pour lecture et écriture.

Syntaxe

<?php
declare(strict_types=1);


class Utilisateur {
    // Lecture : publique
    // Écriture : privée
    public private(set) string $email;
    public private(set) string $motDePasseHash;

    public function __construct(string $email, string $motDePasse) {
        $this->email = $email;
        $this->motDePasseHash = password_hash($motDePasse, PASSWORD_DEFAULT);
    }

    public function changerMotDePasse(string $ancienMotDePasse, string $nouveauMotDePasse): void {
        if (!password_verify($ancienMotDePasse, $this->motDePasseHash)) {
            throw new Exception("Mot de passe incorrect");
        }
        $this->motDePasseHash = password_hash($nouveauMotDePasse, PASSWORD_DEFAULT);
    }
}

$user = new Utilisateur("eva@example.com", "secret123");

// Lecture : OK (public)
echo $user->email;  // eva@example.com

// Écriture : ERREUR (private)
// $user->email = "hacker@evil.com";  // Cannot modify private(set) property

// Modification uniquement via méthode contrôlée
$user->changerMotDePasse("secret123", "newpassword456");

?>

3.7- Exemple Réel : Gestion de Stock de Produits

<?php
declare(strict_types=1);


class Produit {
    private string $reference;
    private string $nom;
    private float $prixHT;
    private int $stock;
    private const TVA = 0.20;  // 20%

    public function __construct(string $ref, string $nom, float $prixHT, int $stockInitial) {
        $this->reference = $ref;
        $this->setNom($nom);
        $this->setPrixHT($prixHT);
        $this->stock = max(0, $stockInitial);
    }

    // Getter simple
    public function getReference(): string {
        return $this->reference;
    }

    public function getNom(): string {
        return $this->nom;
    }

    // Setter avec validation
    public function setNom(string $nom): void {
        if (strlen($nom) < 3) {
            throw new InvalidArgumentException("Le nom doit contenir au moins 3 caractères");
        }
        $this->nom = $nom;
    }

    public function getPrixHT(): float {
        return $this->prixHT;
    }

    public function setPrixHT(float $prix): void {
        if ($prix <= 0) {
            throw new InvalidArgumentException("Le prix doit être positif");
        }
        $this->prixHT = $prix;
    }

    // Getter calculé
    public function getPrixTTC(): float {
        return $this->prixHT * (1 + self::TVA);
    }

    public function getStock(): int {
        return $this->stock;
    }

    // Méthodes métier avec validation
    public function ajouterStock(int $quantite): void {
        if ($quantite <= 0) {
            throw new InvalidArgumentException("La quantité doit être positive");
        }
        $this->stock += $quantite;
        echo "✓ {$quantite} unités ajoutées. Stock : {$this->stock}\n";
    }

    public function vendre(int $quantite): bool {
        if ($quantite <= 0) {
            throw new InvalidArgumentException("La quantité doit être positive");
        }

        if ($this->stock < $quantite) {
            echo "✗ Stock insuffisant ({$this->stock} disponibles)\n";
            return false;
        }

        $this->stock -= $quantite;
        $prixTotal = $this->getPrixTTC() * $quantite;
        echo "✓ Vente de {$quantite} unités. Total : {$prixTotal} FCFA TTC\n";
        echo "  Stock restant : {$this->stock}\n";
        return true;
    }

    public function afficherFiche(): void {
        echo "\n═══════════════════════════════\n";
        echo "Référence : {$this->reference}\n";
        echo "Nom : {$this->nom}\n";
        echo "Prix HT : {$this->prixHT} FCFA\n";
        echo "Prix TTC : {$this->getPrixTTC()} FCFA\n";
        echo "Stock : {$this->stock} unités\n";
        echo "═══════════════════════════════\n";
    }
}

// Utilisation
$produit = new Produit("REF-001", "Laptop Dell XPS", 1200, 50);
$produit->afficherFiche();

$produit->ajouterStock(10);
$produit->vendre(5);
$produit->vendre(100);  // Stock insuffisant
$produit->afficherFiche();

// Tentative de modification directe : ERREUR
// $produit->stock = 1000;  // Cannot access private property

?>

Sortie :

═══════════════════════════════
Référence : REF-001
Nom : Laptop Dell XPS
Prix HT : 1200 FCFA
Prix TTC : 1440 FCFA
Stock : 50 unités
═══════════════════════════════
✓ 10 unités ajoutées. Stock : 60
✓ Vente de 5 unités. Total : 7200 FCFA TTC
  Stock restant : 55
✗ Stock insuffisant (55 disponibles)

═══════════════════════════════
Référence : REF-001
Nom : Laptop Dell XPS
Prix HT : 1200 FCFA
Prix TTC : 1440 FCFA
Stock : 55 unités
═══════════════════════════════

4 - AGRÉGATION ET DÉCOMPOSITION

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.1- Définition de la Composition

La composition est une relation “a-un” (has-a) entre deux classes. Un objet contient d’autres objets comme parties de son état.

Types de Relations

  1. Composition : L’objet contenu n’existe pas sans son conteneur (dépendance forte)
  2. Agrégation : L’objet contenu peut exister indépendamment (dépendance faible)

4.2- Composition (Dépendance Forte)

Définition : L’objet contenu est créé et détruit avec son conteneur.

Exemple Réel : Voiture et Moteur

Une voiture a un moteur. Si la voiture est détruite, le moteur l’est aussi.

<?php
declare(strict_types=1);


class Moteur {
    public function __construct(
        private string $type,
        private int $puissance,
        private int $cylindree
    ) {}

    public function demarrer(): void {
        echo "Moteur {$this->type} ({$this->cylindree}cc, {$this->puissance}ch) : Vroum !\n";
    }

    public function afficherCaracteristiques(): void {
        echo "  - Type : {$this->type}\n";
        echo "  - Puissance : {$this->puissance} ch\n";
        echo "  - Cylindrée : {$this->cylindree} cc\n";
    }
}

class Voiture {
    private Moteur $moteur;  // Composition : la voiture POSSÈDE un moteur

    public function __construct(
        private string $marque,
        private string $modele,
        string $typeMoteur,
        int $puissance,
        int $cylindree
    ) {
        // Le moteur est créé EN MÊME TEMPS que la voiture
        $this->moteur = new Moteur($typeMoteur, $puissance, $cylindree);
    }

    public function demarrer(): void {
        echo "{$this->marque} {$this->modele} démarre...\n";
        $this->moteur->demarrer();
    }

    public function afficherDetails(): void {
        echo "\n╔═══════════════════════════════╗\n";
        echo "  {$this->marque} {$this->modele}\n";
        echo "╠═══════════════════════════════╣\n";
        echo "Caractéristiques du moteur :\n";
        $this->moteur->afficherCaracteristiques();
        echo "╚═══════════════════════════════╝\n";
    }
}

// Utilisation
$voiture = new Voiture("BMW", "M3", "V6 Essence", 510, 2993);
$voiture->demarrer();
$voiture->afficherDetails();

// Le moteur n'existe PAS indépendamment de la voiture
// On ne peut pas créer le moteur séparément puis l'attacher

?>

Sortie :

BMW M3 démarre...
Moteur V6 Essence (2993cc, 510ch) : Vroum !

╔═══════════════════════════════╗
  BMW M3
╠═══════════════════════════════╣
Caractéristiques du moteur :
  - Type : V6 Essence
  - Puissance : 510 ch
  - Cylindrée : 2993 cc
╚═══════════════════════════════╝

4.3- Agrégation (Dépendance Faible)

Définition : L’objet contenu existe indépendamment de son conteneur.

Exemple Réel : Université et Étudiants

Une université a des étudiants. Si l’université ferme, les étudiants continuent d’exister.

<?php
declare(strict_types=1);


class Etudiant {
    public function __construct(
        private string $matricule,
        private string $nom,
        private string $prenom,
        private string $niveau
    ) {}

    public function getMatricule(): string {
        return $this->matricule;
    }

    public function afficher(): void {
        echo "  • {$this->matricule} - {$this->prenom} {$this->nom} ({$this->niveau})\n";
    }
}

class Universite {
    private array $etudiants = [];

    public function __construct(
        private string $nom,
        private string $ville
    ) {}

    // Les étudiants sont AJOUTÉS (pas créés par l'université)
    public function inscrireEtudiant(Etudiant $etudiant): void {
        $this->etudiants[$etudiant->getMatricule()] = $etudiant;
        echo "✓ Étudiant inscrit à {$this->nom}\n";
    }

    public function desinscrireEtudiant(string $matricule): void {
        if (isset($this->etudiants[$matricule])) {
            unset($this->etudiants[$matricule]);
            echo "✓ Étudiant désinscrit de {$this->nom}\n";
        }
    }

    public function afficherEtudiants(): void {
        echo "\n╔═══════════════════════════════════════╗\n";
        echo "  {$this->nom} - {$this->ville}\n";
        echo "╠═══════════════════════════════════════╣\n";
        echo "Étudiants inscrits ({$this->compterEtudiants()}) :\n";

        if (empty($this->etudiants)) {
            echo "  Aucun étudiant inscrit.\n";
        } else {
            foreach ($this->etudiants as $etudiant) {
                $etudiant->afficher();
            }
        }
        echo "╚═══════════════════════════════════════╝\n";
    }

    public function compterEtudiants(): int {
        return count($this->etudiants);
    }
}

// Les étudiants existent AVANT l'université
$etudiant1 = new Etudiant("E001", "Dupont", "eva", "Licence 3");
$etudiant2 = new Etudiant("E002", "Martin", "Bob", "Master 1");
$etudiant3 = new Etudiant("E003", "Durand", "Clara", "Licence 2");

// L'université les agrège
$universite = new Universite("L'Ecole Supérieure la Canadienne", "Bafoussam");
$universite->inscrireEtudiant($etudiant1);
$universite->inscrireEtudiant($etudiant2);
$universite->inscrireEtudiant($etudiant3);

$universite->afficherEtudiants();

$universite->desinscrireEtudiant("E002");
$universite->afficherEtudiants();

// Les étudiants existent toujours après désinscription
echo "\nÉtudiant 2 existe toujours :\n";
$etudiant2->afficher();

?>

Sortie :

✓ Étudiant inscrit à L'Ecole Supérieure la Canadienne
✓ Étudiant inscrit à L'Ecole Supérieure la Canadienne
✓ Étudiant inscrit à L'Ecole Supérieure la Canadienne

╔═══════════════════════════════════════╗
  L'Ecole Supérieure la Canadienne - Bafoussam
╠═══════════════════════════════════════╣
Étudiants inscrits (3) :
  • E001 - Eva Myra (Licence 3)
  • E002 - Sohan Owen (Master 1)
  • E003 - Diane rosto (Licence 2)
╚═══════════════════════════════════════╝
✓ Étudiant désinscrit de L'Ecole Supérieure la Canadienne

╔═══════════════════════════════════════╗
  L'Ecole Supérieure la Canadienne - Bafoussam
╠═══════════════════════════════════════╣
Étudiants inscrits (2) :
  • E001 - Eva Myra (Licence 3)
  • E003 - Diane rosto (Licence 2)
╚═══════════════════════════════════════╝

Étudiant 2 existe toujours :
  • E002 - Sohan Owen (Master 1)

4.4- Décomposition en Classes

Principe : Diviser un système complexe en classes plus simples et cohésives.

Exemple Réel : Système de Commande E-commerce

<?php
declare(strict_types=1);

class Produit {
    public function __construct(
        private string $reference,
        private string $nom,
        private float $prix
    ) {}

    public function getReference(): string { return $this->reference; }
    public function getNom(): string { return $this->nom; }
    public function getPrix(): float { return $this->prix; }
}

class LigneCommande {
    public function __construct(
        private Produit $produit,
        private int $quantite
    ) {}

    public function calculerSousTotal(): float {
        return $this->produit->getPrix() * $this->quantite;
    }

    public function afficher(): void {
        $sousTotal = $this->calculerSousTotal();
        echo "  • {$this->produit->getNom()} x{$this->quantite} = {$sousTotal} FCFA\n";
    }
}

class Adresse {
    public function __construct(
        private string $rue,
        private string $ville,
        private string $codePostal,
        private string $pays
    ) {}

    public function afficher(): void {
        echo "{$this->rue}, {$this->codePostal} {$this->ville}, {$this->pays}";
    }
}

class Client {
    public function __construct(
        private string $nom,
        private string $prenom,
        private string $email,
        private Adresse $adresse
    ) {}

    public function getNomComplet(): string {
        return "{$this->prenom} {$this->nom}";
    }

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

    public function afficherAdresse(): void {
        $this->adresse->afficher();
    }
}

class Commande {
    private array $lignes = [];
    private string $numero;
    private DateTime $date;

    public function __construct(
        private Client $client
    ) {
        $this->numero = "CMD-" . uniqid();
        $this->date = new DateTime();
    }

    public function ajouterLigne(Produit $produit, int $quantite): void {
        $ligne = new LigneCommande($produit, $quantite);
        $this->lignes[] = $ligne;
        echo "✓ Produit ajouté à la commande\n";
    }

    public function calculerTotal(): float {
        $total = 0;
        foreach ($this->lignes as $ligne) {
            $total += $ligne->calculerSousTotal();
        }
        return $total;
    }

    public function afficherFacture(): void {
        echo "\n╔════════════════════════════════════════════╗\n";
        echo "              FACTURE\n";
        echo "╠════════════════════════════════════════════╣\n";
        echo "Commande N° : {$this->numero}\n";
        echo "Date : {$this->date->format('d/m/Y H:i')}\n";
        echo "\n";
        echo "Client : {$this->client->getNomComplet()}\n";
        echo "Email : {$this->client->getEmail()}\n";
        echo "Adresse : ";
        $this->client->afficherAdresse();
        echo "\n";
        echo "╠════════════════════════════════════════════╣\n";
        echo "Détail de la commande :\n";

        foreach ($this->lignes as $ligne) {
            $ligne->afficher();
        }

        echo "╠════════════════════════════════════════════╣\n";
        echo "TOTAL : {$this->calculerTotal()} FCFA\n";
        echo "╚════════════════════════════════════════════╝\n";
    }
}

// Utilisation
$adresse = new Adresse("123 Rue de la Paix", "Bafoussam", "75002", "France");
$client = new Client("Dupont", "eva", "eva@example.com", $adresse);

$produit1 = new Produit("P001", "Laptop Dell XPS", 1200);
$produit2 = new Produit("P002", "Souris Logitech", 45);
$produit3 = new Produit("P003", "Clavier mécanique", 150);

$commande = new Commande($client);
$commande->ajouterLigne($produit1, 1);
$commande->ajouterLigne($produit2, 2);
$commande->ajouterLigne($produit3, 1);

$commande->afficherFacture();

?>

Sortie :

✓ Produit ajouté à la commande
✓ Produit ajouté à la commande
✓ Produit ajouté à la commande

╔════════════════════════════════════════════╗
              FACTURE
╠════════════════════════════════════════════╣
Commande N° : CMD-67abc123def
Date : 26/10/2025 14:30

Client : Eva Myra
Email : eva@example.com
Adresse : 123 Rue de la Paix, 75002 Bafoussam, France
╠════════════════════════════════════════════╣
Détail de la commande :
  • Laptop Dell XPS x1 = 1200 FCFA
  • Souris Logitech x2 = 90 FCFA
  • Clavier mécanique x1 = 150 FCFA
╠════════════════════════════════════════════╣
TOTAL : 1440 FCFA
╚════════════════════════════════════════════╝

Avantages de la décomposition :

  • Chaque classe a une responsabilité unique
  • Code modulaire et réutilisable
  • Maintenance facilitée
  • Tests plus simples

5- GÉNÉRALISATION ET SPÉCIALISATION

5.1- Définition

  • Généralisation : Identifier les caractéristiques communes de plusieurs classes et les regrouper dans une classe parente (classe de base)
  • Spécialisation : Créer des classes enfants qui ajoutent des comportements spécifiques

5.2- Le Concept

Analogie du Monde Réel : Les Animaux

Observation :

  • Un chien, un chat et un oiseau sont tous des animaux
  • Tous ont un nom, un âge
  • Tous peuvent manger, dormir
  • Mais chacun a des comportements spécifiques :
    • Le chien aboie
    • Le chat miaule
    • L’oiseau vole

Solution POO :

  1. Généralisation : Créer une classe Animal avec les caractéristiques communes
  2. Spécialisation : Créer des classes Chien, Chat, Oiseau qui héritent de Animal

5.3 Hiérarchie de Classes

         Animal (classe parente générale)
            |
    ┌───────┼───────┐
    |       |       |
  Chien   Chat   Oiseau (classes enfants spécialisées)

Exemple Sans Généralisation (Répétition)

<?php
declare(strict_types=1);

// MAUVAISE APPROCHE : Répétition de code
class Chien {
    private string $nom;
    private int $age;

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

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

    public function aboyer(): void {
        echo "{$this->nom} : Woof woof !\n";
    }
}

class Chat {
    private string $nom;  // RÉPÉTITION
    private int $age;     // RÉPÉTITION

    public function manger(): void {  // RÉPÉTITION
        echo "{$this->nom} mange.\n";
    }

    public function dormir(): void {  // RÉPÉTITION
        echo "{$this->nom} dort.\n";
    }

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

// Beaucoup de code dupliqué !

?>

5.4- Exemple Avec Généralisation

<?php
declare(strict_types=1);

// Classe parente GÉNÉRALE
class Animal {
    public function __construct(
        protected string $nom,  // protected = accessible dans les enfants
        protected int $age
    ) {}

    // Comportements COMMUNS à tous les animaux
    public function manger(): void {
        echo "{$this->nom} mange.\n";
    }

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

    public function afficherInfos(): void {
        echo "Animal : {$this->nom}, {$this->age} ans\n";
    }
}

// Classes enfants SPÉCIALISÉES
class Chien extends Animal {
    // Comportement SPÉCIFIQUE au chien
    public function aboyer(): void {
        echo "{$this->nom} : Woof woof !\n";
    }
}

class Chat extends Animal {
    // Comportement SPÉCIFIQUE au chat
    public function miauler(): void {
        echo "{$this->nom} : Miaou !\n";
    }
}

class Oiseau extends Animal {
    // Comportement SPÉCIFIQUE à l'oiseau
    public function voler(): void {
        echo "{$this->nom} vole dans le ciel.\n";
    }
}

// Utilisation
$chien = new Chien("Rex", 3);
$chat = new Chat("Whiskers", 2);
$oiseau = new Oiseau("Tweety", 1);

// Comportements communs (hérités)
$chien->manger();
$chat->dormir();
$oiseau->afficherInfos();

// Comportements spécifiques
$chien->aboyer();
$chat->miauler();
$oiseau->voler();


?>

Sortie :

Rex mange.
Whiskers dort.
Animal : Tweety, 1 ans
Rex : Woof woof !
Whiskers : Miaou !
Tweety vole dans le ciel.


5.5- Exemple Réel : Système de Gestion d’Employés

<?php
declare(strict_types=1);

// Classe GÉNÉRALE : Employé - PHP 8.3+
class Employe {
    public function __construct(
        protected string $matricule,
        protected string $nom,
        protected string $prenom,
        protected float $salaireBase
    ) {}

    // Méthode commune
    public function afficherFiche(): void {
        echo "\n┌─────────────────────────────┐\n";
        echo "│ Matricule : {$this->matricule}\n";
        echo "│ Nom : {$this->prenom} {$this->nom}\n";
        echo "│ Salaire : {$this->calculerSalaire()} FCFA\n";
        echo "└─────────────────────────────┘\n";
    }

    // Méthode de base (sera redéfinie dans les enfants)
    public function calculerSalaire(): float {
        return $this->salaireBase;
    }
}

// Classe SPÉCIALISÉE : Développeur
class Developpeur extends Employe {
    public function __construct(
        string $matricule,
        string $nom,
        string $prenom,
        float $salaireBase,
        private string $langage,
        private int $anneesExperience
    ) {
        parent::__construct($matricule, $nom, $prenom, $salaireBase);
    }

    // REDÉFINITION : calcul spécifique
    public function calculerSalaire(): float {
        $bonus = $this->anneesExperience * 100;
        return $this->salaireBase + $bonus;
    }

    // Méthode SPÉCIFIQUE
    public function coder(): void {
        echo "{$this->prenom} code en {$this->langage}...\n";
    }
}

// Classe SPÉCIALISÉE : Manager
class Manager extends Employe {
    public function __construct(
        string $matricule,
        string $nom,
        string $prenom,
        float $salaireBase,
        private int $nombreSubordonnes
    ) {
        parent::__construct($matricule, $nom, $prenom, $salaireBase);
    }

    // REDÉFINITION : calcul spécifique
    public function calculerSalaire(): float {
        $prime = $this->nombreSubordonnes * 200;
        return $this->salaireBase + $prime;
    }

    // Méthode SPÉCIFIQUE
    public function organiserReunion(): void {
        echo "{$this->prenom} organise une réunion avec {$this->nombreSubordonnes} personnes.\n";
    }
}

// Classe SPÉCIALISÉE : Commercial
class Commercial extends Employe {
    public function __construct(
        string $matricule,
        string $nom,
        string $prenom,
        float $salaireBase,
        private float $chiffreAffaires
    ) {
        parent::__construct($matricule, $nom, $prenom, $salaireBase);
    }

    // REDÉFINITION : calcul spécifique
    public function calculerSalaire(): float {
        $commission = $this->chiffreAffaires * 0.05;  // 5% de commission
        return $this->salaireBase + $commission;
    }

    // Méthode SPÉCIFIQUE
    public function prospecter(): void {
        echo "{$this->prenom} contacte des clients potentiels.\n";
    }
}

// Utilisation
$dev = new Developpeur("D001", "Dupont", "eva", 3000, "PHP", 5);
$manager = new Manager("M001", "Martin", "Bob", 4000, 10);
$commercial = new Commercial("C001", "Durand", "Clara", 2500, 50000);

$dev->afficherFiche();
$dev->coder();

$manager->afficherFiche();
$manager->organiserReunion();

$commercial->afficherFiche();
$commercial->prospecter();

?>

Sortie :

┌─────────────────────────────┐
│ Matricule : D001
│ Nom : Eva Myra
│ Salaire : 3500 FCFA
└─────────────────────────────┘
eva code en PHP...

┌─────────────────────────────┐
│ Matricule : M001
│ Nom : Sohan Owen
│ Salaire : 6000 FCFA
└─────────────────────────────┘
Bob organise une réunion avec 10 personnes.

┌─────────────────────────────┐
│ Matricule : C001
│ Nom : Diane rosto
│ Salaire : 5000 FCFA
└─────────────────────────────┘
Clara contacte des clients potentiels.

6- HÉRITAGE

6.1- Définition et Types

L’héritage est un mécanisme qui permet à une classe (classe enfant) de réutiliser les attributs et méthodes d’une autre classe (classe parente).

Relation “EST-UN” (is-a)

  • Un Chien EST UN Animal
  • Un Développeur EST UN Employé
  • Une Voiture EST UN Véhicule

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 de l’Héritage

<?php
declare(strict_types=1);

class ClasseParente {
    // Code de la classe parente
}

class ClasseEnfant extends ClasseParente {
    // Code de la classe enfant
    // + hérite de tout le code de la classe parente
}

?>

6.3- Le Mot-clé parent

Le mot-clé parent:: permet d’accéder aux méthodes et constantes de la classe parente.

<?php
declare(strict_types=1);

class Vehicule {
    public function __construct(
        protected string $marque,
        protected int $vitesseMax
    ) {
        echo "Création d'un véhicule {$this->marque}\n";
    }

    public function demarrer(): void {
        echo "Le véhicule démarre.\n";
    }
}

class Voiture extends Vehicule {
    public function __construct(
        string $marque,
        int $vitesseMax,
        private int $nombrePortes
    ) {
        // Appel du constructeur parent
        parent::__construct($marque, $vitesseMax);
        echo "C'est une voiture à {$this->nombrePortes} portes.\n";
    }

    public function demarrer(): void {
        // Appel de la méthode parente
        parent::demarrer();
        echo "Contact, clé tournée, moteur lancé !\n";
    }

    public function ouvrirCoffre(): void {
        echo "Coffre ouvert.\n";
    }
}

$voiture = new Voiture("Peugeot", 180, 5);
$voiture->demarrer();
$voiture->ouvrirCoffre();

?>

Sortie :

Création d'un véhicule Peugeot
C'est une voiture à 5 portes.
Le véhicule démarre.
Contact, clé tournée, moteur lancé !
Coffre ouvert.

6.4- Redéfinition de Méthodes (Override)

Une classe enfant peut redéfinir (override) une méthode héritée pour changer son comportement.

Attribute #[Override] (PHP 8.3+)

<?php
declare(strict_types=1);

class Forme {
    public function __construct(
        protected string $couleur
    ) {}

    public function afficher(): void {
        echo "Je suis une forme {$this->couleur}.\n";
    }

    public function calculerAire(): float {
        return 0.0;
    }
}

class Rectangle extends Forme {
    public function __construct(
        string $couleur,
        private float $largeur,
        private float $hauteur
    ) {
        parent::__construct($couleur);
    }

    #[Override]  // PHP 8.3+ : indique qu'on redéfinit une méthode parente
    public function afficher(): void {
        parent::afficher();  // Appel de la méthode parente
        echo "C'est un rectangle de {$this->largeur}x{$this->hauteur}.\n";
    }

    #[Override]
    public function calculerAire(): float {
        return $this->largeur * $this->hauteur;
    }
}

class Cercle extends Forme {
    public function __construct(
        string $couleur,
        private float $rayon
    ) {
        parent::__construct($couleur);
    }

    #[Override]
    public function afficher(): void {
        parent::afficher();
        echo "C'est un cercle de rayon {$this->rayon}.\n";
    }

    #[Override]
    public function calculerAire(): float {
        return pi() * $this->rayon ** 2;
    }
}

$rectangle = new Rectangle("rouge", 5, 3);
$cercle = new Cercle("bleu", 4);

$rectangle->afficher();
echo "Aire : {$rectangle->calculerAire()} unités²\n\n";

$cercle->afficher();
echo "Aire : " . round($cercle->calculerAire(), 2) . " unités²\n";

?>

Sortie :

Je suis une forme rouge.
C'est un rectangle de 5x3.
Aire : 15 unités²

Je suis une forme bleu.
C'est un cercle de rayon 4.
Aire : 50.27 unités²

6.5- Les Niveaux de Visibilité et l’Héritage

Visibilité Dans la classe Dans les enfants Hors de la classe
public ✅ Oui ✅ Oui ✅ Oui
protected ✅ Oui ✅ Oui ❌ Non
private ✅ Oui ❌ Non ❌ Non
<?php
declare(strict_types=1);

class Parent {
    public string $public = "Public";
    protected string $protected = "Protected";
    private string $private = "Private";

    public function tester(): void {
        echo $this->public;      // ✅ OK
        echo $this->protected;   // ✅ OK
        echo $this->private;     // ✅ OK
    }
}

class Enfant extends Parent {
    public function tester(): void {
        echo $this->public;      // ✅ OK
        echo $this->protected;   // ✅ OK
        // echo $this->private;  // ❌ ERREUR : Cannot access private property
    }
}

$enfant = new Enfant();
echo $enfant->public;       // ✅ OK
// echo $enfant->protected; // ❌ ERREUR
// echo $enfant->private;   // ❌ ERREUR

?>

Prêt à passer à la suite ?

Vous avez terminé cette leçon sur Programmation Orientée Objet. Continuez votre apprentissage avec la prochaine leçon ou explorez d'autres formations.