Module 2Connaître les principaux piliers de la POO
Chapitre 5 sur 10
Caractériser l'abstraction
Imposer un contrat aux sous-classes avec les classes abstraites et les interfaces.
- ≈ 95 min
- 3 leçons
- 2 exercices
- TP
Module 2Connaître les principaux piliers de la POO
Chapitre 5 sur 10
Imposer un contrat aux sous-classes avec les classes abstraites et les interfaces.
Objectifs
Avec ce chapitre, tu sauras…
abc.Leçon 1
L'exemple de la Forme (résumé OFPPT)
Toutes les formes géométriques ont un périmètre et une surface : il est logique de déclarer perimetre() et surface() dans la classe racine Forme. Mais Forme ne peut pas les calculer sans connaître la forme précise. Si on les écrivait seulement dans Cercle et Rectangle, rien ne garantirait que toutes les futures sous-classes les possèdent.
Solution : déclarer perimetre() et surface() comme méthodes abstraites dans Forme, ce qui rend Forme abstraite et impose aux sous-classes de les implémenter.
┌──────────────────────────────┐
│ «abstraite» Forme │
│ x, y │
│ deplacer() (concrète) │
│ surface() (abstraite) │
│ perimetre() (abstraite) │
└──────────────┬───────────────┘
┌─────────────┴─────────────┐
┌──────┴──────┐ ┌──────┴──────┐
│ Cercle │ │ Rectangle │ ← classes concrètes
│ surface() │ │ surface() │
│ perimetre() │ │ perimetre() │
└─────────────┘ └─────────────┘Les règles des classes abstraites
L'utilité des classes abstraites
Une classe abstraite définit des comportements que les sous-classes devront impérativement implémenter. C'est un contrat : les utilisateurs sont assurés de trouver ces méthodes dans toutes les sous-classes concrètes, et les programmeurs sont obligés de les écrire.
En Python : le module abc
from abc import ABC, abstractmethod
import math
class Forme(ABC): # hérite de ABC : classe abstraite
def __init__(self, x, y):
self.x = x
self.y = y
def deplacer(self, dx, dy): # méthode concrète
self.x += dx
self.y += dy
@abstractmethod
def surface(self): # méthode abstraite : pas de vrai corps
pass
@abstractmethod
def perimetre(self):
pass
class Cercle(Forme):
def __init__(self, x, y, rayon):
super().__init__(x, y)
self.rayon = rayon
def surface(self):
return math.pi * self.rayon ** 2
def perimetre(self):
return 2 * math.pi * self.rayon
class Rectangle(Forme):
def __init__(self, x, y, longueur, largeur):
super().__init__(x, y)
self.longueur = longueur
self.largeur = largeur
def surface(self):
return self.longueur * self.largeur
def perimetre(self):
return 2 * (self.longueur + self.largeur)
formes = [Cercle(0, 0, 1), Rectangle(0, 0, 4, 3)]
for f in formes:
print(type(f).__name__, round(f.surface(), 2), round(f.perimetre(), 2))
# f = Forme(0, 0) → TypeError: Can't instantiate abstract class FormeLeçon 2
Exemple 1 : l'interface Comparable (résumé OFPPT)
La comparaison (plus petit, égal, plus grand) est commune à tous les objets qui ont une relation d'ordre. On définit l'interface Comparable avec la méthode compare_to(). La classe Personne l'implémente en comparant les âges, la classe Outil en comparant les longueurs.
Exemple 2 : l'interface Imprimable (résumé OFPPT)
On veut que toutes les formes aient une méthode imprimer(). Solution 1 : ajouter une méthode abstraite imprimer() dans Forme. Mais d'autres classes qui n'héritent pas de Forme (une facture, un rapport) devraient redéclarer leur propre méthode abstraite. Solution 2, la meilleure : définir une interface Imprimable, que Forme et toutes les autres classes peuvent implémenter.
from abc import ABC, abstractmethod
class Comparable(ABC): # interface : uniquement des méthodes abstraites
@abstractmethod
def compare_to(self, autre):
"""Renvoie un nombre < 0, 0 ou > 0."""
class Imprimable(ABC):
@abstractmethod
def imprimer(self):
pass
class Personne(Comparable, Imprimable): # implémente deux interfaces
def __init__(self, nom, age):
self.nom = nom
self.age = age
def compare_to(self, autre):
return self.age - autre.age
def imprimer(self):
print(f"{self.nom} ({self.age} ans)")
a, b = Personne("Ali", 30), Personne("Sara", 25)
print(a.compare_to(b) > 0) # True : Ali est plus âgé
a.imprimer()Leçon 3
| Classe abstraite | Interface | |
|---|---|---|
| méthodes | abstraites et concrètes | toutes abstraites |
| attributs | oui | non (seulement des constantes dans certains langages) |
| héritage | une classe hérite d'une classe abstraite (Java, C#) | une classe peut implémenter plusieurs interfaces |
| relation | « est un » : un Cercle est une Forme | « sait faire » : une Personne sait se comparer, s'imprimer |
| usage |
1Une méthode abstraite…
2Une classe abstraite peut-elle être instanciée ?
3Une sous-classe qui n'implémente pas toutes les méthodes abstraites est…
4Dans une interface, les méthodes sont…
5En Python, on crée une classe abstraite en héritant de…
6La relation « sait faire » correspond plutôt à…
L'essentiel du chapitre
Forme → Cercle, Rectangle).Comparable, Imprimable).from abc import ABC, abstractmethod.| factoriser le code commun d'une famille de classes |
| définir une capacité commune à des classes sans lien |
Une entreprise paie ses employés et ses fournisseurs (factures). Ces deux classes n'ont rien en commun. Propose une interface Payable avec une méthode montant_a_payer(), implémente-la dans EmployeFixe et dans une classe Facture, puis calcule le total à payer d'une liste mélangeant employés et factures.
Crée une classe abstraite Employe (nom, matricule, méthode concrète afficher(), méthode abstraite salaire()), puis deux classes concrètes : EmployeFixe (salaire mensuel fixe) et EmployeHoraire (prix de l'heure × nombre d'heures). Calcule la masse salariale d'une liste d'employés.