octobre
2011
Derrière ce titre se cache une réflexion que je me suis faite suite à de nombreuses remises en question sur ma manière de développer en PHP. Sur le principe du KISS (Keep It Simple, Stupid !), je m’efforce désormais de rendre mon code le plus prévisible possible, le moins « magique »… pour arriver finalement au constat que je n’utilisais désormais plus du tout certaines méthodes magiques que j’appréciais pourtant il y a quelques années. Voyons pourquoi…
Préambule
Ce billet traite des méthodes magiques, aussi vaut-il mieux connaître le sujet avant d’aller plus loin: http://php.net/manual/fr/language.oop5.magic.php
Je ne traite ici que des méthodes __get, __set et __call : les autres méthodes magiques répondent à des besoins très spécifiques (__sleep, __wakeup…) qui ne rentrent pas dans cette réflexion.
__get et __set : vraiment __utiles ?
Si il est une constante dans mes habitudes en PHP depuis PHP5, c’est de n’avoir jamais utilisé de propriétés publiques dans aucune de mes classes. Principalement, ma préoccupation était de pouvoir garder « sous le coude » la possibilité d’y greffer un traitement supplémentaire (initialisation à la demande, façon Lazy Load…). Afin d’éviter autant que possible de devoir écrire toutes mes méthodes get* et set* à la main, j’utilisais ponctuellement les méthodes magiques __get et __set à cette fin. Plus souvent, j’implémentais les deux façons de faire, suivant le précepte TIMTOWTDI (There Is More Than One Way To Do It).
Seulement voilà, proposer plusieurs façons de faire, c’est sexy sur le papier, mais pas franchement désirable en pratique :
- Impossibilité d’utiliser une Fluent Interface ( http://www.martinfowler.com/bliki/FluentInterface.html ) pourtant bien pratique.
- Confusion des autres développeurs : j’utilise quelle façon de faire ?
- L’auto-complétion des IDE ne fonctionne plus avec les méthodes magiques, alors que des méthodes get* et set* bien documentées ne posent aucun soucis.
- Clarté du code : la définition des méthodes __get et __set est au mieux inesthétique, au pire franchement obscure dés qu’on commence à y greffer des cas d’exception.
Désormais, j’applique la règle suivante : deux façons de faire, c’est une de trop. Voilà comment j’ai quasiment abandonné l’utilisation des méthodes magiques __get et __set au profit d’une approche plus constante et prévisible, même si un brin laborieuse à écrire.
__call of Cthulhu
La méthode __call est bien pratique : elle permet d’intercepter un appel sur une méthode qui n’existe pas pour un objet et en faire ce qu’on veut.
J’ai toujours trouvé le principe très sympa, notamment pour une implémentation du pattern Decorator ( http://en.wiksuripedia.org/wiki/Decorator_pattern ) : on implémente les nouvelles responsabilités très vite, et tout le reste est redirigé vers l’objet interne grâce à la méthode magique __call sans qu’on ait à se soucier de les implémenter. Cette souplesse permet même de greffer un Decorator très simple sur des classes très différentes.
Plusieurs problèmes sérieux me font m’interroger avant d’utiliser cette méthode magique :
- Bien souvent, le rôle de l’objet qui implémente cette méthode magique devient très obscur, et on ne sait plus trop ce qu’il est sensé faire à l’origine. Comme l’a laissé présagé le titre de ce paragraphe, ça devient vite tentaculaire !
- Encore une fois, difficile d’avoir une auto-complétion avec un IDE : généralement les autres développeurs vont « ramer » un peu avant de maîtriser le code.
Quand j’en arrive à considérer l’utilisation de la méthode __call, je me pose désormais une question : pour quelle raison en ait-je besoin ?
Si c’est pour une problématique très particulière qui nécessite cette souplesse, pourquoi pas. Si c’est pour faire des économies en terme de ligne de code, alors je ne vais pas plus loin : le sacrifice en terme de clarté ne justifie pas le gain, et l’application devient vite complexe à comprendre et à maintenir.
$this->assertTrue($goodPractices->isEvil($magicMethods)) ?
Pour conclure ce billet un peu bancal, je n’irais pas dire que les méthodes magiques sont le mal. Avoir besoin de __get et __set peut relever de la préférence personnelle plus que du réel besoin, et __call peut se révéler très utile pour solutionner élégamment une problématique de communication entre deux objets qui ne connaissent pas grand chose l’un de l’autre.
Par contre, un peu de modération sur leur utilisation ne fait pas de mal, bien au contraire : quand quelque chose peut-être fait plus simplement, il est préférable de le faire plus simplement. Et tant pis si on écrit 20 lignes de code en plus, c’est toujours mieux que de courir le risque que le prochain développeur ne comprenne mal votre design…
Et vous, plutôt Gérard Majax, ou inquisition espagnole ?