Traductions pour la plateforme Java

Groovy: un DSL pour les programmeurs Java

Article d'origine: 

Ecrire moins de code et être plus productif avec Groovy

Résumé: 

L'expert Groovy Scott Davis relance la série Groovy par la pratique, en sommeil depuis 2006. Ce volet initial vous met au courant de l'histoire récente de Groovy et de sa situation actuelle. Puis vous verrez à quel point il est facile de se mettre à Groovy en 2009.

Andrew Glover a commencé à écrire sur Groovy pour developerWorks en 2004, commençant par l'article d'introduction "Feeling Groovy" dans la série alt.lang.jre, et continuant avec la série de longue haleine "Groovy par la pratique". C'était avant qu'aucun livre soit publié sur Groovy (il en existe plus d'une douzaine maintenant), et des années avant la version 1.0 de Groovy en janvier 2007. Il y a eu bien des changements depuis que le dernier volet de Groovy par la pratique a été publié fin 2006.

De nos jours, Groovy est téléchargé en moyenne à peu près 35000 fois par mois. Des sociétés relativement conservatrices comme la Mutuelle d'Omaha ont plus de 70000 lignes de code Groovy en production. Groovy a une des listes de diffusion les plus actives de Codehaus.org, qui héberge le projet (voir les Ressources). Le seul projet ayant plus de téléchargements et une liste de diffusion plus active est Grails, un framework Web populaire implémenté en Groovy (voir les Ressources).

Exécuter d'autres langages que Java sur une JVM n'est plus simplement commun, cela fait partie du coeur de la stratégie de Sun pour la JVM. Groovy rejoint à d'autres langages supportés par Sun comme JavaScript, JavaFX, JRuby et Jython dans la valse des alternatives. Ce qui était expérimental en 2004 constitue désormais l'état de l'art.

Ecrire sur Groovy en 2009 est, à bien des égards, assez semblable à ce que c'était lorsqu'Andy a commencé. La syntaxe s'est stabilisée en 2005 et reste la même aujourd'hui. Des fonctionnalités nouvelles et fascinantes sont ajoutées à chaque version, mais préserver la compatibilité descendante est une priorité pour les responsables du projet. Cette fondation solide fait de Groovy un choix privilégié pour les sociétés développant en Java et ayant l'habitude de se fier à des technologies qui seront encore sur le marché aussi longtemps que leurs applications seront en production.

Cet article a pour but de transformer rapidement des développeurs Java expérimentés en développeurs Groovy. Ne soyez pas dupes de son côté introductif. Cette série, comme son nom l'indique, est entièrement consacrée aux utilisations pratiques des savoir-faire Groovy. Une fois que vous aurez dit "Hello, World" dans cette introduction en douceur, préparez-vous à entrer très vite dans le monde réel.

Installer Groovy

Si vous n'avez jamais travaillé avec Groovy auparavant, la première chose à faire est de l'installer. L'installation est relativement facile. Il s'agit de la même procédure que pour installer d'autre applications Java courant comme Ant et Tomcat, ainsi que la plateforme Java elle-même:

  1. Télécharger l'archive (ZIP ou tar) de Groovy la plus récente
  2. Dézipper l'archive dans le répertoire de votre choix. (Il vaut mieux éviter les noms de répertoire contenant un espace)
  3. Créer une variable d'environnement GROOVY_HOME
  4. Ajouter GROOVY_HOME/bin à la variable PATH

Groovy préfère Java 5 ou 6. Entrez java -version à l'invite de commande pour vérifier que vous êtes à jour. Puis entrez groovy -version pour vous assurer que Groovy est bien installé.

Les principaux IDEs (Eclipse, IntelliJ et Netbeans) ont un plugin Groovy qui incorpore des fonctionnalités comme l'autocomplétion et le débuggage pas à pas. Bien qu'un bon IDE soit presque une nécessité pour écrire du code Java de nos jours, ce n'est pas forcément vrai pour ce qui est de Groovy. Grâce à la concision du langage Groovy, de nombreuses personnes choisissent d'utiliser un simple éditeur de texte. Des éditeurs Open Source tels que vi et Emacs supportent Groovy, de même que des éditeurs de texte commerciaux très peu couteux comme Textpad (pour Windows®) et TextMate (pour Max OS X). (Voir les ressources pour plus d'informations.)

Comme vous le verrez plus loin dans l'article, il est facile d'intégrer Groovy dans un projet Java existant. Tout ce que vous avez à faire est d'ajouter au classpath un JAR Groovy qui se trouve dans GROOVY_HOME/embeddable et d'inclure la tache javac existante dans une tache groovyc. (Maven offre un support similaire)

Mais avant que nous n'allions trop loin, nous allons commencer par l'indispensable "Hello World".

Hello Groovy World

Vous savez ce qu'un exemple de type "Hello Wold" est censé démontrer - le programme le plus simple qu'il soit possible d'écrire dans un langage donné. Ce qui est intéressant avec "Hello World" en Java, figurant dans le Listing 1, c'est la somme de connaissance du langage qui est nécessaire pour comprendre complètement ce qui se passe:

Listing 1. Un exemple de "Hello World" en Java

public class HelloJavaWorld {
  public static void main(String[] args){
    System.out.println("Hello Java World");
  }
}

Il faut commencer par créer un fichier nommé HelloJavaWorld.java et y saisir public class HelloJavaWorld. La première leçon délicate qu'apprennent beaucoup de développeurs Java est que si le nom de la classe et celui du fichier ne sont pas exactement les mêmes (y compris pour ce qui est de la casse), la classe ne compilera pas. De plus, les étudiants curieux vont d'ores et déjà se poser des questions sur les modificateurs d'accès comme public et private.

La ligne suivante - public static void main(String[] args) - déchaine généralement une avalanche de questions sur des détails d'implémentation: Que veut dire static ? Que veut dire void ? Pourquoi la méthode doit-elle être nommée main ? Qu'est-ce qu'un tableau de String ? Et finalement, essayez d'expliquer à un développeur Java novice que out est une instance public, static, final d'un objet PrintStream de la classe System. Je n'oublierai jamais l'étudiant qui a dit "Oh la la ! Tout ce que je voulais était dire 'Hello.'"

Mettez ceci en contraste avec "Hello world" en Groovy. Créez un fichier nommé HelloGroovyWorld.groovy et tapez la ligne indiquée dans le Listing 2:

Listing 2. Un exemple de "Hello World" en Groovy

println "Hello Groovy World"

Oui, il s'agit de l'équivalent en Groovy de l'exemple Java du Listing 1. Ici, tous les détails d'implémentation - le "bagage" qui ne contribue pas immédiatement à la résolution du problème en question - s'évanouit dans la nature, vous laissant simplement avec le code qui dit "Hello.". Tapez groovy HelloGroovyWorld pour confirmer que cela fonctionne.

Cet exemple trivial illustre ce que Groovy propose: réduire de manière spectaculaire les lignes de code qu'il faut écrire tout en préservant la sémantique de l'équivalent Java. Dans la section suivante, nous explorerons davantage cette idée.

Creuser davantage Hello World

Les programmeurs Java expérimentés savent qu'il faut compiler son code avant qu'il soit exécuté dans une JVM. Pourtant, il ne semble exister nulle part de fichier class pour le script Groovy. Cela signifie-t-il que l'on peut exécuter du code Groovy directement ? La réponse est "Pas vraiment, mais ça y ressemble pour de bon, n'est-ce-pas ?"

L'interpréteur Groovy compile le code source en mémoire avant de le passer à la JVM. Vous pouvez effectuer cette étape manuellement en tapant groovyc HelloGroovyWorld.groovy. Cependant, si vous essayez d'exécuter la classe résultante en utilisant java, vous tomberez sur l'exception du Listing 3:

Listing 3: Essai d'exécution d'une classe Groovy compilée sans le JAR de Groovy dans le CLASSPATH

$ java HelloGroovyWorld
Exception in thread "main" java.lang.NoClassDefFoundError: groovy/lang/Script

Comme je l'ai signalé précédemment, le JAR de Groovy doit être inclus dans le CLASSPATH. Essayez à nouveau, cette fois en passant l'argument -classpath à java, comme indiqué dans le Listing 4:

Listing 4: Exécuter avec succès une classe Groovy compilée avec la commande java

//Pour UNIX, Linux, et Mac OS X
$ java -classpath $GROOVY_HOME/embeddable/groovy-all-x.y.z.jar:.
        HelloGroovyWorld
Hello Groovy World

//Pour Windows
$ java -classpath %GROOVY_HOME%/embeddable/groovy-all-x.y.z.jar;.
        HelloGroovyWorld
Hello Groovy World

On commence à avancer. Mais afin de prouver que le script Groovy préserve réellement la sémantique de l'exemple Java, vous devez creuser davantage le bytecode. Pour commencer, tapez javap HelloJavaWorld, comme indiqué dans le Listing 5:

Listing 5. Examen du bytecode Java

$ javap HelloJavaWorld
Compiled from "HelloJavaWorld.java"
public class HelloJavaWorld extends java.lang.Object {
  public HelloJavaWorld();
  public static void main(java.lang.String[]);
}

Il ne devrait pas y avoir trop de surprises ici, à part quelques subtilités que le compilateur javac a ajouté pour vous. Vous n'avez pas eu besoin de taper extends java.lang.Object explicitement ou de fournir un constructeur par défaut pour la classe.

Maintenant, tapez javap HelloGroovyWorld, comme dans le Listing 6:

Listing 6. Examen du bytecode Groovy

$ javap HelloGroovyWorld
Compiled from "HelloGroovyWorld.groovy"
public class HelloGroovyWorld extends groovy.lang.Script {
  ...
  public static void main(java.lang.String[]);
  ...
}

Ici, vous constatez que le compilateur groovyc a créé une classe du même nom que votre fichier source. (Le fait que la classe étende groovy.lang.Script au lieu de java.lang.Object devrait vous aider à comprendre pourquoi essayer d'exécuter le fichier sans le JAR de Groovy dans le CLASSPATH lance une exception NoClassDefFoundError). Parmi toutes les méthodes ajoutées par le compilateur, vous devriez trouver une bonne vieille méthode public static void main(String[] args). Le compilateur groovyc a encapsulé les lignes de votre script dans cette méthode pour préserver la sémantique Java. Ceci signifie que Groovy vous permet de tirer parti de toutes vos connaissances Java existantes.

Par exemple, voici comment vous pouvez utiliser des paramètres en ligne de commande dans un script Groovy. Créez un nouveau fichier Hello.groovy et ajoutez la ligne du Listing 7:

Listing 7. Un script Groovy qui prend une entrée en ligne de commande

println "Hello, " + args[0]

Maintenant tapez groovy Hello Jane en ligne de commande. Le tableau de String args est là, comme tout développeur Java s'y attendrait. Utiliser args ici n'est peut-être pas compréhensible au non-initié, mais cela se tient parfaitement pour les développeurs Java confirmés.

Groovy réduit le code Java à sa plus simple expression. Le script Groovy que vous venez d'écrire ressemble beaucoup à du pseudocode exécutable. Il est d'apparence assez simple pour être compris par des novices, mais il n'écarte pas la puissance sous-jacente du langage Java pour les développeurs expérimentés. C'est pour cette raison que je dis que Groovy est un langage dédié (Domain Specific Language ou DSL) pour la plateforme Java. (Voir la section "Qu'est-ce-qu'un DSL ?").

Qu'est-ce-qu'un DSL ?

Martin Fowler a popularisé la notion de langage dédié (voir les Ressources). Il définit un DSL comme un "langage de programmation d'une expressivité limitée destiné à un domaine particulier". Par "expressivité limitée", il ne veut pas dire utilité limitée, mais plutôt que le langage dispose uniquement du vocabulaire nécessaire pour le rendre approprié à un "domaine particulier". Un DSL est un langage limité ayant un but spécifique, par opposition à un langage généraliste comme le langage Java.

SQL est un bon exemple de DSL. On ne pourrait pas écrire de système d'exploitation avec SQL, mais il est idéalement adapté au domaine restreint de la prise en charge des bases de données relationnelles. De manière très semblable, Groovy est un DSL pour la plateforme Java en ce qu'il est idéalement adapté au domaine restreint du développement Java. Mon utilisation du terme DSL ici a pour but d'illustrer mon propos sans être tout a fait exacte. Peut-être qu'en disant que Groovy est un DSL interne pour les idiomes Java courants, je m'attirerais moins les inévitables foudres des puristes des DSL.

Dave Thomas clarifie davantage la notion de DSL (voir les Ressources). Il écrit : "Quand des experts d'un domaine communiquent ... ils utilisent un jargon, un langage spécialisé qu'ils ont inventé à la manière d'un raccourci pour communiquer efficacement avec leurs pairs.". Nous nous approchons peut-être davantage de l'explication de la relation entre Groovy et Java en disant que Groovy est un "Java raccourci". La section suivante de l'article nous en donne un autre exemple.

Plain Old Groovy Objects (ndT: Bons vieux objets Groovy)

Les Javabeans - ou plus familièrement les POJOs (Plain Old Java Objects ndT: Bons vieux objets Java) - constituent un des éléments de base du développement Java. Il faut remplir un certain nombre de critères bien définis quand on crée un POJO pour représenter un objet métier. La classe devrait être publique, et les champs devraient être privés avec un jeu de méthodes get et set publiques. Le Listing 8 montre un POJO typique:

Listing 8. Un POJO

public class JavaPerson {
  private String firstName;
  private String lastName;

  public String getFirstName(){ return firstName; }
  public void setFirstName(String firstName){ this.firstName = firstName; }

  public String getLastName(){ return lastName; }
  public void setLastName(String lastName){ this.lastName = lastName; }
}

Les Plain Old Groovy Objects (POGOs) sont un équivalent des POJOs. Ils gardent tout à fait la même sémantique que les POJOs tout en réduisant spectaculairement la quantité de code à écrire. Le Listing 9 montre une classe écrite en Groovy représentant une personne "raccourcie":

Listing 9. Un POGO

class GroovyPerson {
  String firstName
  String lastName
}

Toutes les classes Groovy sont publiques par défaut. Toutes les propriétés sont privées, et toutes les méthodes sont publiques. Le compilateur génère un jeu de méthodes get et set publiques pour chaque propriété automatiquement. Compilez JavaPerson avec javac et GroovyPerson avec groovyc. Ensuite exécutez javap sur chacune d'elles pour confirmer que l'exemple Groovy fait tout ce que fait l'exemple Java, y compris étendre java.lang.Object. (On n'avait pas spécifié de classe dans l'exemple HelloGroovyWorld précédent, donc Groovy a créé une classe qui étend groovy.lang.Script à la place.)

Tout ceci signifie que vous pouvez immédiatement utiliser des POGOs à la place de vos POJOs. La classe Groovy est la classe Java réduite à sa plus simple expression. Une fois la classe Groovy compilée, les autres classes Java peuvent l'utiliser aussi facilement que si elle avait été écrite en Java. Pour le prouver, créer un fichier nommé JavaTest.java et ajoutez-y le code du Listing 10:

Listing 10: Appel de classes Groovy à partir de code Java

public class JavaTest {
  public static void main(String[] args){
    JavaPerson jp = new JavaPerson();
    jp.setFirstName("John");
    jp.setLastName("Doe");
    System.out.println("Hello " + jp.getFirstName());

    GroovyPerson gp = new GroovyPerson();
    gp.setFirstName("Jane");
    gp.setLastName("Smith");
    System.out.println("Hello " + gp.getFirstName());
  }
}

Même si les getters et les setters n'apparaissent pas dans le code source Groovy, ce test prouve qu'elles sont bien là et pleinement fonctionnelles dans la classe Groovy compilée. Mais cet exemple ne serait pas complet si je ne vous montrais pas le test correspondant en Groovy. Créez un fichier nommé TestGroovy.groovy et ajoutez-y le code du Listing 11:

Listing 11. Appel de classes Java à partir de code Groovy

JavaPerson jp = new JavaPerson(firstName:"John", lastName:"Doe")
println "Greetings, " + jp.getFirstName() + ". 
   It is a pleasure to make your acquaintance."

GroovyPerson gp = new GroovyPerson(lastName:"Smith", firstName:"Jane")
println "Howdy, ${gp.firstName}. How the heck are you?"

La première chose que vous avez probablement noté est le nouveau constructeur offert par Groovy, qui vous permet de nommer les champs et de les spécifier dans l'ordre que vous désirez. Encore plus intéressant, vous pouvez utiliser ce constructeur soit dans des classes Java, soit dans des classes Groovy. Comment est-ce possible ? En réalité, Groovy appelle le constructeur sans arguments par défaut et appelle ensuite les setters et getters appropriés pour chaque champ. Vous pourriez obtenir un comportement similaire avec Java, mais du fait que Java n'a pas d'arguments nommés et que les deux champs sont des Strings, il est impossible de passer les champs nom et prénom dans n'importe quel ordre.

Ensuite, notez que Groovy supporte la manière Java traditionnelle pour concaténer des chaines, de même que la manière Groovy d'embarquer du code entouré par ${} directement dans un String. (Ces derniers sont appelés GStrings, abréviation de Groovy Strings.)

Enfin, vous avez un aperçu des améliorations syntaxiques de Groovy à votre disposition lors de l'appel de getters d'une classe. Au lieu d'utiliser le plus verbeux gp.getFirstName(), vous pouvez appeler simplement gp.firstName. Cela peut ressembler à un accès direct du champ, mais en réalité il s'agit simplement d'un appel caché du getter correspondant. Les setters fonctionnent sur le même principe: gp.setLastName("Jones") et gp.lastName = "Jones" sont équivalents, ce dernier effectuant un appel au premier en arrière-plan.

J'espère que vous conviendrez qu'à chaque fois, la version Groovy ressemble à un raccourci de la version Java - ce que les "experts du domaine" utiliseraient pour "communiquer efficacement avec leurs pairs", ou quoique ce soit s'apparentant à un gentil petit badinage entre vieux amis.

Le code Groovy n'est au bout du compte rien d'autre que du code Java

Un des aspects les plus sous-estimés de Groovy est qu'il supporte entièrement la syntaxe Java. Comme je l'ai mentionné auparavant, vous n'êtes pas obligé de désapprendre si peu que ce soit Java quand vous passez à Groovy. Lorsque vous débutez en Groovy, le plus gros de votre code ressemble beaucoup à du Java classique. Mais au fur et a mesure que vous vous accoutumerez à la nouvelle syntaxe, le code évoluera progressivement pour adopter le style plus concis et expressif de Groovy.

Pour prouver que le code Groovy peut être exactement le même que le code Java, copiez JavaTest.java dans JavaTestInGroovy.groovy, et tapez groovy JavaTestInGroovy. Vous devriez obtenir une sortie identique, mais notez que vous n'avez pas eu à compiler la classe Groovy avant son exécution.

Ceci démontre que Groovy devrait être un choix presque évident pour les développeurs Java expérimentés. Du fait que la syntaxe Java est une syntaxe Groovy valide, la courbe d'apprentissage initiale n'existe pratiquement pas. Vous pouvez utiliser votre propre version de Java avec Groovy, votre propre IDE et votre propre environnement de production. Votre routine journalière devrait donc être très peu perturbée. Tout ce que vous avez à faire est vous assurer que le JAR Groovy est dans le CLASSPATH et modifier les scripts de compilation pour que les classes Groovy soient compilées avec les classes Java. La section suivante montre comment ajouter la tache groovyc à un fichier build.xml de Ant.

Compiler du code Groovy avec Ant

Si javac était un compilateur modulaire, on pourrait lui dire de compiler à la fois les fichiers Groovy et Java en même temps. Parce qu'il ne l'est pas, on inclut simplement la tache Ant javac dans une tache groovyc. De cette manière, groovyc compile le code source Groovy et javac le code source Java comme il l'a toujours fait.

Bien sûr, groovyc peut compiler des fichiers Java ainsi que des fichiers Groovy, mais vous rappelez-vous les méthodes utilitaires supplémentaires que groovyc avait ajouté à HelloGroovyWorld et à GroovyPerson ? Ces méthodes supplémentaires seraient également ajoutées aux classes Java. Il vaut mieux laisser groovyc compiler les fichiers Groovy et javac les fichiers Java.

 

Pour faire appel à groovyc depuis Ant, définissez la tache au moyen de taskdef puis utilisez la tache groovyc comme vous utiliseriez normalement la tache javac (voir les Ressources pour plus d'informations). Le Listing 12 montre le script de construction Ant:

Listing 12. Compiler du code Groovy et du code Java avec Ant

<taskdef name="groovyc"
        classname="org.codehaus.groovy.ant.Groovyc"
        classpathref="my.classpath"/ >
  <groovyc srcdir="${testSourceDirectory}" destdir="${testClassesDirectory}" >
    <classpath >
      <pathelement path="${mainClassesDirectory}"/ >
      <pathelement path="${testClassesDirectory}"/ >
      <path refid="testPath"/ >
    </classpath >
  <javac debug="on" / >
</groovyc >

Au fait, ces chaines contenant ${} ressemblent étrangement à des GStrings, n'est-ce-pas ? Groovy est un langage qui garde le meilleur, empruntant sans vergogne la syntaxe et les fonctionnalités d'autres langages et librairies. Ce n'est pas la dernière fois que vous verrez quelque chose écrit en Groovy qui vous fait penser : "Hum, est-ce-que je n'ai pas déjà vu çà quelque part ?"

Conclusion

Cela a été un voyage éclair dans Groovy. Vous avez quelque peu appris d'où Groovy est parti et où il en est actuellement. Vous avez installé Groovy sur votre système, et au travers de quelques exemples simples vous avez eu un aperçu de la puissance que Groovy offre aux développeurs Java.

Groovy n'est pas le seul langage alternatif à s'exécuter sur la JVM. JRuby est une excellente solution pour les développeurs Java qui connaissent déjà Ruby. Jython est une excellente solution pour les développeurs Java qui connaissent déjà Python. Mais comme vous l'avez vu, Groovy est une excellente solution pour les développeurs Java qui connaissent déjà Java. Le fait que Groovy offre une syntaxe concise à la Java qui préserve aussi la sémantique est assez fascinant. De plus, un nouveau langage dont l'adoption n'implique pas de del *.* ou de rm -Rf * change de manière agréable, n'est-ce-pas ?

La prochaine fois, vous en saurez plus sur les itérations en Groovy. Quand on code, on a fréquemment besoin de parcourir des choses morceau par morceau, qu'il s'agisse d'une liste, d'un fichier ou d'un document XML. Vous verrez de plus près la fermeture each que l'on retrouve partout. En attendant, j'espère que vous trouverez beaucoup d'utilisations pratiques de Groovy.