Cours de Génie Logiciel
Thème :
Junior
1 vues ·11/13 leçonsProgrammation 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
- Polymorphisme de surcharge (Compile-time)
- Polymorphisme de redéfinition (Runtime)
- 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.