Qu’est-ce qu’un patron de conception ?
Un patron de conception est une solution connue à un problème qui revient souvent dans la conception de classes. Il a un nom, ce qui permet de s’expliquer en un mot. Trois patrons très courants sont la fabrique, la stratégie et le décorateur.
La boutique de l’université applique des remises et vend des boissons avec des options. Chaque patron règle l’un de ces besoins.
À la fin de cette leçon
Remarque
Je peux appliquer une fabrique, une stratégie et un décorateur à un problème.
Mots-clés
| Mot | Ce que ça veut dire |
|---|---|
| Patron de conception | une solution nommée à un problème fréquent |
| Fabrique | une fonction ou une classe qui crée le bon objet |
| Stratégie | un objet qui contient une façon de faire, qu’on peut échanger |
| Décorateur | un objet qui en enveloppe un autre pour lui ajouter un comportement |
| Même interface | les objets échangés ont les mêmes méthodes |
| Enveloppe | l’objet qui contient un autre objet et l’appelle |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
La stratégie : une façon de faire qu’on échange
Une remise peut être nulle, de 20 pour cent, ou de 3 euros. Plutôt qu’un long if dans le panier, chaque remise est une petite classe avec une méthode appliquer.
Le panier reçoit un objet remise et lui délègue le calcul. Pour changer de remise, on donne un autre objet.
Des remises interchangeables
class RemisePourcent:
def __init__(self, pourcent):
self.pourcent = pourcent
def appliquer(self, prix):
return prix - prix * self.pourcent // 100
class Panier:
def __init__(self, remise):
self.remise = remise
self.somme = 0
def ajouter(self, prix):
self.somme = self.somme + prix
def total(self):
return self.remise.appliquer(self.somme)
panier = Panier(RemisePourcent(20))
panier.ajouter(600)
panier.ajouter(400)
print(panier.total())
panier.remise = RemisePourcent(50)
print(panier.total())
La fabrique choisit la bonne classe
Le programme lit le mot « etudiant » dans un formulaire. Quelle classe de remise faut-il créer ? Cette décision ne doit pas traîner partout.
Une fabrique la prend à un seul endroit : une fonction qui reçoit un nom et rend le bon objet. Le reste du programme n’a plus à connaître les classes.
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 les remises et la fabrique
printf 'class RemiseAucune:\n def appliquer(self, prix):\n return prix\n\n\nclass RemisePourcent:\n def __init__(self, pourcent):\n self.pourcent = pourcent\n\n def appliquer(self, prix):\n return prix - prix * self.pourcent // 100\n\n\ndef creer_remise(nom):\n if nom == "etudiant":\n return RemisePourcent(20)\n return RemiseAucune()\n' > essais/remises.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.
Demander une remise à la fabrique
PYTHONPATH=essais python3 -c "from remises import creer_remise; print(creer_remise('etudiant').appliquer(1000), creer_remise('plein').appliquer(1000))"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.
Le décorateur enveloppe un objet
Un café coûte 150 centimes. Le lait ajoute 30, le sucre 10. Une classe par combinaison serait absurde.
Un décorateur enveloppe un objet qui a la même interface. AvecLait(Cafe()) appelle le café, puis ajoute 30. On peut empiler les enveloppes dans l’ordre voulu.
Écrire le café et ses options
printf 'class Cafe:\n def prix(self):\n return 150\n\n\nclass AvecLait:\n def __init__(self, boisson):\n self.boisson = boisson\n\n def prix(self):\n return self.boisson.prix() + 30\n\n\nclass AvecSucre:\n def __init__(self, boisson):\n self.boisson = boisson\n\n def prix(self):\n return self.boisson.prix() + 10\n' > essais/cafe.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.
Empiler le lait et le sucre
PYTHONPATH=essais python3 -c "from cafe import Cafe, AvecLait, AvecSucre; print(AvecSucre(AvecLait(Cafe())).prix())"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 qu’il faut un patron partout, ou le choisissent avant d’avoir le problème. Partez du problème : une suite de if qui grossit appelle une stratégie. Le mot « décorateur » existe aussi en Python avec le signe @ : c’est un autre outil, qui ne fait pas partie de cette leçon.
Écrire le patron stratégie
Dans le devoir, vous écrivez trois remises : RemiseAucune, RemisePourcent(pourcent) et RemiseFixe(montant), qui ne descend jamais sous zéro.
Vous écrivez aussi Panier(remise) : ajouter(prix), total() qui applique la remise à la somme, et changer_remise(remise).