Cela fait déjà un an et demi que je suis (enfin) passé à Java 8 et, ma foi, je ne regrette pas le voyage. Java a (enfin) une librairie de Collection digne de ce nom (La librairie de Collection était déjà bien faite, mais il lui manquait des features qui existent déjà depuis de nombreuses années chez de nombreux langages).
Mais surtout ça permet de ressortir ce bon vieux débat entre la programmation déclarative et impérative, notamment grace à l'apport des Streams et des lambdas au langage Java.
Je ne vous réexpliquerai pas en détail ce qu'est un stream ou un lambda, d'autres l'ont déjà fait mieux que moi, mais j'ai plutôt envie de prendre un petit exemple avec l'exercice du FooBarQix pour illustrer ça.
Le FooBarQix
Alors d'abord, c'est quoi l'exercice de FooBarQix ? Pour ceux qui l'ignore, il s'agit d'un bout de code un peu crétin qui consiste à afficher 100 nombres, mais avec quelques exceptions :
- Quand on tombe sur un nombre qui est un multiple de 3 ou qui contient 3, on affiche FOO
- Quand on tombe sur un nombre qui est un multiple de 5 ou qui contient 5, on affiche BAR
- Quand on tombe sur un nombre qui est un multiple de 7 ou qui contient 7, on affiche QIX
- Et c'est cumulatif (par exemple pour 35 on affiche BARQIXFOOBAR)
Il y a quelques années, cet exercice était utilisé par Facebook pour faire un premier tri dans les candidatures pour un poste de développeur. Depuis, on retrouve surtout cet exercice sur des posts de blogs pour illustrer quelques techniques de programmation, même si, FooBarQix n'apporte pas grand chose en terme de valeur.
Commençons ce problème par les multiples de 3, 5 et 7 qui doivent afficher respectivement FOO, BAR et QIX
Essayons d'implémenter ça dans une approche pré-Java 8, cela donne ça :
On retrouve bien tous les signes de la programmation impérative, à savoir que ce code décrit toutes les opérations en séquences d'instructions exécutées pour modifier l'état du programme (cf Wikipedia). C'est le mode de programmation le plus répandu aujourd'hui, et il suffit de lire le code pour comprendre comment il fonctionne. Voyons maintenant ce que ça donne avec des streams et des lambdas :
Passons sur l'initialisation de la Map (pourra-t-on un jour faire Map foobar = (3 -> "FOO", 5 -> "BAR") ?) et regardons le reste. On est plutôt dans une forme de programmation déclarative où l'on se concentre sur le quoi (je filtre, je transforme et je collecte dans une chaine de caractère) et non sur le comment (avec des if/then/else). Ma foi c'est pas trop mal et ça se rapproche doucement de la programmation fonctionnelle.
Et bien sûr, ça se teste comme ça :
Et maintenant occupons-nous des nombres qui contiennent 3, 5, 7 et qui doivent afficher FOO, BAR ou QIX
Dans une approche impérative voilà ce que ça donne :
Et dans une approche plus déclarative :
Et ça se teste comme ça :
Et pour finir
Rappelez vous de l'exercice du FooBarQix, il faut combiner le code qui teste si un nombre est multiple et s'il contient un chiffre, et c'est là que ça devient intéressant. En programmation impérative, pré-Java8, sans surprise, le code décrit pas à pas chacune des instructions. Il suffit de lire le code sans trop réfléchir pour comprendre ce qu'il fait. On obtient ça :
En utilisant les Streams et le lambdas, et en partant vers une programmation plus déclarative, on se retrouve avec du code plus abstrait, il faut comprendre ce qu'il y a derrière les mots "filter", "map" et "collect" mais Heureusement, ces opérations sont assez claires, et rendent finalement le code plus simple.
Là où il y a encore à redire, c'est dans la connexion entre l'instruction qui vérifie le multiple et la suivante, Java ne propose malheureusement rien pour combiner ces deux bouts de code. On peut toujours les séparer dans plusieurs méthodes de classe :
Mais ça devient soudainement trop compliqué pour notre petit exercice de FooBarQix. Cependant, sur du code plus gros, c'est assurément une démarche à suivre.
Mélanger programmation impérative, déclarative et orientée objet est finalement devenu le pain quotidien des développeurs qui utilisent Java 8 ...