POO : classes et objets
La programmation orientée objet (POO) organise un programme autour d’objets qui regroupent des données (état) et les opérations qui s’appliquent à ces données (comportement). Cette rubrique utilise TypeScript pour les exemples (voir TypeScript : installation) ; les idées valent pour Java, C#, Python, PHP, etc. (voir La POO en JavaScript, Python et PHP). Les exemples utilisent des noms génériques.
Vocabulaire
Section intitulée « Vocabulaire »| Terme | Sens |
|---|---|
| Classe | Modèle qui décrit la structure et le comportement d’un type d’objet |
| Objet (instance) | Réalisation concrète d’une classe, avec son propre état |
| Attribut (propriété, champ) | Donnée stockée dans l’objet |
| Méthode | Fonction qui s’applique à l’objet |
| Constructeur | Méthode appelée à la création, qui initialise l’état |
| Encapsulation | Cacher l’état interne, n’exposer que ce qui est utile |
| Héritage | Une classe reprend et étend une autre |
| Polymorphisme | Des objets différents répondent à la même interface |
| Abstraction | Se concentrer sur ce qu’un objet fait, pas sur la façon dont il le fait |
Une première classe
Section intitulée « Une première classe »class Compte { titulaire: string; solde: number;
constructor(titulaire: string, soldeInitial = 0) { this.titulaire = titulaire; this.solde = soldeInitial; }
deposer(montant: number): void { this.solde += montant; }
decrire(): string { return `${this.titulaire} : ${this.solde.toFixed(2)} €`; }}
const compte = new Compte('Alice', 100);compte.deposer(50);console.log(compte.decrire()); // Alice : 150.00 €| Élément | Rôle |
|---|---|
class Compte |
Définit le modèle |
titulaire, solde |
Attributs (déclarés avec leur type) |
constructor |
Initialise l’objet à sa création (new) |
this |
L’objet courant, dans les méthodes |
deposer, decrire |
Méthodes |
new Compte(...) |
Crée une instance |
Chaque objet a son propre état : new Compte('Bob') n’affecte pas le compte d’Alice.
Propriétés de paramètre (raccourci)
Section intitulée « Propriétés de paramètre (raccourci) »class Client { constructor( public readonly id: number, public nom: string, private email: string, ) {}
contact(): string { return `${this.nom} <${this.email}>`; }}Déclarer un modificateur (public, private, readonly) devant un paramètre du constructeur crée et initialise l’attribut. Cette syntaxe génère du code propre à TypeScript : elle n’est pas prise en charge par l’exécution directe de Node.js sans compilation (voir TypeScript : installation) ; écrire alors les attributs explicitement.
this : ce qu’il faut savoir
Section intitulée « this : ce qu’il faut savoir »this désigne l’objet sur lequel la méthode est appelée. Il se perd quand on détache la méthode :
class Minuteur { secondes = 0;
incrementer() { this.secondes += 1; }}
const m = new Minuteur();const detachee = m.incrementer;detachee(); // erreur à l'exécution : « this » vaut undefinedSolutions :
class Minuteur { secondes = 0;
// 1. fonction fléchée : this est lié à l'instance incrementer = () => { this.secondes += 1; };}
// 2. lier au moment de l'usagesetInterval(m.incrementer.bind(m), 1000);setInterval(() => m.incrementer(), 1000);Les fonctions fléchées comme attributs (incrementer = () => …) créent une fonction par instance : pratique pour des rappels (événements, minuteurs), un peu plus coûteux en mémoire.
Membres statiques
Section intitulée « Membres statiques »Appartiennent à la classe, pas aux instances :
class Identifiant { private static compteur = 0;
static suivant(): number { Identifiant.compteur += 1; return Identifiant.compteur; }}
console.log(Identifiant.suivant()); // 1console.log(Identifiant.suivant()); // 2Usages : fabriques, constantes, compteurs. Un état statique est un état global déguisé : à utiliser avec prudence, il rend les tests plus difficiles (voir Tests et pièges courants).
Accesseurs : get et set
Section intitulée « Accesseurs : get et set »class Temperature { private celsius = 0;
get fahrenheit(): number { return this.celsius * 1.8 + 32; }
set fahrenheit(valeur: number) { this.celsius = (valeur - 32) / 1.8; }
get valeur(): number { return this.celsius; }}
const t = new Temperature();t.fahrenheit = 212;console.log(t.valeur); // 100Un accesseur se lit comme une propriété, mais exécute du code : validation, calcul, valeur dérivée. Un attribut avec accesseur permet de changer l’implémentation interne sans modifier les appelants.
Membres privés
Section intitulée « Membres privés »Deux mécanismes existent en TypeScript :
class Coffre { private code: string; // contrôle à la COMPILATION seulement #secret = 42; // privé au sens de JavaScript : inaccessible à l'EXÉCUTION
constructor(code: string) { this.code = code; }
ouvrir(essai: string): boolean { return essai === this.code; }
secret(): number { return this.#secret; }}
const c = new Coffre('1234');c.code; // erreur de compilation(c as any).code; // accessible à l'exécution !private (TypeScript) |
#champ (JavaScript) |
|
|---|---|---|
| Vérifié | À la compilation seulement | À l’exécution |
Accessible via as any ou JavaScript |
Oui | Non |
| Usage | Contrôle de conception | Véritable confidentialité |
Voir Encapsulation et héritage.
Attributs en lecture seule
Section intitulée « Attributs en lecture seule »class Commande { readonly numero: string; readonly creeLe: Date = new Date();
constructor(numero: string) { this.numero = numero; // autorisé dans le constructeur }
renommer() { this.numero = 'autre'; // erreur : lecture seule }}readonly impose l’immutabilité de la référence (pas du contenu d’un objet ou tableau). Des objets immuables simplifient le raisonnement (pas de modification surprise).
Initialisation des attributs
Section intitulée « Initialisation des attributs »class Panier { articles: string[] = []; // valeur initiale à la déclaration total!: number; // « ! » : promesse d'initialisation (à éviter) note?: string; // facultatif : string | undefined}Avec strict, un attribut sans valeur initiale ni affectation dans le constructeur déclenche l’erreur TS2564. Préférer une valeur initiale ou l’affectation dans le constructeur à l’assertion !.
Objets et littéraux : quand une classe ?
Section intitulée « Objets et littéraux : quand une classe ? »const point = { x: 1, y: 2 }; // objet simple : données seulement
class Rectangle { // classe : données + comportement constructor( public largeur: number, public hauteur: number, ) {} aire() { return this.largeur * this.hauteur; }}| Besoin | Outil |
|---|---|
| Transporter des données (réponse d’API, configuration) | Objet simple + interface ou type |
| Regrouper état et comportement qui doivent rester cohérents | Classe |
| Un comportement sans état | Fonction |
| Plusieurs variantes interchangeables | Interface + classes (voir Polymorphisme et interfaces) |
En TypeScript et JavaScript, tout n’a pas à être une classe : des fonctions et des objets simples suffisent souvent. La POO est un outil, pas une obligation.
Exemple : un petit modèle
Section intitulée « Exemple : un petit modèle »class Article { constructor( public readonly reference: string, public readonly libelle: string, public readonly prixCentimes: number, ) {}}
class LignePanier { constructor( public readonly article: Article, public quantite: number, ) {}
get totalCentimes(): number { return this.article.prixCentimes * this.quantite; }}
class Panier { private lignes = new Map<string, LignePanier>();
ajouter(article: Article, quantite = 1): void { const ligne = this.lignes.get(article.reference); if (ligne) ligne.quantite += quantite; else this.lignes.set(article.reference, new LignePanier(article, quantite)); }
retirer(reference: string): void { this.lignes.delete(reference); }
get totalCentimes(): number { let total = 0; for (const ligne of this.lignes.values()) total += ligne.totalCentimes; return total; }}
const panier = new Panier();panier.ajouter(new Article('A12', 'Clavier', 4900), 1);panier.ajouter(new Article('B07', 'Souris', 2500), 2);console.log((panier.totalCentimes / 100).toFixed(2), '€'); // 99.00 €Chaque classe a une responsabilité : Article décrit un produit, LignePanier associe un article à une quantité, Panier gère l’ensemble. Les montants sont en centimes (entiers) pour éviter les erreurs d’arrondi des nombres à virgule.
Dépannage
Section intitulée « Dépannage »| Symptôme | Piste |
|---|---|
this est undefined dans une méthode |
Méthode détachée de l’objet : fonction fléchée, bind, ou appeler via l’objet |
TS2564 has no initializer |
Valeur initiale ou affectation dans le constructeur |
Property 'x' is private and only accessible within class |
Accès extérieur à un membre private : méthode publique |
| Deux instances partagent un tableau | Tableau déclaré hors de la classe, ou valeur statique : initialiser dans le constructeur ou à la déclaration (une valeur par instance) |
Class constructor X cannot be invoked without 'new' |
Appel sans new |
| Propriétés de paramètre refusées à l’exécution | Syntaxe propre à TypeScript non prise en charge par l’exécution directe de Node.js |
readonly modifié quand même |
readonly ne contrôle que la référence, pas le contenu ; Object.freeze pour figer un objet |
Ubuntu 24.04 et 26.04 : différences à connaître
Section intitulée « Ubuntu 24.04 et 26.04 : différences à connaître »Aucune.