Avez-vous une question? (216) 97 656 803 hajjriadh@gmail.com
JavaScript

Polymorphisme et composition

Polymorphisme et composition. Dans cette leçon du cours JavaScript, vous apprendrez à : expliquer le polymorphisme et en tirer parti pour éviter les if sur le type ; définir une méthode « à redéfinir ...

0 chapitres 0 QCM Accès gratuit
Dans cet article

À propos de ce cours

Polymorphisme et composition. Dans cette leçon du cours JavaScript, vous apprendrez à : expliquer le polymorphisme et en tirer parti pour éviter les if sur le type ; définir une méthode « à redéfinir » dans une classe parent ; construire un objet à partir d'autres objets (composition) ; choisir entre héritage (« est un ») et composition (« a un »).

Chapitre 16 — La programmation orientée objet · 16.7 Polymorphisme et composition

ℹ️ À retenir
Nouvelle leçon : le polymorphisme et la composition sont les deux idées qui rendent la POO vraiment utile, et le TP du panier d'achat les utilise toutes les deux.
🎯 Objectifs de la leçon
• expliquer le polymorphisme et en tirer parti pour éviter les if sur le type ;
• définir une méthode « à redéfinir » dans une classe parent ;
• construire un objet à partir d'autres objets (composition) ;
• choisir entre héritage (« est un ») et composition (« a un »).

Polymorphisme et composition
Figure 1 — Deux façons d'organiser des objets.

1. Le polymorphisme

Polymorphisme signifie « plusieurs formes » : le même appel de méthode produit un comportement adapté à chaque objet. Le code qui appelle n'a pas besoin de connaître le type exact de l'objet.

class Forme {
  aire() {
    throw new Error(`${this.constructor.name} doit définir aire()`);
  }
  decrire() {
    return `${this.constructor.name} d'aire ${this.aire().toFixed(2)}`;
  }
}
class Rectangle extends Forme {
  constructor(l, h) { super(); this.l = l; this.h = h; }
  aire() { return this.l * this.h; }
}
class Cercle extends Forme {
  constructor(r) { super(); this.r = r; }
  aire() { return Math.PI * this.r ** 2; }
}
class Triangle extends Forme {
  constructor(b, h) { super(); this.b = b; this.h = h; }
  // aire() oubliée !
}

// POLYMORPHISME : le même appel, un comportement adapté à chaque objet
const formes = [new Rectangle(3, 4), new Cercle(1), new Rectangle(2, 2)];
for (const f of formes) console.log(f.decrire());

const total = formes.reduce((s, f) => s + f.aire(), 0);
console.log("Aire totale :", total.toFixed(2));

try {
  new Triangle(3, 4).decrire();
} catch (e) {
  console.log("Erreur :", e.message);
}

Résultat dans la console

Rectangle d'aire 12.00
Cercle d'aire 3.14
Rectangle d'aire 4.00
Aire totale : 19.14
Erreur : Triangle doit définir aire()
  • decrire() est écrite une seule fois, dans Forme, mais appelle this.aire() qui est différente pour chaque forme.
  • La boucle ne contient aucun if (f instanceof Cercle): ajouter une nouvelle forme ne demande de modifier aucun code existant.
  • Forme lève une erreur claire si un enfant oublie de redéfinir aire(): c'est une méthode « abstraite ».
// ✗ Sans polymorphisme : chaque nouveau type oblige à modifier cette fonction
function aire(forme) {
  if (forme.type === "rectangle") return forme.l * forme.h;
  if (forme.type === "cercle") return Math.PI * forme.r ** 2;
  // … et ainsi de suite
}

2. La composition

La composition consiste à construire un objet en y plaçant d'autres objets, qui font chacun une partie du travail. L'objet principal délègue: il appelle les méthodes de ses composants.

// COMPOSITION : un objet « A UN » autre objet, au lieu « d'ÊTRE UN »
class Moteur {
  constructor(puissance) {
    this.puissance = puissance;
    this.allume = false;
  }
  demarrer() {
    this.allume = true;
    return `Moteur ${this.puissance} ch démarré`;
  }
}

class GPS {
  itineraire(destination) {
    return `Itinéraire calculé vers ${destination}`;
  }
}

class Voiture {
  constructor(marque, puissance, avecGPS = false) {
    this.marque = marque;
    this.moteur = new Moteur(puissance);      // une voiture A UN moteur
    this.gps = avecGPS ? new GPS() : null;    // et peut-être un GPS
  }
  partir(destination) {
    const etapes = [this.moteur.demarrer()];  // elle DÉLÈGUE au moteur
    if (this.gps) etapes.push(this.gps.itineraire(destination));
    return `${this.marque} : ${etapes.join(", ")}`;
  }
}

console.log(new Voiture("Clio", 90, true).partir("Sousse"));
console.log(new Voiture("Twingo", 65).partir("Bizerte"));

Résultat dans la console

Clio : Moteur 90 ch démarré, Itinéraire calculé vers Sousse
Twingo : Moteur 65 ch démarré

3. Héritage ou composition ?

HéritageComposition
Relation« est un » (un livre est un produit)« a un » (une voiture a un moteur)
Lienfort et fixé à l'écrituresouple : on peut changer de composant
Risquehiérarchies profondes et fragilesun peu plus de code de délégation
Exemple du TPProduitPhysique extends Produitun Panier contient des LignePanier

Règle souvent citée : « préférez la composition à l'héritage ». N'héritez que si la relation « est un » est vraie et durable.

ℹ️ À retenir
• Polymorphisme : même appel, comportement propre à chaque classe ; pas de if sur le type.
• Une méthode parent qui lève une erreur force les enfants à la redéfinir.
• Composition : un objet contient d'autres objets et leur délègue le travail.
• « est un » → héritage ; « a un » → composition ; dans le doute, composition.

🛠 Activité 7 — Préparer le panier d'achat

Mission : Avant le TP, entraînez-vous sur un modèle réduit : une commande de restaurant.

Questions pour avancer (répondez dans votre compte rendu, puis appliquez dans votre projet) :

  1. Un restaurant vend des Plats, des Boissons et des Desserts. Chacun a un nom et un prix, mais les boissons sont taxées à 7 % et le reste à 19 %. Quelle méthode sera polymorphe ? Où la déclarez-vous ?
  2. Une Commande contient des articles et une table. Est-ce un héritage ou une composition ? Dessinez le diagramme des classes avec les flèches « est un » et « a un ».
  3. Écrivez Commande.total() sans aucun if ni instanceof. Comment faites-vous ?
  4. Ajoutez un nouveau type Menu (plat + boisson + dessert à prix réduit). Combien de fichiers ou de classes existants avez-vous dû modifier ? Un Menu est-il un article, contient-il des articles, ou les deux ?
  5. Un collègue propose class Commande extends Array pour profiter de push et map. Quels sont les risques ? Que proposez-vous à la place ?

Je vérifie / je rends :

  • ☐ Le diagramme des classes.
  • ☐ Le code de la commande et la démonstration de l'ajout de Menu.

← 16.6 Les prototypes : ce qui se cache derrière les classes16.8 TP 16 : Gérer un panier d'achat en POO →