Getter et setter
-
Un peu d’histoire, bonnes pratiques
Avec la professionnalisation de la programmation sont arrivées de nombreuses “bonnes pratiques” qui ont pour but d’améliorer l’évolutivité des logiciels. En voilà une bien connue, qui répond à un certain nombre de problématiques, concernant l’utilisation de getters et setters.
A l’origine la programmation objets proposait d’ajouter des propriétés aux classes, propriétés publiques pour être directement accessibles depuis des classes tierces. En PHP, ça donne ça :
class Man { /** * @var integer */ public $age; /** * @var string */ public $name; } // example of use in your external program /** @var $man Man */ echo $man . ' is ' . $man->age . ' old.';
Les développeurs se sont ensuite aperçu que lorsqu’on fait évoluer un logiciel, on peut se retrouver avec des données dont la consultation ou la modification peut nécessiter des traitements. Dans notre exemple si on ajoute un jour la date de naissance dans notre classe
Man
on peut se retrouver à préférer calculer l’age à partir de la date de naissance plutôt que le stocker, devoir le mettre à jour une fois par an, etc.Dans cet exemple donc on pourra remplacer la propriété $age par une méthode
getAge()
par exemple, qui fait ce calcul. Mais dans ce cas partout dans notre logiciel où on utilise la propriété$age
il faut modifier le programme pour utiliser la méthode à la place. Ce genre de modification est donc lourd de conséquences sur le code. Et que dire alors d’une librairie qui pourrait être utilisée par de nombreux programmes tiers sur lesquels nous n’avons pas la main ? Ils vont tous tomber en panne parce qu’on a supprimé la propriété$age
?Pour mettre un terme à ces problématiques, les développeurs professionnels ont décidé qu’il fallait bannir les propriétés publiques de nos objets, et d’implémenter systématiquement des getters et setters. Par exemple ici :
class Man { /** * @var integer */ private $age; /** * @var string */ private $name; /** * @return integer */ public function getAge() { return $this->age; } /** * @return string */ public function getName() { return $this->name; } /** * @param $age integer */ public function setAge($age) { $this->age = $age; } /** * @param $name string */ public function setName($name) { $this->name = $name; } }
Les objets ne contiennent plus publiquement que des méthodes. Le résultat est qu’on a prévu dès le départ de pouvoir changer à l’avenir leur implémentation, y compris les propriétés qui les composent et les calculs que leur consultation / modification peut engendrer. L’intérêt est évident.
Toutefois une des problématiques qui conduit à l’utilisation, voire à l’écriture, d’un framework, est la masse de code à maintenir de notre application. A mon sens implémenter systématiquement getters et setters va à l’encontre d’un principe simple pour la maintenabilité qui serait d’éviter du code inutile ou redondant. Ici avoir systématiquement des méthodes qui lisent et écrivent une propriété est un exemple typique de code inutile et redondant.
Certains langages tels Delphi et maintenant Lazarus, Javascript, Python proposent une implémentation native pour l’utilisation de getters / setters implicites : on garde ainsi nos propriétés publiques, et on peut ajouter du comportement à la lecture et à la modification de leur valeur.
Une proposition d’implémentation de ces accesseurs avait été proposée pour PHP 5.5, qui fut refusée à l’époque. Le langage ne propose aujourd’hui aucun mécanisme natif qui permette de déclarer simplement des propriétés publiques et de lui associer un getter et/ou un setter.
Getters et Setters dans it.rocks
it.rocks implémente donc des getters et setters pour vos propriétés.
On peut donc programmer nos classes métier le plus légèrement possibles, en reprenant notre exemple d’origine :
class Man { /** * @var integer */ public $age; /** * @var string */ public $name; } // example of use in your external program /** @var $man Man */ echo $man . ' is ' . $man->age . ' old.';
Prenons le cas où on rajoute par la suite une date de naissance, et qu’on fasse de l’age une donnée calculée, la propriété
$age
devant rester disponible dans le logiciel, notre classe reste compatible avec le code existant tout en ajoutant du comportement. Cela s’écrit le plus simplement possible en utilisant l’annotation de propriété @getter :class Man { /** * @getter * @var integer */ public $age; /** * @mandatory * @var Date_Time */ public $birth_date; /** * @var string */ public $name; /** * @return integer */ public function getAge() { return $this->birth_date->diff(Date_Time::now())->years(); } } // example of use in your external program : it does not change /** @var $man Man */ echo $man . ' is ' . $man->age . ' old.';
On parie ainsi simplement l’intérêt couplé de garder nos propriétés publiques et de pouvoir utiliser setters et getters.
Voir aussi