Qu’est-ce que la composition en programmation objet ?
La composition consiste à construire un objet à partir d’autres objets qu’il contient et qu’il utilise. On dit qu’il a une partie, au lieu de dire qu’il est une version d’une autre classe. Elle rend un programme plus facile à changer qu’une grande famille de classes liées par l’héritage.
Une université prévient ses étudiants d’un cours annulé. Le message part par SMS, par courriel, ou par les deux.
À la fin de cette leçon
Remarque
Je peux choisir la composition plutôt que l’héritage et dire pourquoi.
Mots-clés
| Mot | Ce que ça veut dire | Exemple |
|---|---|---|
| Composition | un objet qui en contient d’autres et les utilise | |
| Est un | la relation d’héritage | un colis express est un colis |
| A un | la relation de composition | une alerte a un canal |
| Délégation | un objet confie un travail à la partie qu’il contient | |
| Couplage | le degré auquel une classe dépend d’une autre | |
| Interface | les méthodes qu’un objet promet de savoir faire |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
L’héritage enferme le choix dans la classe
Imaginez une classe AlerteSms et une classe AlerteCourriel, filles de Alerte. Chaque canal est une classe entière.
Une alerte qui doit partir par SMS et par courriel demande une troisième classe. Avec un troisième canal, il en faut sept. Le nombre de classes explose.
Une alerte reçoit son canal
Au lieu d’hériter, Alerte reçoit un objet canal dans son constructeur et le range dans un attribut. Pour prévenir, elle lui délègue le travail : self.canal.envoyer(texte).
Chaque canal est une petite classe à part. Changer de canal, c’est donner un autre objet.
Une alerte qui contient un canal
class CanalSms:
def envoyer(self, texte):
return "SMS : " + texte
class CanalCourriel:
def envoyer(self, texte):
return "Courriel : " + texte
class Alerte:
def __init__(self, canal):
self.canal = canal
def prevenir(self, texte):
return self.canal.envoyer(texte)
alerte = Alerte(CanalSms())
print(alerte.prevenir("Cours annulé"))
alerte.canal = CanalCourriel()
print(alerte.prevenir("Cours annulé"))
Créer un dossier pour les essais
mkdir -p essaisAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Écrire l’alerte et ses deux canaux
printf 'class CanalSms:\n def envoyer(self, texte):\n return "SMS : " + texte\n\n\nclass CanalCourriel:\n def envoyer(self, texte):\n return "Courriel : " + texte\n\n\nclass Alerte:\n def __init__(self, canal):\n self.canal = canal\n\n def prevenir(self, texte):\n return self.canal.envoyer(texte)\n' > essais/alerte.pyAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Prévenir par SMS puis par courriel
PYTHONPATH=essais python3 -c "from alerte import Alerte, CanalSms, CanalCourriel; a = Alerte(CanalSms()); print(a.prevenir('Cours annule')); a.canal = CanalCourriel(); print(a.prevenir('Cours annule'))"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Comment choisir entre hériter et composer
Posez la question : « B est-il vraiment un A, dans tous les cas, pour toujours ? ». Si oui, l’héritage est possible.
Si B utilise A, ou si la partie doit pouvoir changer en cours de route, composez. Dans le doute, composez : une partie se remplace, une classe mère ne se quitte pas.
Héritage et composition côte à côte
| Question | Héritage | Composition |
|---|---|---|
| Relation | est un | a un |
| Quand le choix se fait | à l’écriture de la classe | à l’exécution, en donnant un objet |
| Changer de comportement | écrire une nouvelle classe | donner une autre partie |
| Couplage avec l’autre classe | fort | faible : seule la méthode compte |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Lire la classe de la partie contenue
PYTHONPATH=essais python3 -c "from alerte import Alerte, CanalCourriel; a = Alerte(CanalCourriel()); print(type(a.canal).__name__)"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Erreur fréquente
Attention
Beaucoup croient que la composition est une façon plus longue d’écrire l’héritage. Ce sont deux relations différentes. L’héritage fixe la parenté pour toujours. La composition laisse changer la partie quand on le veut, même pendant l’exécution.
Écrire une alerte à plusieurs canaux
Dans le devoir, vous écrivez la classe Alerte. Elle range une liste de canaux. ajouter_canal(canal) en ajoute un, retirer_canal(canal) en enlève un.
prevenir(texte) envoie le texte à chaque canal et rend la liste des réponses, dans l’ordre. Les tests lui donnent leurs propres faux canaux.