Admettons que vous ayez ce genre de code :
Entity x = getEntity1();
Entity y = getEntity2();
Entity z = x.get() + y.get();
Il a l’air tout bête comme ca mais il peut générer des comportements inattendu. Par exemple getEntity1() peut renvoyer une exception, ou alors y.get() peut renvoyer null.
Si on se met à gérer tous les cas d’erreurs on a vite fait de submerger le code métier avec du code technique.
Pour palier à ca on peut par exemple utiliser l’AOP pour rajouter des comportements autour de certaines méthodes.
Si on utilise un langage de programmation fonctionnel un autre moyen est d’utiliser des Monades.
Cette monade va permettre de gérer tout une liste d’opération sans avoir à gérer les cas d’exception dans le code métier. Ainsi le code restera lisible.
Soit une map avec des continents, des pays et des capitales :
Splitter.MapSplitter splitter = Splitter.on(", ").withKeyValueSeparator(" -> ");
Map<String, Map<String, String>> capitalCities = new HashMap<>() {{
put("Europe", splitter.split("France -> Paris, Espagne -> Madrid"));
put("Amérique", splitter.split("Etats-Unis -> Washington"));
}};
Pour afficher la capitale de la France voilà a quoi le code ressemblerait avec la programmation impérative classique:
Map<String, String> europe = capitalCities.get("Europe");
if (europe != null) {
String city = europe.get("France");
if (city != null && city.equals("Paris")){
System.out.println("La capitale de la France est : "+city);
}
}
Puis avec une Monade Option :
Option.wrap(capitalCities)
.andThen(accessContinent("Europe"))
.andThen(accessCountry("France"))
.andThen(printCapitalCity());
On remarque tout de suite que le deuxième exemple est beaucoup plus lisible. Même une personne avec un profile non technique pourrait le comprendre. Alors vous allez me dire que c’est de la triche car on a extrait la complexité dans d’autre méthode sur le second exemple. Oui mais même si je faisais la même chose sur le premier exemple je ne pourrais pas me débarrasser du code technique tel que les ifs et ainsi lire mon algorithme comme une succession d’étapes. Je ne pourrais pas non plus me débarrasser des créations de variables tel que europe et city. Ceci est rendu possible notamment par les closures, c’est à dire une fonction qui garde le contexte d’exécution et qui peut être passée en paramètre d’une autre fonction.
Pour avoir plus de détail sur l’implémentation de cet exemple je vous recommande vivement cet article écrit par François Sarradin du blog de Xébia dont je me suis fortement inspiré.
Pour conclure je vais me contenter de citer François Sarradin :
Ainsi, les monades sont :
une solution issue de la programmation fonctionnelle permettant de représenter une succession de traitements, tout comme la programmation impérative représente par une séquence d’instructions cette succession de traitements
un moyen d’encapsuler la notion d’état mutable et le confiner dans un « espace de travail »
des contextes d’exécution permettant de modifier le comportement non pas des traitements mais du chaînage sur ces traitements