Projet : Amélioration des objets dés
Nous allons améliorer le code du dés pour accéder aux attributs de l'objet de façon indirecte. Lorsqu'un attribut d'un objet est soumis à un contrôle, par exemple position qui ne peut prendre comme valeur des entiers entre 1 et 6, il est de coutume de ne pas accéder directement à celui-ci, mais de passer par des méthodes pour le modifier et y accéder
Mutateur ou setter
Un mutateur (setter) est une méthode dont le but est de modifier un attribut. On la nomme usuellement : set_[nom de l'attribut](nouvelle_valeur)
Créez une méthode Dé.set_position(nouvelle_position)
qui modifie la position du dé.
Faites en sorte que la position ne puisse être plus petite que 1 (si nouvelle_position
< 1 alors l'attribut vaut 1) ni plus grande que 6 (si nouvelle_position
> 6 alors l'attribut vaut 6).
Créez un test pour la méthode Dé.set_position(nouvelle_position)
et modifiez, s'ils y en a, les tests qui modifient directement l'attribut positions pour qu'ils utilisent le setter.
Le but est de faire disparaître l'utilisation directe de l'attribut dans le code. Une fois qu'on ne le verra plus, c'est comme s'il n'existait plus.
Avant de continuer, nous allons éliminer les 2 constantes non nommées 1 et 6 que nous venons certainement de créer (si vous ne l'avez pas fait, bravo !).
Les nombres 1 et 6 sont les bornes du dé, il faut les nommer en tant que tel, sinon on va oublier leur signification et le code sera plus dire à modifier plus tard :
Coding mantra
Ajouter les constantes MIN_VALEUR = 1
et MAX_VALEUR = 6
dans le fichier dé.py
et utilisez les dans tout le code. Y compris le test du lacer de dé.
Changer ses valeurs changera la nature de tous les dés crées.
Accesseur ou getter
Si l'on veut accéder à un attribut sans l'utiliser directement, il faut le faire via une méthode. Cela peut être pratique si l'attribut n'est pas directement donné dans l'objet mais est construit (par exemple des coordonnées polaires d'un objet point2D où ses attributs sont en réalité ses coordonnées cartésiennes)
Créez une méthode Dé.get_position()
qui rend la position du dé.
Créez un test pour cette méthode.
Afin de faire disparaître l'existence de l'attribut dans le code :
modifiez tous les tests et les codes des programmes qui utilisent directement l'attribut position
pour qu'ils utilisent l'accesseur.
On peut maintenant rendre l'attribut position
privé.
Définition
Un attribut privé est un attribut qui ne doit pas être utilisé autre-part que dans les définitions de méthodes de la classe. Les attribut directement utilisables dans le code sont dit public.
Tout code voulant accéder ou modifier à cet attribut doit passer par son accesseur/mutateur.
En python, un attribut privé est précédé d'un _
. Par exemple, l'attribut position
est public alors que l'attribut _position
est privé.
Rendez l'attribut position
de la classe Dé
privé.
En python, la notion d'attribut privé ou public n'existe pas vraiment, ce sont juste des conventions entre codeur. Il est ainsi tout à fait possible d'utiliser un attribut privé partout (mais c'est bad karma). Le seul endroit où l'utilisation directe d'un attribut privé est autorisé, c'est dans le test de son accesseur.
Property
Python a une superbe fonctionnalité qui permet d'utiliser les accesseur les mutateur comme si l'on utilisait directement un attribut !
La façon la plus clair d'utiliser cela est d'utiliser des décorateurs. Leur utilisation générale en programmation dépasse le cadre de ce cours, nous allons juste utiliser ceux de python permettant de décorer des accesseurs et des mutateurs ici.
Considérons une classe qui possède, entre autres choses un attribut nommé toto
:
class MaClasse:
def __init__(self):
# ...
self.toto = 0
# ...
# ...
Cet attribut est public et on peut l'utiliser librement :
>>> mon_objet = MaClasse()
>>> mon_objet.toto = 42
>>> print(mon_objet.toto)
Si l'on rend cet attribut privé, il faut utiliser des accesseurs/mutateurs :
class MaClasse:
def __init__(self):
self._toto = 0
def get_toto(self):
return self._toto
def set_toto(self, nouveau_toto):
self._toto = nouveau_toto
C'est plus safe, car maintenant, on peut faire attention à ce que l'on donne comme valeur à l'attribut, mais son utilisation est bien plus lourde :
>>> mon_objet = MaClasse()
>>> mon_objet.set_toto(42)
>>> print(mon_objet.get_toto())
Python permet de combiner les deux approches avec les décorateurs @property
:
class MaClasse:
def __init__(self):
self._toto = 0
@property
def toto(self):
return self._toto
@toto.setter
def toto(self, nouveau_toto):
self._toto = nouveau_toto
Ce qu'on a fait :
- on décore la première méthode
MaClasse.toto()
par le décorateur@property
pour lui signifier que cette méthode est l'accesseur de l'attributtoto
- on décore la seconde méthode
MaClasse.toto()
par le décorateur@toto.setter
pour signifier que cette méthode est le mutateur de l'attributtoto
L'utilisation est alors pratique comme un attribut public et safe comme un attribut privé :
>>> mon_objet = MaClasse()
>>> mon_objet.toto = 42 # c'est le mutateur qui est utilisé par directement l'attribut
>>> print(mon_objet.toto) # c'est l'accesseur qui est utilisé par directement l'attribut
Utilisez l'attribut privé Dé._position
avec un décorateur @property
.
Modifiez tous les tests et les programmes principaux.
Finalement
Vérifier que tout fonctionne :
- les tests
- la user story
- le programme principal