Aller au contenu

Héritage

L’héritage est probablement le concept le plus mal compris de la programmation orientée objet.

C’est puissant, oui.
Mais mal utilisé, ça rend le code :

  • compliqué
  • rigide
  • difficile à faire évoluer

Donc dans cette page, on va faire les choses proprement et calmement.


L’idée de base

L’héritage permet de créer une classe à partir d’une autre classe.

On parle de :

  • classe parent (ou classe mère)
  • classe enfant

La classe enfant :

  • récupère les attributs
  • récupère les méthodes
  • peut en ajouter ou en modifier

Exemple simple

Reprenons notre exemple de jeu.

class Personnage:
    def __init__(self, nom, vie):
        self.nom = nom
        self.vie = vie

    def attaquer(self):
        print("Le personnage attaque")

On peut maintenant créer une classe plus spécialisée :

class Guerrier(Personnage):
    pass

Guerrier est une classe :

  • plus spécifique
  • qui est un Personnage

Héritage = “est un”

C’est la règle d’or :

Si une classe n’est pas un cas particulier de l’autre, alors l’héritage est une mauvaise idée.

  • Un guerrier est un personnage → OK
  • Une arme n’est pas un personnage → pas OK

Si la phrase “X est un Y” ne fonctionne pas naturellement, il ne faut probablement pas utiliser l’héritage.


Redéfinir une méthode

Une classe enfant peut redéfinir une méthode héritée.

class Guerrier(Personnage):
    def attaquer(self):
        print(f"{self.nom} attaque avec son épée")

Maintenant :

p = Personnage("Alice", 100)
g = Guerrier("Bob", 120)

p.attaquer()
g.attaquer()

Affiche :

Le personnage attaque
Bob attaque avec son épée

Appeler le constructeur parent : super()

Si la classe enfant a besoin d’un constructeur, il faut appeler celui de la classe parente, à l'aide du mot clef super().

class Guerrier(Personnage):
    def __init__(self, nom, vie, force):
        super().__init__(nom, vie)
        self.force = force

Quizz

Laquelle de ces relations justifie un héritage ?
- Une voiture a un moteur
- Un joueur dispose d'un inventaire
- *Un mage est un personnage
- Un personnage attaque avec une arme
> L’héritage correspond à une relation “est un”.

Le polymorphisme

Regardez ce code :

personnages = [
    Personnage("Alice", 100),
    Guerrier("Bob", 120, 10)
]

for p in personnages:
    p.attaquer()

Python ne se demande pas :

  • si p est un Personnage
  • ou un Guerrier

Il appelle simplement la bonne méthode.

C’est ce qu’on appelle le polymorphisme :

même méthode, comportement différent


Composition vs héritage (aperçu)

Quand on veut dire :

“X a un Y”

On parle de composition, pas d’héritage.

class Personnage:
    def __init__(self, nom, vie, arme):
        self.arme = arme

On verra ça plus en détail plus tard, mais gardez ça en tête :

“a un” ≠ “est un”


Exercice pratique

On veut modéliser différents types de notifications dans une application.

Créez une classe Notification qui :

- possède une heure d'envoi
- possède une méthode `envoyer()` qui affiche un message générique (du genre `vous avez reçu une notification à X heure`).

Créez une classe NotificationEmail qui hérite de Notification et qui :

  • possède un expéditeur
  • redéfinit la méthode envoyer() pour afficher
    Vous avez reçu un e-mail de la part de X à Y heure

Créez une classe NotificationBanque qui hérite de Notification et qui :

  • possède un montant
  • redéfinit la méthode envoyer() pour afficher
    Vous avez effectué un paiement de X euros à Y heure

Créez une liste contenant plusieurs notifications de types différents
(NotificationEmail, NotificationSMS, etc.)

Parcourez cette liste et appelez envoyer() sur chaque notification.

L’objectif de cet exercice est de comprendre que : - plusieurs classes peuvent hériter d’une même classe parente - une même méthode peut produire des comportements différents - le reste du code n’a pas besoin de connaître le type exact de l’objet

Ce n’est pas l’exercice le plus fun du monde,
mais c’est un excellent test pour savoir si l’héritage est bien compris.

En résumé

L’héritage permet :

  • de spécialiser une classe existante
  • de réutiliser du code
  • d’écrire du code plus générique

Mais :

  • ce n’est pas obligatoire
  • ce n’est pas toujours une bonne idée
  • mal utilisé, ça complique tout

Dans la page suivante, on va voir une alternative parfois meilleure : la composition, et pourquoi elle est si importante.