Data-Oriented Programming (DOP) avec Java 19 — Kévin Llopis

CARBON
5 min readJan 31, 2023

Cet article est issu du blog de Kévin Llopis, développeur Fullstack chez CARBON IT. Vous pouvez retrouver tous ses articles sur son blog personnel.

Suite à la sortie de Java 19 en Septembre 2022, le défilé des JEP a eu lieu comme d’habitude, avec notamment les JEP 405 et 427. Elles permettent respectivement de déconstruire nos Records et de proposer des améliorations au Pattern Matching appliqué sur un switch. Mais ce que les JEP ne précisent pas, c’est la possibilité de pallier à certains problèmes rencontrés en programmation orientée objet (OOP : Object Oriented Programming). Nous allons voir cela en détail.

Problèmes sur les données en OOP

Lorsque l’on utilise l’OOP, on mélange les fonctions et les données dans des objets. Imaginez maintenant qu’on veuille mettre à jour le modèle de données, cette tâche est moins simple qu’elle n’y paraît parce que cela implique de considérer tous les impacts dans les méthodes implémentant les comportements métiers.

Bien évidemment, un problème courant est d’oublier certains cas de traitements liés à ces nouvelles données. Ce qui signifie que ce problème deviendra soit une future anomalie ou soit une spécificité simplement ignorée par les équipes.

Prenons l’exemple d’une API REST dédiée au rappel d’événements (Talk, BBL, Practice session) dans laquelle nous avons une fonctionnalité d’export des événements au format CSV :

Pour représenter les différents événements, on a les classes suivantes implémentant l’interface Event.

En effet, la méthode getCsvColumns()est redéfinie dans les classes ci-dessus en fonction des champs présents (et donc des données). On se sert de cette manière du polymorphisme pour obtenir le format des colonnes CSV correspondant à chaque type d’événement dans le cadre de la fonctionnalité d’export CSV :

Bien que ce soit fidèle aux standards du OOP, en cas d’ajout ou de suppression de champs dans nos classes du domaine (Talk, BBL et PracticeSession), on risque d’oublier de faire évoluer le format des colonnes CSV.

Le DOP à la rescousse !

Et si on pouvait identifier ces problèmes plus facilement plutôt que de risquer de les oublier, c’est là que le DOP intervient.

Le DOP a pour objectif de simplifier la conception des applications que ce soit côté frontend ou backend, tout en facilitant la gestion du modèle de données. A la différence du OOP, on ne mélangera pas les données et les fonctions. En effet, il s’agit de séparer ces deux éléments, de manière à avoir d’un côté des données représentées au sein de structures de données et de l’autre des fonctions pures (pour implémenter les comportements métiers).

Appliquer le DOP avec Java 19

Pour mettre en pratique le DOP, il est nécessaire d’appliquer les 4 principes énoncés par Yehonathan Sharvit (comme dans son livre Data-Oriented Programming) :

  1. Séparer les fonctions des données
  2. Représenter les données avec des structures de données génériques (dictionnaires, arrays)
  3. Garder les données immuables
  4. Séparer les données du domaine des DTO

Avec Java 19, représenter les données avec le DOP repose sur les Records (comme présenté par Rémi Forax au Paris JUG). En effet, avec les Records, le principe n°3 est bien respecté puisque ses champs sont immuables. Cela dit, l’utilisation de classes dans le DOP est habituellement interdite, du fait que ça nous incite à lier les données avec des méthodes dans la même classe (comme avec l’OOP). C’est pour cela qu’appliquer le DOP en Java nous impose de la discipline en évitant d’ajouter des méthodes à nos Records, en vue de respecter le principe n°1 énoncé précédemment.

Si on applique le DOP à l’application présentée précédemment, la première étape consiste à transformer nos classes du domaine sous la forme de Records. On distinguera le domaine représenté par nos Records et de l’autre les entités JPA puisqu’en effet les entités doivent être muables.

On peut constater que l’immuabilité des Records n’est pas parfaite, notamment lorsque l’un des champs est un objet muable. En effet, l’objet pourra muter en se servant du getter. C’est par exemple le cas de PracticeSession qui prend le champ List<Attendee> attendees. Pour pallier à ça et garder les données immuables, on va devoir redéfinir le constructeur en s’assurant que List<Attendee> attendees est affecté d’une liste immuable avec Collections.unmodifiableList(attendees).

Puis, en ce qui concerne le code de l’export CSV, au lieu de s’appuyer sur le polymorphisme, on va se baser sur le Pattern Matching pour lier nos Records à des fonctions pures. Pour ce faire, on fera appel à une méthode getCsvColumns(Event event)pour transformer nos Event en colonnes CSV.

En combinant switch/case et Pattern Matching, on peut spécifier des casestels que case Talk(String title,String description,LocalDateTime date,Speaker speaker). Ce qui signifie qu’en cas de changement dans la définition de Talk, une erreur apparaîtra à la compilation au niveau du casecorrespondant. Prenons l’exemple de l’ajout d’un nouveau champ difficultyLevel pour représenter le niveau de difficulté d’un Talk.

Puis, à la compilation on aura l’erreur suivante ci-dessous :

De cette manière, cela va obliger le développeur à faire évoluer tout le code dépendant des données, ce qui résout notre problème.

Conclusion

Bien que Java 19 ne soit pas une version LTS et que les choix faits pour appliquer le DOP peuvent être amenés à changer à l’avenir (d’ici la publication de la prochaine version LTS), le DOP basé sur les Records et le Pattern Matching, ouvre la voie à une nouvelle manière de développer en Java. En effet, cette alternative au OOP permettrait de résoudre un problème récurrent, c’est-à-dire l’évolution du modèle de données et des comportements métiers qui lui sont liés. De plus, qui aurait pu penser que Java serait autre chose qu’un langage de programmation orientée objet ? Dans tous les cas, il nous faudra patienter jusqu’à la prochaine version LTS pour nous assurer que développer par le DOP en Java est viable, mais rien ne nous empêche de nous y préparer …

Ressources

--

--

CARBON

Une communauté de passionnés, spécialisés dans l’expertise technique et les bonnes pratiques de développement web !