Pourquoi Eclipse et les annotations ont révolutionné ma façon de coder
Comme beaucoup d'entre vous, "vi" (prononcez "vi-aille") a longtemps été mon ami pour développer mon code PHP.
Il me plaisait bien, j'avais l'air d'être un geek et tout allait bien.
Et puis il y a quelques temps (2-3 ans), j'ai du travailler chez un client avec Eclipse + Plugin PHP (que je connaissais en Java mais qui ne m'attirait pas à l'époque pour PHP au vu du peu de fonctionnalités que je pensais disponible pour cette plateforme).
Ce type d'outils généère un bloc de commentaire au dessus d'une fonction ou méthode quand on en créé une (après le passage à la ligne par l'appui sur la touche entrée).
J'ai vu qu'il rajoutait des annotations ou doclet :
/** * ... * * @return unknown */
Je me suis dit que j'allais alors remplacer unknown par le type retourné quand il s'agissait d'un objet, par exemple :
/** * ... * * @return Sms */
(en ce temps là, j'avais encore le temps de perdre mon temps à faire des choses d'apparences inutiles dans le code...).
Jusque là, rien de transcendant. Et puis dans un autre fichier, j'ai utilisé ma méthode nouvellement créé, et là, surprise ! Eclipse me propose une autocomplétion avec la liste de toutes les méthodes de la classe Sms ! Cool !
Je me suis dit alors que j'allais essayer de systématiquement mettre les @return, et que ca m'aiderait à aller plus vite (plutôt que de chercher le nom des méthodes dispos en switchant entre les fichiers).
Puis je remarque aussi qu'il me met des @param au-dessus des méthodes / fonctions, pour décrire les paramètres de mes fonctions.
/** * ... * * @param unknown $sms * @param unknown $xmlelement * * ... */
Je décide alors d'essayer de mettre des types, par exemple :
/** * ... * * @param Sms $sms le sms à envoyer * @param SimpleXMLElement $xmlelement le document xml * * ... */
Et je me rends compte qu'il m'indique ces types dans l'autocompletion, ce qui m'aide dans de nombreux cas pour savoir quoi passer à la fonction / méthode.
Par contre, j'ai souvent plein de méthodes qui ne renvoient rien, donc je ne bénéficie d'aucune complétion automatique dans ces cas là.
Je me dis alors, et si je renvoyait $this ?
Je mets alors des :
...
{
...
return $this;
}
à la fin de chaque méthode qui n'est pas censée renvoyer un résultat (par exemple les setters !) et je rajoute l'annotation @return <la-classe-de-$this>.
Du coup, je peux chainer certains de mes appels :
$sms = new Sms();
$sms->setSender(...)
->addRecipient(...)
->addRecipient(...)
->setMessage(...)
->send();
Cool ! Pas mal, mon code devient plus concis et du coup, j'essaye de nommer mes méthodes pour que leur "enchainement" soit compréhensible et lisible. En plus, j'ai l'auto-complétion pour chacune des lignes grâce à mon @return !
La cerise sur le gâteau : j'utilise alors le pattern Factory (avec une méthode statique) :
/**
* Returns a new instance of this class
*
* @return Sms
*/
public final static function getInstance()
{
return new self;
}
Ce qui me permet de simplifier encore mon utilisation de mes classes :
Sms::getInstance()
->setSender(...)
->addRecipient(...)
->addRecipient(...)
->setMessage(...)
->send();
On pourrait même mettre tout sur une ligne ! (attention à la lisibilité).
Si je résume, grâce à Eclipse et aux annotations :
- j'ai tendance à utiliser plutôt les structures objet pour avoir l'auto-complétion (plutôt que de multiple paramètre string)
- je fais des return $this systématiques pour les méthodes qui ne renvoient rien
- j'ai simplifié et rendu plus lisible les noms de mes méthodes pour qu'elles s'inscrivent harmonieusement dans un enchainement
- j'ai typé mes paramètres de fonctions / méthodes ce qui me permet d'avoir l'auto-complétion sur les paramètres dans mes fonctions
- je crée quasi systématiquement des factories pour éviter de passer par une variable inutile
- ...
Une vraie révolution, à l'époque, pour moi !
Et vous, quels sont les petits trucs qui ont révolutionné votre manière de coder ?
Commentaires
J'utilise Komodo edit. J'ajoute les doc block à la main, mais ceux ci sont ensuite supportés par l'autocompletion ce qui est une bonne incitation à leur utilisation :)
J'avoue que je n'utilise pas trop le chainage des appels, le poids de ma pratique de PHP4 sans doute. Et généralement mes setters renvoient un booleen.
Même chose et même évolution que toi.
Au début c'était cool l'autocomplétion puis c'est devenu une vrai méthode de travail. Les appels chaînés sont devenu une religion...
Ce qui m'amène à regretter que les types scalaire de php ne soit pas objet et qu'on ai toujours les str... fonctions, les array... fonctions etc...
Attention ! Il ne faut pas mélanger les "doc block" (autrement dit PHPDoc) et les "annotations"; ce sont deux choses entièrement différentes dans Eclipse.
Ce que vous présentez-là sont les blocs de commentaire à la JavaDoc (pour PHP on dit PHPDoc). Alors que les annotations sont un ensemble de mises en relief du code que propose Eclipse. On trouve les options d'annotations dans General > Editors > Text Editors > Annotations. Elles permettent notamment avec PDT par exemple, d'avoir pour une variable toutes les occurrences surlignées en une couleur (lorsque le curseur se trouve dessus).
Il y a de très nombreux paramètres et on s'y perd vite, mais une bonne utilisation de ceux-ci permet de gagner beaucoup de temps.
Je recommande à tous ceux qui travaillent sur un fond noir de passer par là pour les problèmes de couleurs (light/light).
En passant :
"public function getInstance" n'est pas déclarée "static" ;)
@avetis: merci de la précision sur une des nombreuses fonctionnalités d'Eclipse ;)
Par contre, en PHP le terme Annotations est aussi utilisé pour ce type d'artefact, cf http://www.slideshare.net/stubbles/declarative-development-using-annotations-in-php, ou le package PEAR PHP_Annotation, entre autre.
J'en conviens il s'agit plus de doc bloc façon xdoclet (de mémoire en Java) plutôt que de réelle annotation à la sauce Java. Mais c'est un compromis déjà efficace à leur actuelle.
Merci pour la coquille sur le public final static getInstance, c'est corrigé.
Je ne suis pas un expert avec les patterns, pourrais-tu développer cette phrase ?
"je crée quasi systématiquement des factories pour éviter de passer par une variable inutile"
Dans un nouveau post éventuellement.
@Moosh: plus simplement (j'espère ;) ) :
$o = new MaClasse();
$o->maMethod1();
$o->maMethod2();
$resultat = $o->maMethod3();
Dans ce cas là, tu réalises un enchainement de méthodes qui appartiennent toutes à l'objet $o. La variable $o finalement ne te sert pas à grand chose si ce n'est de pouvoir appeller les méthodes les unes à la suite des autres. Pour factoriser tu pourrais faire :
$o = new MaClasse();
$resultat =
$o->maMethod1()
->maMethod2()
->maMethod3();
C'est déjà un peu mieux (on peut tout mettre sur une ligne si on veut), par contre on se rend compte que la variable $o ne sert à rien finalment car elle n'est plus jamais utilisée (dans la suite du script). Du coup tu pollue ton script avec des variable qui ne sont pas utilier (et aussi l'espace mémoire).
L'idée de la factory c'est finalement de faire une méthode static qui permettra d'éviter de mettre le "new MaClasse()" dans une variable :
$resultat = MaClasse::creeObjet()->maMethod1()->maMethod2()->maMethod3();Si tu as pris soin de mettre de nom de classe explicites, des noms de méthodes assez court et parlant, tes scripts peuvent être plus concis et plus parlant (tu peux aussi mettre sur plusieurs lignes).
La méthode creeObjet() peut aussi prendre un paramètre (c'est tout l'intérêt d'une factory), on sait qu'on fait souvent l'enchainement maMethod1() et maMethod2 dans notre code, et qu'en suite on a besoin de l'objet. On peut alors faire :
class MaClasse {
...
/**
* Fait quelquechose
*
* @return MaClasse
*/
public function maMethod1() {
...
return $this;
}
/**
* Fait quelquechose
*
* @return MaClasse
*/
public function maMethod2() {
...
return $this;
}
/**
* Fait quelquechose
*
* @return string
*/
public function maMethod3() {
...
return "un resultat quelconque ici";
}
/**
* Retourne une instance de la classe
*
* @param string $comment optionnel, quel type d'instance retourner ? Si null, une instance vierge, si 'm1_et_m2' => l'instance aura déjà eu maMethod1() et maMethod2() d'appellées
*
* @return MaClasse
*/
public final static function creeObjet($comment=null) {
$o = new self;
if (null !== $comment && 'm1_et_m2' === $comment) {
$o->maMethod1()->maMethod2();
}
return $o;
}
...
}
$result = MaClasse::creeObjet('m1_et_m2')->maMethod3();
Ma remarque voulais simplement dire qu'en utilisant ce principe de factory (qui prennent un paramètre éventuellement) je peux éviter de faire le new moi-même et éviter de stocker l'objet dans une variable (si combiné avec des 'return $this' dans chacune des maMethodX())
J'espère que c'est un peu plus claire, sinon n'hésites pas à demander des précis / rectifications
Pour moi, je travaille souvent sur Netbeans 6.5 grâce à ses fonctionnalités qui couvrent tous les besoins d'un developpeur professionnel
J'ai tendance à faire pareil sauf que je rajoute le typage :
@param array $tab : mon commentaire