Tutoriel Java 2

Présentation

Cet article consistue la deuxième partie du tutoriel Java, dont l'objectif est de réaliser une application "Todo List" en langage Java avec une interface graphique écrite en Swing. Dans la première partie, nous avons développé les classes model et dao. Nous allons maintenant écrire les classes service, qui vont notamment mettre en oeuvre les règles de gestion.

Création des classes de service métier

Nous allons continuer à travailler dans le projet TodoListService créé sous NetBeans dans la première partie du tutoriel. Pour rappel, ce projet va contenir à terme toute la couche métier de l'application.

Spécification du service

Commençons donc par créer l'interface TodoListService que nous plaçons dans le package info.jtips.todolist.service :

package info.jtips.todolist.service;

import info.jtips.todolist.model.Tache;
import info.jtips.todolist.model.Utilisateur;
import java.util.List;

public interface TodoListService {
   Utilisateur verifierLogin(String id, String pwd);
   void enregistrerUtilisateur(Utilisateur u);
   Tache lireTache(int id);
   List<Tache> lireListeTaches();
   void sauvegarderTache(Tache t);
   void supprimerTache(Tache t);
}


Cette interface représente le contrat du service métier, c'est à dire qu'elle se contente d'énoncer la liste des méthodes qui doivent être disponibles dans un service de ce type. Elle ne dit pas comment les méthodes doivent être mises en oeuvre, car cela est de la responsabilité des classes d'implémentation (voir un peu plus loin).

Par ailleurs, nous ajoutons une classe d'exception personnalisée dans le même package, car les méthodes du service métier seront susceptibles de lever un exception si une règle de gestion échoue :

package info.jtips.todolist.service;

public class TodoListException extends RuntimeException {

   public TodoListException(Throwable cause) {
       super(cause);
   }

   public TodoListException(String message, Throwable cause) {
       super(message, cause);
   }

   public TodoListException(String message) {
       super(message);
   }

   public TodoListException() {
   }

}

Remarque : j'ai choisi d'hériter de la classe RuntimeException, plutôt que de la classe Exception. Ce choix est discutable, mais il apporte de la simplicité au développement pour les raisons suivantes :

Implémentation du service

Ajoutons maintenant la classe d'implémentation de l'interface définie précédemment, à savoir la classe TodoListServiceImpl placée dans le package info.jtips.todolist.service.impl. Cette implémentation s'appuie sur la couche DAO pour interroger et mettre à jour la base de données. Les méthodes du service vont donc utiliser des instances de UtilisateurDao et TacheDao pour effectuer le requêtage SQL.

Comme les DAO sont utilisées dans toutes les méthodes du service, nous les déclarons en champs d'instance (en fait, on peut procéder de cette façon car les DAO sont dites stateless, c'est à dire qu'elles ne possèdent pas d'état propre à un objet client).

Dans le code ci-dessous, examiner les règles de gestion et notamment l'utilisation de l'exception personnalisée TodoListException :

package info.jtips.todolist.service.impl;

import info.jtips.todolist.dao.TacheDao;
import info.jtips.todolist.dao.UtilisateurDao;
import info.jtips.todolist.dao.impl.TacheDaoImpl;
import info.jtips.todolist.dao.impl.UtilisateurDaoImpl;
import info.jtips.todolist.model.Tache;
import info.jtips.todolist.model.Utilisateur;
import info.jtips.todolist.service.TodoListException;
import info.jtips.todolist.service.TodoListService;
import java.util.List;

public class TodoListServiceImpl implements TodoListService {

   private UtilisateurDao utilisateurDao = new UtilisateurDaoImpl();
   private TacheDao tacheDao = new TacheDaoImpl();

   /**
    * Cette méthode permet de vérifier un couple "identifiant/mot de passe"
    * issue d'un écran de login. Si l'utilisateur correspondant à l'identifiant
    * ou si le mot de passe est incorrect, alors une exception est levée
    */
   public Utilisateur verifierLogin(String id, String pwd) {
       Utilisateur u = utilisateurDao.findById(id);
       if (u != null && pwd != null && pwd.equals(u.getMotDePasse())) {
           return u;// connexion OK
       }
       throw new TodoListException("Echec de l'authentification");
   }

   /**
    * Un utilisateur peut être enregistré si son identifiant n'est pas déjà utilisé.
    * Si l'identifiant est déjà utilisé, une exception est levée.
    */
   public void enregistrerUtilisateur(Utilisateur u) {
       // vérification login déjà utilisé
      Utilisateur utilisateurExistant = utilisateurDao.findById(u.getIdentifiant());
      if (utilisateurExistant != null) throw new TodoListException("Identifiant déjà utilisé");

      // ok, on peut enregistrer
      utilisateurDao.create(u);
   }

   /**
    * Dans cette implémentation, l'association "Utilisateur <-> Tache" est
    * initialisée uniquement côté "Tache", ie la propriété "utilisateur".
    * Côté "Utilisateur", la collection de tâches n'est pas initialisée.
    */
   public List<Tache> lireListeTaches() {
       List<Tache> taches = tacheDao.findAll();
       for (Tache t : taches) {
           Utilisateur u = utilisateurDao.findById(t.getUtilisateur().getIdentifiant());
           t.setUtilisateur(u);
       }
       return taches;
   }

   /**
    * Dans cette méthode, l'association "Utilisateur <-> Tache" est complètement
    * initialisée : ie la propriété "utilisateur" côté "Tache" et la propriété
    * "taches" coté "Utilisateur".
    */
   public Tache lireTache(int id) {
       Tache tache = tacheDao.findById(id);
       String idUtilisateur = tache.getUtilisateur().getIdentifiant();
       Utilisateur u = utilisateurDao.findById(idUtilisateur);
       List<Tache> taches = tacheDao.findByUtilisateur(u);
       for (Tache t : taches) {
           u.ajouterTache(t);
           if (t.getId() == id) tache = t;// évite d'avoir deux instances différentes
                                          // pour l'entité recherchée dans le graphe d'objet
       }
       tache.setUtilisateur(u);
       return tache;
   }

   /**
    * Cette méthode consiste à effectuer un INSERT s'il s'agit de sauvegarder
    * une nouvelle tâche (reconnue par id == -1). S'il s'agit d'une tâche
    * existante (reconnue par id != -1), alors un UPDATE est généré.
    */
   public void sauvegarderTache(Tache t) {
       if (t.getId() == -1) {
           tacheDao.create(t);
       }
       else {
           tacheDao.update(t);
       }
   }

   /**
    * Suppression de la tâche passée en paramètre
    */
   public void supprimerTache(Tache t) {
       tacheDao.delete(t);
   }

}


Tests unitaires

De même que pour la couche DAO, il convient de prévoir des tests unitaires afin de vérifier le bon fonctionnement de notre service métier.

La classe de test ci-dessous est similaire à celles que nous avons écrites pour les DAO. Remarquez cependant la méthode de test testEnregistrerUtilisateurExistant, pour laquelle nous paramétrons l'annotation @Test avec expected=TodoListException.class : cela signifie que le test est correct si l'exception TodoListException est levée par la méthode testée.

package info.jtips.todolist.service;

import info.jtips.todolist.model.Tache;
import info.jtips.todolist.model.Utilisateur;
import info.jtips.todolist.service.impl.TodoListServiceImpl;
import java.util.Date;
import java.util.List;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;

public class TodoListServiceTest {

   @Test
   public void testEnregistrerUtilisateur() {
       Utilisateur u = new Utilisateur();
       u.setIdentifiant("bb");
       u.setPrenom("Bugs");
       u.setNom("Bunny");
       u.setMotDePasse("mp");

       TodoListService service = new TodoListServiceImpl();
       service.enregistrerUtilisateur(u);

       System.out.println("Terminé");
   }

   @Test(expected=TodoListException.class)
   public void testEnregistrerUtilisateurExistant() {
       Utilisateur u = new Utilisateur();
       u.setIdentifiant("bb");
       u.setPrenom("Bugs");
       u.setNom("Bunny");
       u.setMotDePasse("mp");

       TodoListService service = new TodoListServiceImpl();
       service.enregistrerUtilisateur(u);

       System.out.println("Terminé");
   }

   @Test
   public void testVerifierLoginOK() {
       TodoListService service = new TodoListServiceImpl();
       Utilisateur u = service.verifierLogin("bb", "mp");

       assertNotNull(u);
       assertEquals("Bugs", u.getPrenom());
       assertEquals("Bunny", u.getNom());
   }

   @Test(expected=TodoListException.class)
   public void testVerifierLoginKO() {
       TodoListService service = new TodoListServiceImpl();
       Utilisateur u = service.verifierLogin("bb", "pas le bon mot de passe");
   }

   @Test
   public void testSauvegarderTache1() {// test INSERT
       Utilisateur u = new Utilisateur();
       u.setIdentifiant("bb");
       u.setPrenom("Bugs");
       u.setNom("Bunny");
       u.setMotDePasse("mp");

       Tache t1 = new Tache();
       t1.setLibelle("Une première tâche");
       t1.setDateFin(new Date());
       t1.setPriorite(1);
       u.ajouterTache(t1);

       Tache t2 = new Tache();
       t2.setLibelle("Une deuxième tâche");
       t2.setDateFin(new Date());
       t2.setPriorite(1);
       u.ajouterTache(t2);

       TodoListService service = new TodoListServiceImpl();
       service.sauvegarderTache(t1);
       service.sauvegarderTache(t2);
   }

   @Test
   public void testSauvegarderTache2() {// test UPDATE
       TodoListService service = new TodoListServiceImpl();
       Tache t = service.lireTache(2);
       t.setLibelle("Tâche modifiée");
       service.sauvegarderTache(t);

   }

   @Test
   public void testLireTache() {
       TodoListService service = new TodoListServiceImpl();
       Tache t = service.lireTache(1);
       assertEquals(1, t.getId());
       assertEquals("Une première tâche", t.getLibelle());
       assertNotNull(t.getUtilisateur());
       assertEquals("bb", t.getUtilisateur().getIdentifiant());
       assertEquals(2, t.getUtilisateur().getTaches().size());

   }

   @Test
   public void testLireListeTaches() {
       TodoListService service = new TodoListServiceImpl();
       List<Tache> taches = service.lireListeTaches();
       assertEquals(2, taches.size());
   }

   @Test
   public void testSupprimerTaches() {
       TodoListService service = new TodoListServiceImpl();
       Tache t = service.lireTache(1);
       service.supprimerTache(t);
       List<Tache> taches = service.lireListeTaches();
       assertEquals(1, taches.size());
   }

}

Conclusion

Notre couche métier est désormais terminée. Elle peut être utilisée depuis une couche de présentation.

Tut2Java1.png

Notez cependant quelques imperfections :

Pour résoudre ces imperfections, plusieurs solutions existent :

Téléchargement du projet

Le projet NetBeans correspondant à la couche métier.


Suite du tutoriel

Bientôt un nouvel article pour la partie présentation avec Swing...