PHP : Appels de méthode à la chaîne
Par Méthylbro le dimanche 13 septembre 2009, 08:00 - Développement - Lien permanent
Il y a plein de bonne pratique que je vois trop souvent être ignorées par d'autres développeurs qui m'entourent (collègues, stagiaires etc). Pour certaines d'entre elles, il s'agit de pratique que j'ai ignoré pendant trop longtemps par méconnaissance d'un l'intérêt qu'elles peuvent apporter.
J'imagine qu'il m'en reste encore énormément à découvrir d'ailleurs. L'idée ici est de factoriser un ensemble d'appels de méthode sur un objet en une seule instruction. Pouvoir faire des appels de méthodes à la chaîne.

Il est fréquent de tomber sur des cas semblables à l'exemple suivant :
class Object {
function methodA() {}
function methodB() {}
function methodC() {}
}
$instance = new Object();
$instance->methodA();
$instance->methodB();
$instance->methodC();
En considérant que les méthodes A, B et C soit de type void. C'est-à-dire qu'elle ne retournent rien et qu'elle n'auront pas à l'avenir pour vocation de retourner une information. Il serait intéressant alors de factoriser les trois instructions utilisées pour l'appel a ces trois méthodes.
Pour ce faire, il suffit lors de la déclaration des méthodes, de ajouter l'information suivante :
class Object {
function methodA() {
return $this;
}
function methodB() {
return $this;
}
function methodC() {
return $this;
}
}
L'intérêt est de pouvoir utiliser plus simplement cette classe et de faire appel aux méthodes A, B et C en une seule instruction :
$instance = new Object();
$instance->methodA()->methodB()->methodC();
Plus intéressant n'est-ce pas ?
Commentaires
Sauf que c'est sémantiquement incorrect, et qu'on y gagne quasiment rien. De plus, le code devient plus difficile à lire.
C'est l'une de raisons pour lesquelles je ne supporte pas jQuery.
Je ne suis pas d'accord avec toi.
Je ne trouves pas cela systématiquement "sémantiquement incorrect".
Tout dépend du contexte.
Mais lorsque tu utilises par exemple plusieurs assesseurs ; les enchaîner les uns les autres ne signifie pas systématiquement une faute de sens.
En revanche je suis à demi-d'accord sur un point.
Le code peut en effet s'avérer difficile à lire.
Difficile seulement si l'interface à mal été pensée et que les noms des caractéristiques publique de ta classe n'ont peu ou pas de sens.
C'est clair cela m'a directement fait penser a jQuery. Et je l'utilise avec jQuery parce que les contraintes sont differentes, cela permet de gagner un peu de place et de réduire le volume des fichiers.
Par contre coté serveur, je trouve que ca réduit trop la clareté du code, même avec les assesseurs....l'autre inconvénient que j'y vois c'est que comme tu le souligne c'est une pratique peu courante et que donc si le code est maintenu par un collègue cela risque de le "perdre" donc perte de temps...
Ola,
Pour moi c'est une très bonne pratique, à réserver toutefois pour des utilisations précises où justement, on pourrait avoir un intérêt à vouloir enchainer plusieurs modifications de l'objet.
Le meilleur exemple reste Doctrine, qui l'utilise à merveille.
@Tim : j'aboute dans ton sens depuis le début. Je considère moi aussi cela comme une bonne pratique.
Cependant je suis le premier à consentir les limites d'un appel à la chaine dues aux problèmes de sémantique et de lisibilité inhérents.
J'avoue que là je suis un peu dépassé. Je ne vois réellement aucun intérêt à une telle pratique si ce n'est de réduire la lisibilité du code.
Pour le reste ça reste une question de choix du programmeur, mais à part dans des cas de types d'objets simples avec des accessurs (enchainement de méthodes sur un objet de type String par exemple), ça n'a à mon avis aucun sens.
En fait c'est ton exemple qui est mauvais. Tu sous entends dans ton article qu'on peut faire ça pour toutes les méthodes de ta classe qui retourne void. C'est là l'erreur. Tu ne peux retourner l'objet que si la méthode traite l'objet lui même. Cet exemple est plus parlant :
$resultset->filter('country','france')->getFirst();
au lieu de :
$resultset->filter('country','france');
$resultset->getFirst();
$resultSet étant un objet de classe héritant de ArrayObject et contenant une liste de données.
Et même dans le cas là où la sémantique est respectée et où le code veut encore dire quelque chose, on y gagne quoi dans le premier exemple ? 12 caractères. Avant la pré-compilation.
Si j'avais un $resultset->addItem(1) qui ajoute un item à mon ArrayObject et retourne void, ça serait illogique qu'il retourne $resultset. S'il devait retourner autre chose que void, il devrait retourner un boolean qui décrit le résultat de la commande par exemple. Et ose me dire que la ligne suivante ne te choque pas :
$resultset->addItem(1)->addItem(2)->addItem(3)->addItem(4)->addItem(5)->addItem(6)->addItem(7);
Non, franchement, ça vaut pas le coup. Et jQuery c'est mauvais !