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

Programmation Orientée Objet

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

📚 Objectifs du cours

  • Maîtriser les concepts fondamentaux de la programmation orientée objet (POO)
  • Comprendre et appliquer les principes de conception orientée objet
  • Développer des applications modulaires et réutilisables
  • Implémenter les concepts POO dans différents langages (PHP 8.4, C++, Java)

1. Introduction aux concepts orientés objets

1.1 Paradigme orienté objet

Définition : La POO est un paradigme de programmation basé sur le concept d’objets qui contiennent des données (attributs) et du code (méthodes).

Avantages principaux :

  • Modularité : Code organisé en unités autonomes
  • Réutilisabilité : Composants réutilisables
  • Maintenance : Facilité de modification et d’évolution
  • Abstraction : Représentation simplifiée du monde réel

1.2 Concepts fondamentaux

// Exemple conceptuel en Java
public class VoitureConceptuelle {
    // Données (État)
    private String marque;
    private int vitesse;

    // Comportements (Méthodes)
    public void accelerer() {
        vitesse += 10;
    }
}

2. Objets et Classes

2.1 Définitions

  • Classe : Modèle ou blueprint définissant la structure et le comportement
  • Objet : Instance concrète d’une classe

2.2 Implémentation dans les trois langages

PHP 8.4

<?php
class Personne {
    // Propriétés avec types (PHP 8+)
    private string $nom;
    private int $age;

    // Constructeur avec promotion de propriétés (PHP 8+)
    public function __construct(
        private readonly string $id = uniqid()
    ) {}

    // Méthodes
    public function sePresenter(): string {
        return "Je suis {$this->nom}, j'ai {$this->age} ans";
    }
}

// Création d'objets
$personne1 = new Personne();
$personne2 = new Personne();
?>

C++

#include <iostream>
#include <string>

class Personne {
private:
    std::string nom;
    int age;

public:
    // Constructeur
    Personne(std::string n, int a) : nom(n), age(a) {}

    // Destructeur
    ~Personne() {}

    // Méthode
    void sePresenter() {
        std::cout << "Je suis " << nom << ", j'ai " << age << " ans" << std::endl;
    }
};

// Utilisation
int main() {
    Personne personne1("Alice", 25);
    Personne* personne2 = new Personne("Bob", 30);

    personne1.sePresenter();
    personne2->sePresenter();

    delete personne2;
    return 0;
}

Java

public class Personne {
    // Attributs
    private String nom;
    private int age;

    // Constructeur
    public Personne(String nom, int age) {
        this.nom = nom;
        this.age = age;
    }

    // Méthode
    public void sePresenter() {
        System.out.println("Je suis " + nom + ", j'ai " + age + " ans");
    }

    // Main pour tester
    public static void main(String[] args) {
        Personne personne1 = new Personne("Alice", 25);
        Personne personne2 = new Personne("Bob", 30);

        personne1.sePresenter();
        personne2.sePresenter();
    }
}

3. Encapsulation et masquage d’information

3.1 Principe

L’encapsulation consiste à :

  • Regrouper les données et les méthodes dans une classe
  • Contrôler l’accès aux membres de la classe
  • Masquer les détails d’implémentation

3.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

3.3 Exemple complet d’encapsulation

PHP 8.4

<?php
class CompteBancaire {
    private float $solde = 0.0;
    private readonly string $numeroCompte;
    private array $historique = [];

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

    // Getter avec validation
    public function getSolde(): float {
        return $this->solde;
    }

    // Setter avec logique métier
    public function deposer(float $montant): void {
        if ($montant <= 0) {
            throw new InvalidArgumentException("Montant invalide");
        }
        $this->solde += $montant;
        $this->ajouterHistorique("Dépôt: $montant");
    }

    public function retirer(float $montant): void {
        if ($montant > $this->solde) {
            throw new Exception("Solde insuffisant");
        }
        $this->solde -= $montant;
        $this->ajouterHistorique("Retrait: $montant");
    }

    private function ajouterHistorique(string $operation): void {
        $this->historique[] = date('Y-m-d H:i:s') . " - $operation";
    }

    // Propriété calculée (PHP 8.1+)
    public function getHistorique(): array {
        return array_slice($this->historique, -10); // Dernières 10 opérations
    }
}
?>

4. Agrégation et Décomposition

4.1 Agrégation (relation “a un”)

Définition : Relation où un objet contient des références à d’autres objets, mais ces derniers peuvent exister indépendamment.

Exemple en Java

import java.util.*;

// Classe Etudiant peut exister sans Université
class Etudiant {
    private String nom;
    private String matricule;

    public Etudiant(String nom, String matricule) {
        this.nom = nom;
        this.matricule = matricule;
    }

    public String getNom() { return nom; }
}

// Agrégation : Université "a" des étudiants
class Universite {
    private String nom;
    private List<Etudiant> etudiants;

    public Universite(String nom) {
        this.nom = nom;
        this.etudiants = new ArrayList<>();
    }

    // Les étudiants existent indépendamment
    public void inscrireEtudiant(Etudiant etudiant) {
        etudiants.add(etudiant);
    }

    public void desinscrireEtudiant(Etudiant etudiant) {
        etudiants.remove(etudiant);
    }
}

4.2 Composition (relation forte)

Définition : Relation où l’objet contenant est responsable du cycle de vie des objets contenus.

Exemple en C++

#include <vector>
#include <memory>

class Moteur {
private:
    int cylindree;
    int puissance;
public:
    Moteur(int cyl, int puiss) : cylindree(cyl), puissance(puiss) {}
};

class Roue {
private:
    int diametre;
public:
    Roue(int d) : diametre(d) {}
};

// Composition : Voiture possède et crée ses composants
class Voiture {
private:
    std::unique_ptr<Moteur> moteur;  // Composition forte
    std::vector<std::unique_ptr<Roue>> roues;

public:
    Voiture(int cylindree, int puissance) {
        // La voiture crée ses propres composants
        moteur = std::make_unique<Moteur>(cylindree, puissance);

        // Création des 4 roues
        for(int i = 0; i < 4; i++) {
            roues.push_back(std::make_unique<Roue>(17));
        }
    }
    // Destructeur automatique grâce aux smart pointers
};

5. Généralisation et Spécialisation

5.1 Concept

  • Généralisation : Processus d’extraction des caractéristiques communes
  • Spécialisation : Processus de création de classes plus spécifiques

5.2 Hiérarchie de classes

PHP 8.4

<?php
// Classe générale
abstract class Vehicule {
    protected string $marque;
    protected string $modele;
    protected float $vitesseMax;

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

    abstract public function demarrer(): void;
    abstract public function calculerConsommation(): float;

    public function afficherInfo(): void {
        echo "{$this->marque} {$this->modele}";
    }
}

// Spécialisation niveau 1
class VehiculeTerrestre extends Vehicule {
    protected int $nombreRoues;

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

    public function demarrer(): void {
        echo "Démarrage du moteur terrestre";
    }
}

// Spécialisation niveau 2
class Voiture extends VehiculeTerrestre {
    private int $nombrePortes;

    public function __construct(string $marque, string $modele, int $portes) {
        parent::__construct($marque, $modele, 4);
        $this->nombrePortes = $portes;
    }

    public function calculerConsommation(): float {
        return 7.5; // L/100km
    }
}

class Moto extends VehiculeTerrestre {
    private string $type; // Sport, Cruiser, etc.

    public function __construct(string $marque, string $modele, string $type) {
        parent::__construct($marque, $modele, 2);
        $this->type = $type;
    }

    public function calculerConsommation(): float {
        return 4.5; // L/100km
    }
}
?>

6. Héritage

6.1 Types d’héritage

  • Simple : Une classe hérite d’une seule classe parent
  • Multiple : Une classe hérite de plusieurs classes (C++)
  • Multiniveau : Chaîne d’héritage A → B → C

6.2 Implémentation de l’héritage

Java - Héritage et Interfaces

// Interface pour capacités multiples
interface Volant {
    void decoller();
    void atterrir();
    default double calculerAltitudeMax() {
        return 10000; // mètres par défaut
    }
}

interface Flottant {
    void naviguer();
    void jeterAncre();
}

// Classe de base
class Vehicule {
    protected String marque;
    protected double prix;

    public Vehicule(String marque, double prix) {
        this.marque = marque;
        this.prix = prix;
    }

    public void afficherInfo() {
        System.out.println(marque + " - Prix: " + prix);
    }
}

// Héritage simple
class Avion extends Vehicule implements Volant {
    private int nombreMoteurs;

    public Avion(String marque, double prix, int moteurs) {
        super(marque, prix);
        this.nombreMoteurs = moteurs;
    }

    @Override
    public void decoller() {
        System.out.println("L'avion décolle avec " + nombreMoteurs + " moteurs");
    }

    @Override
    public void atterrir() {
        System.out.println("L'avion atterrit");
    }
}

// Héritage multiple via interfaces
class Hydravion extends Avion implements Flottant {
    public Hydravion(String marque, double prix) {
        super(marque, prix, 2);
    }

    @Override
    public void naviguer() {
        System.out.println("L'hydravion navigue sur l'eau");
    }

    @Override
    public void jeterAncre() {
        System.out.println("L'hydravion jette l'ancre");
    }
}

C++ - Héritage multiple

#include <iostream>

class Terrestre {
protected:
    int vitesseMaxSol;
public:
    Terrestre(int v) : vitesseMaxSol(v) {}
    virtual void rouler() {
        std::cout << "Roule à " << vitesseMaxSol << " km/h" << std::endl;
    }
};

class Aquatique {
protected:
    int vitesseMaxEau;
public:
    Aquatique(int v) : vitesseMaxEau(v) {}
    virtual void naviguer() {
        std::cout << "Navigue à " << vitesseMaxEau << " noeuds" << std::endl;
    }
};

// Héritage multiple
class VehiculeAmphibie : public Terrestre, public Aquatique {
private:
    std::string nom;
public:
    VehiculeAmphibie(std::string n, int vt, int ve)
        : Terrestre(vt), Aquatique(ve), nom(n) {}

    void changerMode() {
        std::cout << nom << " change de mode" << std::endl;
    }

    // Résolution d'ambiguïté si nécessaire
    using Terrestre::rouler;
    using Aquatique::naviguer;
};

7. Polymorphisme et Liaison Dynamique

7.1 Types de polymorphisme

  1. Polymorphisme de surcharge (Compile-time)
  2. Polymorphisme de redéfinition (Runtime)
  3. Polymorphisme paramétrique (Génériques/Templates)

7.2 Polymorphisme de surcharge

Java

public class Calculatrice {
    // Surcharge de méthodes
    public int additionner(int a, int b) {
        return a + b;
    }

    public double additionner(double a, double b) {
        return a + b;
    }

    public int additionner(int a, int b, int c) {
        return a + b + c;
    }

    public String additionner(String a, String b) {
        return a + b; // Concaténation
    }
}

7.3 Polymorphisme de redéfinition et liaison dynamique

PHP 8.4

<?php
abstract class Forme {
    protected string $couleur;

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

    // Méthode abstraite
    abstract public function calculerAire(): float;

    // Méthode virtuelle
    public function dessiner(): void {
        echo "Dessin d'une forme {$this->couleur}";
    }
}

class Rectangle extends Forme {
    private float $largeur;
    private float $hauteur;

    public function __construct(string $couleur, float $l, float $h) {
        parent::__construct($couleur);
        $this->largeur = $l;
        $this->hauteur = $h;
    }

    // Redéfinition obligatoire
    public function calculerAire(): float {
        return $this->largeur * $this->hauteur;
    }

    // Redéfinition optionnelle
    public function dessiner(): void {
        echo "Rectangle {$this->couleur} de {$this->largeur}x{$this->hauteur}";
    }
}

class Cercle extends Forme {
    private float $rayon;

    public function __construct(string $couleur, float $r) {
        parent::__construct($couleur);
        $this->rayon = $r;
    }

    public function calculerAire(): float {
        return M_PI * pow($this->rayon, 2);
    }

    public function dessiner(): void {
        echo "Cercle {$this->couleur} de rayon {$this->rayon}";
    }
}

// Utilisation du polymorphisme
class GestionnaireFormes {
    private array $formes = [];

    public function ajouterForme(Forme $forme): void {
        $this->formes[] = $forme;
    }

    public function dessinerTout(): void {
        foreach ($this->formes as $forme) {
            $forme->dessiner(); // Liaison dynamique
            echo " - Aire: " . $forme->calculerAire() . PHP_EOL;
        }
    }

    public function calculerAireTotale(): float {
        return array_reduce($this->formes,
            fn($total, $forme) => $total + $forme->calculerAire(),
            0.0
        );
    }
}

// Démonstration
$gestionnaire = new GestionnaireFormes();
$gestionnaire->ajouterForme(new Rectangle("rouge", 10, 5));
$gestionnaire->ajouterForme(new Cercle("bleu", 3));
$gestionnaire->ajouterForme(new Rectangle("vert", 7, 7));

$gestionnaire->dessinerTout();
echo "Aire totale: " . $gestionnaire->calculerAireTotale();
?>

7.4 Polymorphisme paramétrique (Génériques/Templates)

C++ - Templates

#include <iostream>
#include <vector>

template<typename T>
class Pile {
private:
    std::vector<T> elements;

public:
    void empiler(T element) {
        elements.push_back(element);
    }

    T depiler() {
        if (elements.empty()) {
            throw std::runtime_error("Pile vide");
        }
        T top = elements.back();
        elements.pop_back();
        return top;
    }

    bool estVide() const {
        return elements.empty();
    }

    size_t taille() const {
        return elements.size();
    }
};

// Utilisation avec différents types
int main() {
    Pile<int> pileEntiers;
    pileEntiers.empiler(10);
    pileEntiers.empiler(20);

    Pile<std::string> pileChaines;
    pileChaines.empiler("Hello");
    pileChaines.empiler("World");

    return 0;
}

Java - Generics

import java.util.*;

public class Pile<T> {
    private LinkedList<T> elements;

    public Pile() {
        elements = new LinkedList<>();
    }

    public void empiler(T element) {
        elements.addLast(element);
    }

    public T depiler() {
        if (elements.isEmpty()) {
            throw new EmptyStackException();
        }
        return elements.removeLast();
    }

    public boolean estVide() {
        return elements.isEmpty();
    }

    // Méthode générique avec contrainte
    public <E extends Comparable<E>> E maximum(E x, E y, E z) {
        E max = x;
        if (y.compareTo(max) > 0) max = y;
        if (z.compareTo(max) > 0) max = z;
        return max;
    }
}

8. Exemple complet : PHP 8.4

Système de gestion d’entreprise

<?php
declare(strict_types=1);

// Utilisation des énumérations (PHP 8.1+)
enum TypeEmploye: string {
    case DEVELOPPEUR = 'developpeur';
    case MANAGER = 'manager';
    case DESIGNER = 'designer';
}

// Trait pour fonctionnalités réutilisables
trait Identifiable {
    private readonly string $id;

    protected function genererID(): string {
        return uniqid('EMP_');
    }

    public function getID(): string {
        return $this->id;
    }
}

// Interface
interface Payable {
    public function calculerSalaire(): float;
    public function genererFichePaie(): array;
}

// Classe abstraite de base
abstract class Employe implements Payable {
    use Identifiable;

    protected string $nom;
    protected string $email;
    protected float $salaireBase;
    protected TypeEmploye $type;

    public function __construct(
        string $nom,
        string $email,
        float $salaireBase,
        TypeEmploye $type
    ) {
        $this->id = $this->genererID();
        $this->nom = $nom;
        $this->email = $email;
        $this->salaireBase = $salaireBase;
        $this->type = $type;
    }

    abstract public function calculerBonus(): float;

    public function calculerSalaire(): float {
        return $this->salaireBase + $this->calculerBonus();
    }

    public function genererFichePaie(): array {
        return [
            'id' => $this->id,
            'nom' => $this->nom,
            'type' => $this->type->value,
            'salaire_base' => $this->salaireBase,
            'bonus' => $this->calculerBonus(),
            'salaire_total' => $this->calculerSalaire()
        ];
    }
}

// Classes concrètes
class Developpeur extends Employe {
    private array $langages;
    private int $lignesCode = 0;

    public function __construct(string $nom, string $email, float $salaire, array $langages) {
        parent::__construct($nom, $email, $salaire, TypeEmploye::DEVELOPPEUR);
        $this->langages = $langages;
    }

    public function calculerBonus(): float {
        // Bonus basé sur le nombre de langages maîtrisés
        return count($this->langages) * 500;
    }

    public function coder(int $lignes): void {
        $this->lignesCode += $lignes;
        echo "{$this->nom} a codé {$lignes} lignes. Total: {$this->lignesCode}\n";
    }
}

class Manager extends Employe {
    private array $equipe = [];

    public function __construct(string $nom, string $email, float $salaire) {
        parent::__construct($nom, $email, $salaire, TypeEmploye::MANAGER);
    }

    public function ajouterMembre(Employe $employe): void {
        $this->equipe[] = $employe;
    }

    public function calculerBonus(): float {
        // Bonus basé sur la taille de l'équipe
        return count($this->equipe) * 1000;
    }

    public function calculerBudgetEquipe(): float {
        $budget = $this->calculerSalaire();
        foreach ($this->equipe as $membre) {
            $budget += $membre->calculerSalaire();
        }
        return $budget;
    }
}

// Classe de gestion avec pattern Singleton
class GestionRH {
    private static ?GestionRH $instance = null;
    private array $employes = [];

    private function __construct() {}

    public static function getInstance(): GestionRH {
        if (self::$instance === null) {
            self::$instance = new GestionRH();
        }
        return self::$instance;
    }

    public function embaucher(Employe $employe): void {
        $this->employes[$employe->getID()] = $employe;
        echo "Embauche de {$employe->nom} (ID: {$employe->getID()})\n";
    }

    public function calculerMasseSalariale(): float {
        $total = 0;
        foreach ($this->employes as $employe) {
            $total += $employe->calculerSalaire();
        }
        return $total;
    }

    public function genererRapport(): void {
        echo "\n=== RAPPORT RH ===\n";
        echo "Nombre d'employés: " . count($this->employes) . "\n";
        echo "Masse salariale totale: " . $this->calculerMasseSalariale() . "€\n\n";

        foreach ($this->employes as $employe) {
            $fiche = $employe->genererFichePaie();
            echo "- {$fiche['nom']} ({$fiche['type']}): {$fiche['salaire_total']}€\n";
        }
    }
}

// Utilisation
$rh = GestionRH::getInstance();

// Création d'employés
$dev1 = new Developpeur("Alice", "alice@company.com", 3500, ['PHP', 'JavaScript', 'Python']);
$dev2 = new Developpeur("Bob", "bob@company.com", 3200, ['Java', 'C++']);
$manager = new Manager("Charlie", "charlie@company.com", 4500);

// Configuration de l'équipe
$manager->ajouterMembre($dev1);
$manager->ajouterMembre($dev2);

// Embauches
$rh->embaucher($dev1);
$rh->embaucher($dev2);
$rh->embaucher($manager);

// Actions
$dev1->coder(1500);
$dev2->coder(2000);

echo "\nBudget équipe de {$manager->nom}: " . $manager->calculerBudgetEquipe() . "€\n";

// Rapport final
$rh->genererRapport();

// Utilisation des nouvelles fonctionnalités PHP 8.4
// Pattern matching (si disponible en PHP 8.4)
function traiterEmploye(Employe $emp): string {
    return match($emp->type) {
        TypeEmploye::DEVELOPPEUR => "Coder des fonctionnalités",
        TypeEmploye::MANAGER => "Gérer l'équipe",
        TypeEmploye::DESIGNER => "Créer des designs",
    };
}

echo "\n=== TÂCHES PRINCIPALES ===\n";
foreach([$dev1, $dev2, $manager] as $emp) {
    echo "{$emp->nom}: " . traiterEmploye($emp) . "\n";
}
?>

📋 Exercices pratiques

Exercice 1 : Système de bibliothèque

Créer un système de gestion de bibliothèque avec :

  • Classes : Livre, Magazine, DVD (héritage de Media)
  • Gestion des emprunts avec dates
  • Calcul des amendes pour retard
  • Pattern Observer pour notifications

Exercice 2 : Jeu de stratégie

Implémenter un mini-jeu avec :

  • Classes abstraites : Unite, Batiment
  • Interfaces : Attaquable, Constructible
  • Polymorphisme pour les actions
  • Factory Pattern pour création d’unités

Exercice 3 : Système bancaire

Développer un système bancaire avec :

  • Différents types de comptes (épargne, courant, titre)
  • Transactions avec validation
  • Pattern Strategy pour calcul d’intérêts
  • Gestion des exceptions personnalisées

📚 Ressources supplémentaires

Livres recommandés

  • “Design Patterns” - Gang of Four
  • “Clean Code” - Robert C. Martin
  • “Effective Java” - Joshua Bloch
  • “Modern C++ Design” - Andrei Alexandrescu

Documentation officielle

Outils pratiques

  • UML : PlantUML, draw.io pour diagrammes de classes
  • IDE : PhpStorm, IntelliJ IDEA, Visual Studio
  • Tests : PHPUnit, JUnit, Google Test

🎯 Évaluation

Répartition des notes

  • Contrôle continu (40%)
    • TP notés : 20%
    • Projet mini : 20%
  • Projet final (30%)
    • Application complète utilisant tous les concepts
  • Examen final (30%)
    • Questions théoriques
    • Analyse de code
    • Conception UML

Critères d’évaluation

  • Maîtrise des concepts POO
  • Qualité du code (lisibilité, documentation)
  • Application des principes SOLID
  • Utilisation appropriée des design patterns
  • Gestion des erreurs et exceptions

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.