Leçon 1/8 9 min

Pourquoi la POO ?

Sortir du code spaghetti en regroupant données et comportements dans des objets : le problème que la POO résout.

Le problème : le code spaghetti

Au début, un programme est petit : quelques variables, quelques fonctions, tout tient dans la tête. Puis il grossit. Et arrive le moment où tu as des données un peu partout et des fonctions qui se les passent dans tous les sens. Tu changes une variable ici, et quelque chose casse trois fichiers plus loin. C'est ce qu'on appelle le code spaghetti : tout est emmêlé, plus rien n'est isolé.

Prenons un exemple tout simple, un compte en banque, écrit « à plat » (sans objets) :

// Les données du compte vivent dans des variables séparées
let solde = 100;
let titulaire = "Alice";

// Et les fonctions doivent se faire passer ces données à la main
function deposer(soldeActuel, montant) {
  return soldeActuel + montant;
}
function retirer(soldeActuel, montant) {
  return soldeActuel - montant;   // oups : rien n'empêche un solde négatif
}

solde = deposer(solde, 50);   // 150
solde = retirer(solde, 999);  // -849 !  personne n'a vérifié

Deux problèmes sautent aux yeux. D'abord, les données (le solde, le titulaire) et les actions (déposer, retirer) sont séparées : rien ne dit qu'elles forment un tout. Ensuite, n'importe qui peut bricoler le solde directement, sans passer par aucun garde-fou. Plus le programme grandit, plus ça devient ingérable.

L'idée de la POO : regrouper ce qui va ensemble

La programmation orientée objet (POO) part d'une intuition simple : et si les données et les actions qui les concernent vivaient au même endroit, dans une petite boîte autonome ? Cette boîte, c'est un objet. Un objet a un état (ses données, qu'on appelle ses propriétés : un solde, un titulaire) et des comportements (ce qu'il sait faire, qu'on appelle ses méthodes : déposer, retirer). Le programme devient alors une collection d'objets qui collaborent, chacun responsable de ses propres affaires.

À gauche, sans objets : les données (solde, titulaire) et les fonctions (déposer, retirer) sont éparpillées et reliées par des flèches emmêlées. À droite, avec un objet : un seul bloc « Compte » contient ses données ET ses actions. Sans objets : éparpillé Avec un objet : regroupé solde titulaire historique déposer() retirer() Compte DONNÉES solde · titulaire historique ACTIONS deposer() retirer()
Un objet, c'est une boîte qui range ensemble ses données et les actions qui agissent dessus. Tout ce qui concerne le compte vit dans le compte.
Prédisez avant de lire

On gère un compte avec des variables séparées let titulaire = "Alice"; let solde = 0; et des fonctions libres function deposer(...). Avant de dérouler : si demain on doit gérer 100 comptes distincts, qu'est-ce qui casse avec cette approche, et qu'est-ce qu'une classe résout ?

Voir la réponse

Avec des variables globales, il n'y a qu'un seul état partagé : impossible d'avoir 100 soldes indépendants sans dupliquer les variables à la main. Rien ne garantit non plus que deposer agit sur le bon compte, puisqu'elle reçoit les données de l'extérieur. Une classe résout les deux problèmes : c'est un moule réinstanciable. Chaque appel à new Compte(...) crée un objet distinct, portant son propre état (son propre solde, son propre titulaire) et ses propres méthodes qui agissent dessus via this. Cent comptes, cent objets indépendants, une seule définition de classe.

Réécrivons le compte, version objet (en pseudo-code, valable dans presque tous les langages) :

objet Compte:
    donnée solde = 100
    donnée titulaire = "Alice"

    action deposer(montant):
        solde = solde + montant

    action retirer(montant):
        si montant > solde:
            refuser   // le garde-fou vit DANS l'objet
        sinon:
            solde = solde - montant

// On manipule le compte par ses actions, jamais le solde en direct
monCompte.deposer(50)
monCompte.retirer(999)   // refusé proprement : l'objet protège son solde

La différence n'est pas qu'esthétique. Le solde et les règles qui le protègent sont au même endroit. Pour utiliser un compte, je n'ai pas besoin de savoir comment il fonctionne à l'intérieur : je lui demande de déposer ou de retirer, et lui s'occupe de respecter ses propres règles.

Voici la même idée en vrai JavaScript exécutable : deux comptes créés depuis la même classe, chacun avec son propre solde. Clique sur Run et observe que le dépôt sur Alice ne touche pas Bob.

class Compte {
  constructor(titulaire) {
    this.titulaire = titulaire;
    this.solde = 0;
  }
  deposer(montant) {
    this.solde += montant;
  }
}

const compteAlice = new Compte("Alice");
const compteBob   = new Compte("Bob");

compteAlice.deposer(200);
compteBob.deposer(50);

console.log(compteAlice.titulaire, "→ solde :", compteAlice.solde);
console.log(compteBob.titulaire,   "→ solde :", compteBob.solde);
console.log("Dépôt supplémentaire sur Alice...");
compteAlice.deposer(100);
console.log(compteAlice.titulaire, "→ solde :", compteAlice.solde);
console.log(compteBob.titulaire,   "→ solde (inchangé) :", compteBob.solde);

Ce que la POO t'apporte (et ce qu'elle n'est pas)

  • Du code rangé : chaque objet est responsable de ses données. On sait où regarder.
  • Des garde-fous au bon endroit : les règles vivent avec les données qu'elles protègent.
  • Des morceaux réutilisables : un objet bien fait se réutilise et se teste isolément.
  • Un vocabulaire commun : les mêmes idées se retrouvent en PHP, Java, Python, JavaScript, C#…

Une idée fausse à éviter dès maintenant : la POO n'est pas « modéliser le monde réel ». On entend souvent « un chien EST un animal, donc faites une classe Chien qui hérite d'Animal ». C'est un piège : à trop vouloir copier la réalité, on fabrique des usines à gaz. La POO sert à organiser le code, pas à recopier l'univers. On y reviendra avec l'héritage et la composition, là où ce piège fait le plus de dégâts.

Les piliers qu'on va explorer

Maintenant qu'on tient l'intuition (regrouper données + comportements dans des objets), la suite du cours déplie les grandes idées de la POO, une par une, toujours avec des analogies et des schémas :

  • Objet et classe : la distinction qui bloque tout le monde au début (le moule et les gâteaux).
  • L'encapsulation : cacher l'intérieur, n'exposer qu'une porte contrôlée.
  • L'héritage et la composition : réutiliser… sans se tirer une balle dans le pied.
  • Le polymorphisme : un même message, des réponses différentes selon l'objet.

The problem: spaghetti code

At first, a program is small: a few variables, a few functions, it all fits in your head. Then it grows. And the moment comes when you have data scattered everywhere and functions passing it around in every direction. You change one variable here, and something breaks three files away. That's spaghetti code: everything is tangled, nothing is isolated.

Take a very simple example, a bank account, written "flat" (without objects):

// The account's data lives in separate variables
let balance = 100;
let owner = "Alice";

// And functions must be handed that data by hand
function deposit(currentBalance, amount) {
  return currentBalance + amount;
}
function withdraw(currentBalance, amount) {
  return currentBalance - amount;   // oops: nothing prevents a negative balance
}

balance = deposit(balance, 50);   // 150
balance = withdraw(balance, 999);  // -849 !  nobody checked

Two problems jump out. First, the data (balance, owner) and the actions (deposit, withdraw) are separate: nothing says they form a whole. Second, anyone can tamper with the balance directly, without going through any safeguard. The bigger the program, the more unmanageable this gets.

The OOP idea: group what belongs together

Object-oriented programming (OOP) starts from a simple intuition: what if the data and the actions that concern it lived in the same place, in a small self-contained box? That box is an object. An object has a state (its data, called its properties: a balance, an owner) and behaviors (what it can do, called its methods: deposit, withdraw). The program then becomes a collection of collaborating objects, each responsible for its own affairs.

On the left, without objects: data (balance, owner) and functions (deposit, withdraw) are scattered and linked by tangled arrows. On the right, with an object: a single "Account" block holds its data AND its actions. Without objects: scattered With an object: grouped balance owner history deposit() withdraw() Account DATA balance · owner history ACTIONS deposit() withdraw()
An object is a box that stores together its data and the actions that act on it. Everything about the account lives inside the account.
Predict before reading

We manage an account with separate variables let owner = "Alice"; let balance = 0; and free functions function deposit(...). Before you expand: if tomorrow we need to manage 100 distinct accounts, what breaks with this approach, and what does a class fix?

See the answer

With global variables there is only one shared state: impossible to have 100 independent balances without duplicating the variables by hand. Nothing guarantees that deposit acts on the right account either, since it receives data from the outside. A class fixes both problems: it is a reusable mold. Each call to new Account(...) creates a distinct object carrying its own state (its own balance, its own owner) and its own methods that act on it via this. A hundred accounts, a hundred independent objects, a single class definition.

Let's rewrite the account, object version (in pseudo-code, valid in almost every language):

object Account:
    data balance = 100
    data owner = "Alice"

    action deposit(amount):
        balance = balance + amount

    action withdraw(amount):
        if amount > balance:
            refuse   // the safeguard lives INSIDE the object
        else:
            balance = balance - amount

// We work the account through its actions, never the balance directly
myAccount.deposit(50)
myAccount.withdraw(999)   // cleanly refused: the object protects its balance

The difference isn't just cosmetic. The balance and the rules that protect it are in the same place. To use an account, I don't need to know how it works inside: I ask it to deposit or withdraw, and it takes care of enforcing its own rules.

Here is the same idea in real, runnable JavaScript: two accounts created from the same class, each with its own balance. Hit Run and see that depositing into Alice's account does not touch Bob's.

class Account {
  constructor(owner) {
    this.owner = owner;
    this.balance = 0;
  }
  deposit(amount) {
    this.balance += amount;
  }
}

const aliceAccount = new Account("Alice");
const bobAccount   = new Account("Bob");

aliceAccount.deposit(200);
bobAccount.deposit(50);

console.log(aliceAccount.owner, "→ balance:", aliceAccount.balance);
console.log(bobAccount.owner,   "→ balance:", bobAccount.balance);
console.log("Extra deposit on Alice...");
aliceAccount.deposit(100);
console.log(aliceAccount.owner, "→ balance:", aliceAccount.balance);
console.log(bobAccount.owner,   "→ balance (unchanged):", bobAccount.balance);

What OOP gives you (and what it isn't)

  • Tidy code: each object is responsible for its data. You know where to look.
  • Safeguards in the right place: the rules live with the data they protect.
  • Reusable pieces: a well-built object can be reused and tested in isolation.
  • A common vocabulary: the same ideas show up in PHP, Java, Python, JavaScript, C#…

A misconception to avoid right now: OOP is not "modeling the real world". You often hear "a dog IS an animal, so make a Dog class that inherits from Animal". That's a trap: trying too hard to copy reality, you build over-engineered messes. OOP is for organizing code, not for copying the universe. We'll come back to this with inheritance and composition, where the trap does the most damage.

The pillars we'll explore

Now that we have the intuition (group data + behavior into objects), the rest of the course unfolds the big OOP ideas, one by one, always with analogies and diagrams:

  • Object and class: the distinction that trips everyone up at first (the mold and the cakes).
  • Encapsulation: hide the inside, expose only a controlled door.
  • Inheritance and composition: reuse… without shooting yourself in the foot.
  • Polymorphism: one message, different responses depending on the object.

🎯 Pratique

S'entraîner (clique pour ouvrir) :

🧠 Rappel libre
Rappel libre

Sans remonter dans la leçon : quel problème la POO résout-elle, et qu'est-ce qu'un objet regroupe ? Reprends l'exemple du compte en banque pour l'illustrer.

La POO résout le code spaghetti : quand les données traînent à un endroit et les fonctions qui les manipulent à un autre, tout finit emmêlé. Un objet regroupe au même endroit ses données (l'état, ses propriétés) et ses comportements (les actions, ses méthodes). Pour le compte : le solde et le titulaire vivent dans l'objet, et c'est lui qui porte deposer() et retirer() ; le garde-fou (refuser un retrait trop gros) vit DANS l'objet, à côté de la donnée qu'il protège.
⚖️ Juge le code de l'IA
Accepter ou rejeter le code de l'IA

Tu demandes à l'IA de modéliser un compte en banque. Elle te propose ce design. Ton rôle de relecteur : l'accepter tel quel ou le rejeter, et dire pourquoi.

objet Compte:
    donnée solde = 100

// Pas d'action sur le compte : tout se fait depuis l'extérieur
function deposer(compte, montant) {
    compte.solde = compte.solde + montant;
}
function retirer(compte, montant) {
    compte.solde = compte.solde - montant;
}

retirer(monCompte, 999)   // monCompte.solde = -899, personne n'a vérifié
À rejeter. C'est un objet de façade : il porte la donnée (solde) mais aucune action. Les fonctions deposer et retirer vivent dehors et tripotent compte.solde directement, exactement le code spaghetti que la POO devait ranger. Conséquence : rien ne protège le solde, d'où le -899. Le réflexe juste : remettre deposer() et retirer() DANS l'objet, avec le garde-fou à côté de la donnée qu'il protège, et ne jamais toucher solde de l'extérieur.
Qu'est-ce qu'un objet, en POO ?
Quel est le principal défaut du code « à plat » de l'exemple du compte ?
Quel est l'intérêt principal de regrouper données et comportements dans une classe ?
Tu dois gérer 100 comptes bancaires. Pourquoi une class Compte est-elle préférable à 100 jeux de variables globales (solde1, solde2…) ?
Prochaine étape

Tu vois pourquoi la POO existe et le problème qu'elle résout. Place aux deux mots qui fondent tout le reste : l'objet et la classe, le moule et les gâteaux qu'on en sort.

Leçon 2 : Objet et classe →

Une erreur dans cette leçon, un passage flou, une question ? Écrivez-moi : chaque retour améliore ce cours.

Besoin d'un développeur pour votre projet ?

Réponse sous 24h · Sans engagement