« est un » ou « a un » ?
À la leçon précédente, l'héritage disait « est un » : un CompteEpargne est un Compte. Mais réfléchis à une voiture et un moteur. Une voiture n'est pas un moteur. Une voiture a un moteur. Faire « Voiture hérite de Moteur » serait absurde.
Quand la relation est « a un », on n'hérite pas : on compose. L'objet contient d'autres objets comme propriétés, et leur délègue le travail. C'est la composition, et c'est le réflexe que préfèrent les pros.
L'idée : un objet en contient d'autres
Une Voiture a un Moteur (et des Roues, une Radio…). Chacun est un objet à part entière, avec sa propre logique. La voiture ne refait pas le travail du moteur : quand on la démarre, elle demande au moteur de démarrer. On appelle ça déléguer.
En code : contenir et déléguer
classe MoteurEssence:
public demarrer(): retourne "Vroum vroum !"
classe Voiture:
privé moteur
constructeur(moteur):
this.moteur = moteur // la Voiture A UN moteur
public demarrer():
retourne this.moteur.demarrer() // elle DÉLÈGUE au moteur
voiture = new Voiture(new MoteurEssence())
voiture.demarrer() // "Vroum vroum !"
Le gros avantage : on peut échanger une pièce sans rien casser. Donne un MoteurElectrique à la place, et la voiture roule pareil, en silence. Avec l'héritage, ce genre de changement à chaud est bien plus rigide.
Composition ou héritage ? Le bon réflexe
- « est un » (CompteEpargne est un Compte) → héritage. Plus rare qu'on ne croit.
- « a un » (Voiture a un Moteur) → composition. C'est le cas le plus fréquent.
Règle des pros : « préférer la composition à l'héritage ». La composition est souple (on assemble et on remplace des pièces), l'héritage est rigide (l'enfant est soudé au parent). Dans le doute, demande-toi « est-ce un » vs « a un » : la plupart du temps, c'est « a un », donc compose.
La composition en vrai (essaie, et change le moteur)
Une Voiture qui a un Moteur. Démarre-la (elle délègue au moteur), puis échange le moteur : la voiture ne change pas, son comportement oui. Impossible aussi proprement avec de l'héritage.
Tu as monté deux moteurs différents et appelé la même méthode demarrer() sur chacun, avec un résultat différent. Ce petit miracle a un nom : le polymorphisme. C'est le dernier pilier.
"is a" or "has a"?
In the previous lesson, inheritance said "is a": a CompteEpargne is a Compte. But think of a car and an engine. A car is not an engine. A car has an engine. Writing "Car extends Engine" would be absurd.
When the relationship is "has a", you don't inherit: you compose. The object contains other objects as properties, and delegates the work to them. That's composition, and it's the reflex the pros prefer.
The idea: an object contains others
A Voiture (car) has a Moteur (engine) (and Roues wheels, a Radio…). Each is a full-fledged object with its own logic. The car doesn't redo the engine's work: when you start it, it asks the engine to start. We call that delegating.
In code: contain and delegate
class MoteurEssence:
public demarrer(): return "Vroum vroum !"
class Voiture:
private moteur
constructor(moteur):
this.moteur = moteur // the Voiture HAS A moteur
public demarrer():
return this.moteur.demarrer() // it DELEGATES to the engine
voiture = new Voiture(new MoteurEssence())
voiture.demarrer() // "Vroum vroum !"
The big upside: you can swap a part without breaking anything. Give it a MoteurElectrique instead, and the car drives the same, silently. With inheritance, that kind of hot-swap is far more rigid.
Composition or inheritance? The right reflex
- "is a" (CompteEpargne is a Compte) → inheritance. Rarer than you'd think.
- "has a" (Voiture has a Moteur) → composition. The most frequent case.
The pros' rule: "favor composition over inheritance". Composition is flexible (you assemble and replace parts), inheritance is rigid (the child is welded to the parent). When in doubt, ask "is a" vs "has a": most of the time it's "has a", so compose.
Composition for real (try it, and swap the engine)
A Voiture that has a Moteur. Start it (it delegates to the engine), then swap the engine: the car doesn't change, its behavior does. Also impossible this cleanly with inheritance.
You mounted two different engines and called the same demarrer() method on each, with a different result. That little miracle has a name: polymorphism. It's the last pillar.
Sans remonter dans la leçon : quel test mental distingue une relation qui demande de l'héritage d'une relation qui demande de la composition, et qu'est-ce que déléguer veut dire concrètement ?
CompteEpargne est un Compte) ; « a un » → composition (une Voiture a un Moteur). La plupart des relations sont des « a un », donc on compose plus souvent qu'on n'hérite. Déléguer, c'est ne pas refaire le travail soi-même : voiture.demarrer() appelle en réalité this.moteur.demarrer(). La voiture contient le moteur comme propriété et lui passe la tâche.Tu demandes à l'IA de modéliser une voiture et son moteur. Elle te propose cette conception. Ton rôle de relecteur : l'accepter telle quelle ou la rejeter, et dire pourquoi.
class Moteur {
demarrer() { return "Vroum vroum !"; }
}
// L'IA fait hériter la voiture du moteur
class Voiture extends Moteur {
rouler() { return this.demarrer() + " la voiture avance"; }
}
Voiture extends Moteur dit « une voiture est un moteur » : c'est faux, une voiture a un moteur. Le code « marche » (la voiture peut appeler demarrer()), mais la relation est mal modélisée et ça coûte cher ensuite : impossible d'avoir deux moteurs, impossible de changer de moteur à chaud, et la voiture hérite de tout le moteur même de ce qui ne la regarde pas. La bonne conception, c'est la composition : class Voiture { constructor(moteur){ this.moteur = moteur; } }, puis this.moteur.demarrer(). On garde la possibilité d'échanger la pièce.