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.
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.
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.
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.
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.
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.
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.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é
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.You see why OOP exists and the problem it solves. Now the two words everything else is built on: the object and the class, the mould and the cakes you bake from it.
Lesson 2: Object and class →