Lesson 3/6 10 min

Encapsulation: the capsule

Hide the data inside the object and expose only a controlled door. Why protecting your state changes everything, with an interactive demo.

FR EN

Le problème : la porte grande ouverte

À la leçon précédente, on fabriquait des comptes avec une méthode deposer() qui contient les règles. Sauf que rien n'empêche d'écrire, ailleurs dans le code :

compte.solde = 999999;   // on contourne deposer() et toutes ses règles
compte.solde = -500;     // un solde négatif ? pourquoi pas, personne ne vérifie

Si n'importe qui peut modifier le solde directement, tes garde-fous ne servent à rien. Le jour où un bout de code (le tien, ou celui d'un collègue) écrit une valeur incohérente, tu auras un bug impossible à retrouver : qui a mis ce solde à -500, et ? C'est exactement le genre de chaos que la POO veut éviter.

L'idée : cacher l'intérieur, n'exposer qu'une porte

L'encapsulation, c'est fermer l'objet : ses données deviennent privées (inaccessibles de l'extérieur), et la seule façon d'agir dessus est de passer par ses méthodes publiques, qui imposent les règles. L'objet devient une capsule : on voit les boutons, pas les rouages.

L'image juste, c'est le distributeur de boissons. Tu appuies sur un bouton (méthode publique), la machine te sort une canette. Tu n'ouvres pas la machine pour prendre une canette à la main, et tu ne touches pas à la caisse à l'intérieur (donnée privée). L'interface est réduite et contrôlée : c'est ce qui rend la machine fiable.

L'objet Compte protège sa donnée privée solde derrière un mur ; on ne peut y agir que par les méthodes publiques deposer et retirer. Un accès direct à solde est refusé. objet Compte privé solde = 120 méthodes publiques (les portes) deposer() retirer() compte.solde = 999999 compte .deposer(50) La donnée est protégée ; on n'agit dessus que par des portes qui imposent les règles.
La donnée privée est à l'abri ; les méthodes publiques sont les seules portes, et elles imposent les règles.

En code : private, public, et les accesseurs

On marque les données privées (souvent private, ou un # en JavaScript) et les méthodes utiles publiques. Pour lire une donnée privée sans permettre de l'écrire n'importe comment, on expose un accesseur (un « getter ») :

classe Compte:
    privé solde = 0          // inaccessible de l'extérieur

    public deposer(montant):
        si montant > 0:       // la règle vit ici, impossible à contourner
            this.solde += montant

    public retirer(montant):
        si montant > 0 et montant <= this.solde:
            this.solde -= montant

    public lireSolde():       // accesseur : on peut LIRE, pas écrire
        retourne this.solde

compte.solde = 999999        // ❌ refusé : solde est privé
compte.deposer(50)           // ✅ seule voie autorisée

Bénéfice immédiat : il n'existe qu'un seul endroit où le solde change (les méthodes). Un solde incohérent devient impossible, pas juste « pas censé arriver ». Et tu peux changer l'intérieur (stocker en centimes, ajouter un journal…) sans rien casser dehors, tant que les portes ne changent pas.

La capsule en vrai (essaie de tricher)

Ce Compte garde son solde en privé (un vrai champ privé JS #solde). Essaie la voie normale… puis essaie de tricher en écrivant le solde directement.

solde (lecture seule) 0 €

Ce que l'encapsulation t'apporte

  • Des invariants garantis : un solde négatif devient impossible, pas juste déconseillé.
  • Un seul endroit à débugger : si le solde est faux, le coupable est forcément dans une méthode de la classe.
  • La liberté de changer l'intérieur : tant que les méthodes publiques gardent la même signature, tu refais la cuisine interne sans casser le reste du programme.

Règle d'or : expose le minimum. Données privées par défaut, méthodes publiques seulement pour ce dont l'extérieur a vraiment besoin. Une petite surface publique = un objet facile à comprendre, à utiliser et à faire évoluer.

Accepter ou rejeter la conception de l'IA

Tu demandes à l'IA d'« encapsuler » la classe Compte. Elle te rend ceci. Ton rôle de relecteur : l'accepter telle quelle ou la rejeter, et dire pourquoi.

class Compte {
  #solde = 0;
  get solde(){ return this.#solde; }
  set solde(v){ this.#solde = v; }   // setter public, sans contrôle
}

compte.solde = -500;   // passe : le setter ne vérifie rien
Rejeter. Le champ #solde est bien privé, mais le set solde(v) rouvre la porte en grand : depuis l'extérieur, compte.solde = -500 passe sans aucun contrôle. C'est de la fausse encapsulation : on a juste déplacé l'accès direct dans un setter sans règle. Un getter en lecture seule, oui ; un setter public sans validation revient à rendre la donnée publique. Garde le get, supprime le set, et fais passer les écritures par deposer() / retirer() qui imposent les invariants.
Rappel libre

Sans remonter dans la leçon : qu'est-ce que l'encapsulation, et pourquoi rendre solde privé protège-t-il mieux qu'un simple commentaire « ne pas toucher » ?

L'encapsulation cache les données d'un objet (elles deviennent privées) et n'autorise l'accès que par des méthodes publiques qui imposent les règles. Rendre solde privé est plus fort qu'un commentaire parce que c'est garanti par le langage : compte.solde = -500 est refusé, pas juste déconseillé. Résultat : un seul endroit où le solde change, donc un invariant garanti et un seul endroit à débugger.
À quoi sert l'encapsulation ?
Pourquoi rendre une donnée privée plutôt que publique ?
Prochaine étape

Tes objets sont solides et bien fermés. Maintenant, comment créer un CompteÉpargne qui fait presque comme un Compte, mais avec un taux d'intérêt en plus, sans tout réécrire ? C'est le rôle de l'héritage.

Leçon 4 : l'héritage →

The problem: the wide-open door

In the previous lesson, we built accounts with a deposer() method holding the rules. Except nothing stops you from writing, elsewhere in the code:

account.solde = 999999;   // we bypass deposer() and all its rules
account.solde = -500;     // a negative balance? sure, nobody checks

If anyone can change the balance directly, your safeguards are useless. The day some code (yours, or a colleague's) writes an inconsistent value, you'll have a bug that's impossible to track: who set this balance to -500, and where? That's exactly the chaos OOP wants to avoid.

The idea: hide the inside, expose only a door

Encapsulation means closing the object: its data becomes private (unreachable from outside), and the only way to act on it is through its public methods, which enforce the rules. The object becomes a capsule: you see the buttons, not the gears.

The right image is the vending machine. You press a button (public method), the machine hands you a can. You don't open the machine to grab a can by hand, and you don't touch the cash inside (private data). The interface is small and controlled: that's what makes the machine reliable.

The Compte object protects its private balance behind a wall; you can only act on it through the public methods deposit and withdraw. A direct access to the balance is refused. Account object private balance = 120 public methods (the doors) deposit() withdraw() account.balance = 999999 account .deposit(50) The data is protected; you act on it only through doors that enforce the rules.
The private data is safe; the public methods are the only doors, and they enforce the rules.

In code: private, public, and accessors

We mark the data private (often private, or a # in JavaScript) and the useful methods public. To read a private value without allowing it to be written carelessly, we expose an accessor (a "getter"):

class Account:
    private balance = 0      // unreachable from outside

    public deposit(amount):
        if amount > 0:        // the rule lives here, impossible to bypass
            this.balance += amount

    public withdraw(amount):
        if amount > 0 and amount <= this.balance:
            this.balance -= amount

    public getBalance():      // accessor: you can READ, not write
        return this.balance

account.balance = 999999     // ❌ refused: balance is private
account.deposit(50)          // ✅ the only allowed way

Immediate benefit: there is a single place where the balance changes (the methods). An inconsistent balance becomes impossible, not just "shouldn't happen". And you can change the inside (store cents, add a log…) without breaking anything outside, as long as the doors stay the same.

The capsule for real (try to cheat)

This Compte keeps its balance private (a real JS private field #solde). Try the normal way… then try to cheat by writing the balance directly.

solde (read-only) 0 €

What encapsulation gives you

  • Guaranteed invariants: a negative balance becomes impossible, not just discouraged.
  • A single place to debug: if the balance is wrong, the culprit is necessarily inside a method of the class.
  • Freedom to change the inside: as long as the public methods keep the same signature, you redo the internal plumbing without breaking the rest of the program.

Golden rule: expose the minimum. Data private by default, public methods only for what the outside truly needs. A small public surface = an object that's easy to understand, use and evolve.

Next step

Your objects are solid and well-closed. Now, how do you create a SavingsAccount that behaves almost like an Account, but with an extra interest rate, without rewriting everything? That's the role of inheritance.

Lesson 4: inheritance →
Besoin d'un développeur pour votre projet ?

Réponse sous 24h · Sans engagement