Aller au contenu

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.

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

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 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 undefined

Solutions :

class Minuteur {
secondes = 0;
// 1. fonction fléchée : this est lié à l'instance
incrementer = () => {
this.secondes += 1;
};
}
// 2. lier au moment de l'usage
setInterval(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.

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()); // 1
console.log(Identifiant.suivant()); // 2

Usages : 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).

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); // 100

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

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.

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

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

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.

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.

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.