Classes, Encapsulation, Héritage, Composition en action
Construisons un système avec des classes qui collaborent
1. Récapituler la semaine
Classes, encapsulation, héritage, composition
2. Concevoir un système
Plusieurs classes qui collaborent
3. Séparation des responsabilités
Appliquer sur un vrai projet
4. Bon design
Chaque classe a UN seul job
Récap de la semaine
Classes, encapsulation (#), héritage, composition
Présentation du projet
Mini-jeu RPG avec des classes qui collaborent
Design du système
Personnage, Inventaire, Combat
GRAINE D'ARCHITECTURE
Combat ne modifie pas Personnage directement
Live coding
Construire le squelette ensemble
Le moule pour créer des objets
Une classe = propriétés + méthodes
Les objets sont des instances de cette classe
Protéger les données internes d'une classe
class Personnage {
#pointsDeVie = 100;
recevoirDegats(montant) {
this.pointsDeVie -= montant;
}
}
💡 On ne modifie jamais pointsDeVie directement → on passe par une méthode!
Une classe enfant hérite des propriétés et méthodes du parent
class Guerrier extends Personnage {
#force = 10;
attaquer() {
return this.force;
}
}
✅ Guerrier a automatiquement pointsDeVie et recevoirDegats()
Une classe contient d'autres classes
"Un Personnage a un Inventaire" (pas "est un")
class Personnage {
#inventaire;
constructor() {
this.inventaire = new Inventaire();
}
}
💡 Composition vs Héritage : Privilégiez la composition!
Plus flexible, moins de couplage
Un jeu de combat en console
⚔️
Combat
Tour par tour
👤
Personnage
Héros & ennemis
🎒
Inventaire
Objets & potions
👤 Personnage
Gère l'état du héros
• Points de vie
• Force, défense
• Méthodes : attaquer(), recevoirDegats(), estVivant()
🎒 Inventaire
Gère les objets
• Liste d'objets
• Méthodes : ajouter(), utiliser(), aDesPotions()
⚔️ Combat
Orchestre le combat
• Gère les tours
• Méthodes : lancerTour(), determinerVainqueur()
Responsabilité unique : Gérer l'état du héros
class Personnage {
#nom;
#pointsDeVie;
#force;
attaquer() {
return this.force;
}
recevoirDegats(montant) {
this.pointsDeVie -= montant;
}
estVivant() {
return this.pointsDeVie > 0;
}
}
✅ Toutes les propriétés sont privées (#) → encapsulation!
Responsabilité unique : Gérer les objets
class Inventaire {
#objets = [];
ajouter(objet) {
this.objets.push(objet);
}
utiliser(objet) {
// Retirer l'objet de la liste
}
aDesPotions() {
return this.objets.includes('potion');
}
}
✅ L'inventaire ne sait pas ce qu'est un personnage → découplage!
Responsabilité unique : Orchestre le combat
class Combat {
#joueur;
#ennemi;
lancerTour() {
const degats = this.joueur.attaquer();
this.ennemi.recevoirDegats(degats);
}
determinerVainqueur() {
// Logique de fin de combat
}
}
Le principe fondamental
Combat ne modifie PAS Personnage directement
Il appelle des méthodes → Séparation des responsabilités
Combat accède directement aux propriétés privées
class Combat {
lancerTour() {
// ❌ INTERDIT!
this.ennemi.pointsDeVie -= 10;
}
}
⚠️ pointsDeVie est privé (#) → Combat ne peut pas y accéder!
C'est le test ultime de l'encapsulation
Combat appelle les méthodes de Personnage
class Combat {
lancerTour() {
const degats = this.joueur.attaquer();
this.ennemi.recevoirDegats(degats);
}
}
✅ Personnage contrôle comment ses données sont modifiées
C'est exactement le pattern Use Case + Aggregate
⚔️ Combat = Use Case
Orchestre le flux
• Gère les tours
• Coordonne les actions
• Ne contient pas de logique métier
👤 Personnage = Aggregate
Protège son état
• Encapsule ses données
• Expose des méthodes publiques
• Valide les invariants
✅ Ce pattern se retrouve partout dans les applications professionnelles!
Construisons le squelette ensemble
Je construis la base, vous complétez
Chaque classe dans un fichier séparé
/src
├── Personnage.js
├── Inventaire.js
├── Combat.js
└── main.js // Point d'entrée
💡 Une classe = un fichier → organisation claire!
❌ Tout mettre dans une seule classe
"Pourquoi se compliquer? Une classe RPG suffit!"
✅ Guidez vers la séparation des responsabilités
❌ Combat accède aux propriétés privées
"Mais c'est plus simple de modifier pointsDeVie directement!"
✅ C'est le test ultime de l'encapsulation!
💡 Le projet semble ambitieux
Rassurez : on construit pas à pas!
✅ Commencez par Personnage, puis Inventaire, enfin Combat
Chaque classe a UN seul job
Personnage = état | Inventaire = objets | Combat = orchestration
Combat ne touche PAS aux propriétés privées
Il appelle des méthodes → Personnage contrôle son état
Pattern Use Case + Aggregate
Use Case orchestre, Aggregate protège son état
Bon design = classes simples qui collaborent
Pas une classe géante qui fait tout!
Encapsulation
Protéger les données avec private/protected
Séparation
Chaque classe a UN seul job
Collaboration
Les classes travaillent ensemble via des méthodes
Pattern
Use Case + Aggregate = architecture professionnelle
Un bon design émerge quand chaque classe a UN job
C'est la clé du code maintenable et évolutif!
Complétez le mini-jeu RPG
1. Créez la classe Personnage
Avec nom, pointsDeVie, force et les méthodes attaquer(), recevoirDegats(), estVivant()
2. Créez la classe Inventaire
Avec objets et les méthodes ajouter(), utiliser(), aDesPotions()
3. Créez la classe Combat
Avec lancerTour() qui appelle les méthodes de Personnage (pas d'accès direct aux propriétés!)
4. Testez dans main.js
Créez 2 personnages, lancez un combat, affichez le vainqueur
L'OOP, c'est des classes simples qui collaborent
Pratiquez avec l'exercice RPG!
Encapsulation, Séparation, Collaboration → les piliers du bon design