Blog ENI : Toute la veille numérique !
🐠 -25€ dès 75€ 
+ 7 jours d'accès à la Bibliothèque Numérique ENI. Cliquez ici
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
  1. Livres et vidéos
  2. Java Spring
  3. Spring et les design patterns
Extrait - Java Spring Le socle technique des applications Jakarta EE (4e édition)
Extraits du livre
Java Spring Le socle technique des applications Jakarta EE (4e édition) Revenir à la page d'achat du livre

Spring et les design patterns

Introduction

Aujourd’hui, dans le monde Java, il est relativement rare d’écrire une nouvelle fonctionnalité technique. Nous assemblons plutôt des briques déjà faites en se basant sur des frameworks et des librairies open source. De plus en plus, nous essayons d’implémenter des briques fonctionnelles réutilisables afin de mutualiser le plus possible les développements. Il faut donc prendre soin de ne pas réinventer la roue d’une part et d’autre part, il faut s’efforcer de rendre son code réutilisable.

En effet, la maintenabilité du code est un sujet à prendre au sérieux car souvent c’est une équipe qui code et une autre qui fait la maintenance par la suite. Les projets s’inscrivent aussi dans la durée et sur des logiciels d’envergure développés sur plusieurs années, les développeurs ne font qu’une partie de l’application qui sera réutilisée par d’autres. Il faut à la limite considérer son code comme une partie d’un framework plus vaste et réutilisable à volonté. Le code se doit donc d’être modulaire, bien structuré, documenté avec la javadoc (avec des @snippet en Java 18) et doit aussi être testable (et testé).

Nous verrons dans le chapitre Documentation Spring REST Docs qu’il...

Le singleton en Java

Un singleton est un objet unique dans un espace donné. Il est créé lors de sa première utilisation pour être ensuite réutilisé. Nous l’utilisons quand nous souhaitons partager une ressource ou quand nous souhaitons profiter de la réentrance du code pour n’avoir qu’un exemplaire d’une séquence de traitement pour une préoccupation donnée, c’est-à-dire une méthode unique utilisable par plusieurs traitements que ce soit de façon séquentielle sur un système à un seul thread ou de façon concurrente (simultanée) grâce à la réentrance sur un système multi-threadé.

1. Préoccupation

Historiquement, quand nous avions besoin d’une action, nous l’implémentions dans l’objet qui en avait besoin. Pour un traitement identique, il y avait alors une multitude de copies de ces actions dans les diverses instanciations des objets qui l’utilisaient. Ceci était dû au fait que dans un système orienté objet, les traitements sont encapsulés dans les objets conjointement aux données. 

Pour les actions qui ne se basent pas sur l’état de l’objet qui la porte, nous avons décidé de déplacer l’action dans la partie statique de la classe. En effet la partie statique est partagée pour toutes les instanciations de la classe. Il n’y a donc plus de duplication du code en mémoire.

Certains services sont séparés des données qu’ils traitent et sont alors regroupés dans des classes sans instanciation, sous forme de classes massivement statiques. On retrouve un système qui sépare les données des traitements avec d’un côté des POJO, c’est-à-dire des objets sans traitement, à l’exception des mécanismes de gestion des instanciations comme les constructeurs et les accesseurs, et de l’autre côté des traitements séparés des données comme on le ferait en C. Avec ce type d’architecture, nous arrivons rapidement aux limites du système.

On élabora alors le singleton. Il s’agit d’une classe qui n’est instanciée qu’une fois et qui ensuite est réutilisée...

L’inversion de contrôle

Avec le principe de l’IOC (Inversion of Control, inversion de contrôle en français), les classes qui implémentent les fonctions d’une librairie ne sont plus instanciées et appelées directement par l’application. Le programme indique qu’il a besoin d’une librairie et elle lui est automatiquement fournie. C’est un mécanisme externe qui met à disposition la classe via un framework ou un mécanisme de plugins. Par exemple, avec Spring, l’application indique qu’elle a besoin d’un service avec une API précise et Spring lui fournit le meilleur candidat.

Spring permet de spécifier le service voulu, via une interface dans les cas les plus courants ou via une classe quand il n’y a pas d’interface. Il utilise pour cela généralement une fabrique d’objets qui est elle-même un design pattern pour mettre à disposition l’objet.

List<String> liste = new ArrayList<String>(); 
UtilisateurDao dao = (UtilisateurDao)  
factory.getBean("utilisateurDao"); 

Une bonne habitude à prendre en général en Java est de déclarer un objet par son interface le caractérisant le mieux. Plutôt que d’instancier un objet soi-même, on laisse à Spring le soin de trouver la classe qui correspond au mieux à l’objet...

Façade

Une façade est par exemple une classe qui concentre les API d’un ensemble de classes en un unique point dans une classe façade. Spring JDBC est un exemple de façade qui masque les différences entre les implémentations de diverses bases de données via une API unifiée. Spring JDBC permet également de simplifier la gestion des exceptions liées aux bases de données. Il offre une façade qui encapsule de façon uniforme des traitements très différents d’une base à une autre, mais qui sont vus comme identiques du côté de la façade.

images/02EP02N.png

Un service Spring (annoté avec @Service) peut être considéré comme une façade qui expose d’un côté des API métier et de l’autre côté s’interface avec les DAO pour effectuer les opérations métier et bases de données.

Cette façade permet alors de concevoir une API qui peut être simple, testable. Le programme qui utilise la façade n’a plus besoin des dépendances car celles-ci sont déplacées dans la façade.

Fabrique

Spring utilise un modèle de fabrique (factory) pour créer des beans à l’aide d’une référence au contexte applicatif. Ces beans sont décrits dans la configuration. Une partie de la configuration est fixée à l’avance alors qu’une autre partie est déduite à partir de règles relativement complexes. Spring trouve le meilleur candidat pour un contrat d’API donné.

BeanFactory factory = new XmlBeanFactory(new  
FileSystemResource("spring.xml")); 
Triangle triangle = (Triangle) factory.getBean("triangle"); 
triangle.draw(); 

Spring met à disposition une batterie complète de fabriques afin de couvrir un maximum de cas d’utilisation à travers l’interface BeanFactory. Cette interface a en effet neuf sous-interfaces et vingt-quatre implémentations dans sa dernière version.

images/02EP03N.png

La fabrique peut être visible dans le cas d’une application standalone. Nous utilisons alors une factory qui se base sur des fichiers comme celle citée dans l’exemple. Pour une application web, la fabrique se cache dans un filtre de servlet.

Décorateur

Un décorateur se base sur un composant concret qui implémente une interface. Le décorateur est lui-même dérivé de cette interface. Il inclut l’objet d’origine et lui ajoute des attributs ou des méthodes. Il permet l’interception de méthodes mais il utilise l’héritage.

images/02EP05N.png

Proxy

Ce design pattern est utilisé intensément par Spring pour tout ce qui concerne la programmation orientée aspect (AOP) et les accès à distance.

L’AOP est décrit en détail dans le chapitre Programmation orientée aspect avec Spring.

images/02EP06N.png

Spring ne donne pas un accès direct aux objets mais via un objet intermédiaire appelé proxy ce qui permet à Spring de modifier le comportement de l’objet d’origine.

interface Pojo3 {  
    public void foo();  
} 
 
class SimplePojo3 implements Pojo3 {  
    public void foo() {  
        System.out.println("foo");  
    }  
} 
 
class RetryAdvice implements AfterReturningAdvice {  
   public void afterReturning(Object returnValue, Method method,  
         Object[] args, Object target) throws Throwable {  
      System.out.println("Après "+method.getName());  
   }  
} 
 
public class MainAvecAspect {  
    public static void main(String[] args) {  
        ProxyFactory factory = new ProxyFactory(new SimplePojo3()); 
        Class<?> intf=Pojo3.class;  ...

Modèle Vue Contrôleur (MVC)

Spring MVC permet de faire simplement des applications web en séparant les trois éléments principaux :

Le modèle

Les données.

La vue

Ce qui est affiché.

Le contrôleur

Le traitement sur les données et l’enchaînement des vues.

Spring délègue le choix de la vue à un ViewResolver qui est, lui-même, un design pattern.

images/03RI07.png

Par exemple, le contrôleur suivant sur la sollicitation de l’URL /bonjourdemandera l’affichage de la page bonjour.jsp en lui fournissant la date pour que la page l’affiche :

@Controller 
public class BonjourController { 
  @RequestMapping("/bonjour") 
  public ModelAndView bonjour() { 
    ModelAndView mav = new ModelAndView(); 
    mav.setViewName("bonjour"); 
    mav.addObject("date", new Date()); 
    return mav; 
  } 

Les templates

On utilise des templates (modèles) pour tous les codes génériques.

images/02EP07N.png

Spring utilise massivement ces templates. Voici quelques exemples :

Template

Module

Utilité

RepeatTemplate

Spring Batch

Implémentation de RepeatOperations

JdbcTemplate

Spring

Opération courante JDBC

QueryDslJdbcTemplate

Spring Data

Support QueryDsl

NamedParameterJdbcTemplate

Spring

JdbcTemplate mais avec des paramètres nommés

TransactionTemplate

Spring

Gestion générique des transactions

HibernateTemplate

Spring

Opérations courantes sur Hibernate

JdoTemplate

Spring

JDO générique

JpaTemplate

Spring

Opération JPA

JpaTransactionManager

Spring

Transactions JPA

RestTemplate

Spring

Web service REST

SimpMessagingTemplate

Spring

Message (par exemple JMS)

Ce livre présente des exemples pour l’utilisation de certains de ces templates.

Les templates sont une grande force pour Spring car ils simplifient le code. Ils masquent la complexité et il est même possible d’interfacer un système exotique en étendant un template Spring et d’offrir une interface standard aux développeurs.

Stratégie

L’injection de dépendances (DI) est un design pattern de stratégie. En effet, chaque fois que vous voulez mettre en place une logique d’interchangeabilité entre beans, vous trouverez une interface qui correspond à un constructeur ou une méthode setter appropriée sur la classe d’accueil pour câbler votre propre implémentation de cette interface.

images/02EP08N.png

Pour une même interface, nous pouvons demander à Spring d’injecter un objet implémentant l’interface et choisir ainsi un objet ou un autre ayant les mêmes API de traitement mais avec des API ayant des comportements différents. Par exemple, pour une liste qui a pour interface List on pourra choisir entre les implémentations suivantes : AbstractList, AbstractSequentialList, ArrayList, AttributeList, CopyOnWriteArrayList, Linked List, RoleList, RoleUnresolvedList, Stack, Vector... en fonction du comportement que l’on souhaitera avoir.

Points clés

  • Le framework Spring est massivement orienté design pattern.

  • Il faut identifier les design patterns pour les signaler dans les commentaires du code.

  • Avant de coder quelque chose, il faut voir s’il s’agit d’un design pattern et réutiliser ou spécialiser du code qui implémente déjà ce patron de conception. 

  • Coder avec l’esprit design pattern permet à son code d’être plus facilement réutilisable.