Développer une application modulaire en Java


précédentsommairesuivant

5. Séparation de l'application

Une autre chose qu'il est intéressant de considérer quand on pense à une application modulaire est l'application en elle-même. Doit elle être intégrée au core ? Doit elle être complètement indépendante de l'application qui l'emploie.

L'une et l'autre de ses façons de voir ont leurs avantages et inconvénients :

Le core est lié à l'application : Ce type de core est fait pour une application bien spécifique. C'est-à-dire qu'il n'est destiné à être utilisé que par cette application et des modules pour cette application. Si on prend un exemple tout bête, cela voudra dire que le core connaît le nom de l'application et c'est lui qui a les informations sur les crédits de l'application. C'est peut être simpliste comme exemple, mais imaginez que vous développiez une nouvelle application et que vous vous basiez sur un Core de ce genre et que vous ne pouvez pas changer le nom de l'application ni l'auteur dans les crédits, ce n'est pas de plus pratiques. ce type de core peut être une application modulaire de type 1 ou 2.

Le core est complètement indépendant de l'application : Ce précédent type de core est entièrement générique et peut être utilisé par n'importe quelle application. Si on reprend l'exemple d'avant, cette fois-ci, le core ne connaîtra pas le nom de l'application, c'est cette dernière qui doit configurer le core pour qu'il affiche son nom. Donc vous n'êtes plus embêtés si vous voulez l'utiliser avec votre application alors qu'il a été créé pour une autre. Ce core est obligatoirement de type 2. En effet, cela n'aurait pas de sens d'y mettre des fonctionnalités si on veut pouvoir utiliser ce core avec n'importe quelle application. Imaginez une application de jeux pour enfants qui contiendrait une calculatrice scientifique en anglais de base ainsi que les cours de la bourse. Pas vraiment pratique, non ?

Ceci est donc encore un point qu'il faut définir avant de concevoir son application modulaire. Voici quelques questions qu'il faut se poser :

  • Est-ce que mon core devra contenir des fonctionnalités ou non ?
  • Est-ce que mon core est mon application ?
  • Est-ce que mon core pourra être utilisée par plusieurs applications ?

En fonction des réponses, ce n'est pas très difficile de s'orienter vers un choix entre les 2 types de Core. Il faut quand même noter qu'il n'est pas très lourd de séparer la partie application de la partie core, même après coup.

6. Exemple complet

Maintenant que nous avons vu tous les concepts de base, ainsi que les points spécifiques de l'implémentation. Nous allons développer une petite application modulaire exemple qui va utiliser tous les concepts présentés dans cet article.

On va créer une application du type 2, c'est-à-dire sans aucune fonctionnalité de base, elle ne sert à rien sans module. De plus, on va créer un core qui puisse être utilisé par n'importe quel application.

Cette application sera des plus simples, une simple vue avec une zone de texte dans laquelle les modules pourront ajouter des lignes.

On va ajouter un service permettant de logguer des messages. Et un autre service fournira une méthode pour ajouter du texte dans la vue, ce sera notre point d'extension.

6.1. Le core

On va maintenant implémenter le Core de l'application.

6.1.1. La classe Core

La classe Core sera responsable de charger les modules et de démarrer les différents services, ainsi que de la gestion du cycle de vie de l'application. Comme on va partir sur un Core complètement indépendant de l'applicatioin, il possèdera également un attribut applicationName et un attribut applicationFolder.

De plus, le Core aura donc une méthode launch() et une méthode exit().

Méthode launch() :

launch()
Sélectionnez
public void launch(){
    modules = ModuleLoader.loadModules();
 
    LogManager.getInstance().init();
    ViewManager.getInstance().init();
 
    LogManager.getInstance().log("Chargement des modules");
 
    for(IModule module : modules){
        module.plug();
 
        LogManager.getInstance().log(module.getName() + " OK. ");
    }
 
    ViewManager.getInstance().display();
}

Voilà ce qu'on y fait :

  1. Chargement des modules
  2. Chargement des services
  3. Utilisation d'un service pour logguer quelque chose
  4. Démarrage des modules
  5. Affichage de la vue de l'application

On n'y fait rien de bien compliqué.

Méthode exit() :

exit()
Sélectionnez
public void exit(){
    for(IModule module : modules){
        module.unplug();
    }
 
    ViewManager.getInstance().close();
 
    LogManager.getInstance().log("Fermeture de l'application");
}

Encore plus simple. Voilà ce qu'on y fait :

  1. Extinction des modules
  2. Fermeture de la vue
  3. Logge d'un message de fermeture

Et voici maintenant le code complet avec les getters/setters, les champs et la javadoc. Ca se passe de commentaire :

Core.java
CacherSélectionnez

6.1.2. Le chargement des modules

On va maintenant s'occupper du chargement des modules. On reprend ici le code proposé au chapitre 4. dans une seule et même classe :

ModuleLoader.java
CacherSélectionnez

La seule différence est qu'on parcours le dossier "modules" du dossier de l'application. Pour plus d'explications, référez-vous au chapitre 4. ou ce code est expliqué.

6.1.3. Les extensions et services

On va maintenant créer nos "services". Le premier fournit aux modules la possibilité de logguer des messages. Dans la pratique, on va utiliser un framework de logging évolué comme log4j, mais pour notre article, on va simplement l'afficher en console avec une indication devant le message.

Voici donc ce que donnerait notre classe :

LogManager.java
CacherSélectionnez

Comme vous pouvez le voir, il n'y a rien de compliqué dans cette classe. J'ai ajouté une méthode init() pour l'exemple, car en pratique, vous aurez presque toujours quelque chose à faire pour charger le service.

On va passer maintenant, au service de vue. Ce service va simplement proposer d'afficher la vue, de la fermer et d'y ajouter du texte. Voici ce qu'il va donner :

ViewManager.java
CacherSélectionnez
package org.dvp.core;
 
/**
 * Manager permettant de gérer la vue de l'application.
 *
 * @author Baptiste Wicht
 */
public class ViewManager {
    private static final ViewManager instance = new ViewManager();
 
    private CoreView view;
 
    /**
     * Retourne l'instance unique de ViewManager.
     *
     * @return L'instance unique de ViewManager.
     */
    public static ViewManager getInstance() {
        return instance;
    }
 
    /**
     * Initialise le service.
     */
    public void init() {
        view = new CoreView();
    }
 
    /**
     * Affiche la vue.
     */
    public void display() {
        view.setVisible(true);
    }
 
    /**
     * Ferme la vue.
     */
    public void close() {
        view.dispose();
    }
 
    /**
     * Affiche du texte sur la vue.
     *
     * @param text Le texte à afficher.
     */
    public void displayText(String text) {
        view.addText(text);
    }
}

Là encore, rien de spécial à redire. On va maintenant définir la vue. On va utiliser une JFrame contenant un JTextArea pour afficher le texte. Voici ce que ça pourrait donner :

CoreView.java
CacherSélectionnez
package org.dvp.core;
 
import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
 
/**
 * Vue principale de l'application. 
 * 
 * @author Baptiste Wicht
 */
public class CoreView extends JFrame {
    private final JTextArea textArea;
 
    /**
     * Construit la vue. 
     */
    public CoreView(){
        super();
 
        setSize(640, 480);
        setTitle(Core.getInstance().getApplicationName());
        setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        addWindowListener(new WindowCloseListener());
 
        textArea = new JTextArea("Texte des modules : \n");
 
        add(new JScrollPane(textArea), BorderLayout.CENTER);
 
        setLocationRelativeTo(null);
    }
 
    /**
     * Ajoute un texte dans la vue. 
     * 
     * @param message Le message à ajouter. 
     */
    public void addText(String message){
        textArea.append('\t' + message + '\n');
    }
 
    /**
     * Le listener pour fermer l'application complète lorsqu'on ferme la fenêtre. 
     * 
     * @author Baptiste Wicht
     */
    private static class WindowCloseListener extends WindowAdapter {
        @Override
        public void windowClosing(WindowEvent e) {
            Core.getInstance().exit();
        }
    }
}

Ce qu'on peut noter, c'est qu'on récupère le nom de l'application pour le titre de la fenêtre. Il faut aussi avertir le core de la fermeture de l'application. Dans notre cas, ce n'est pas des plus utiles étant donné qu'il n'y a aucune ressource à libérer. Mais en pratique, on aura toujours quelque chose à libérer dans une application.

6.2. Un module d'exemple

Maintenant que notre Core est terminé, on va créer un petit module qui va utiliser notre Core.

Ce module sera tout simple, il va juste rajouter 2 lignes de texte dans la vue et utiliser la fonction de log du service LogManager pour afficher quelque chose en console.

Voici donc notre module :

 
CacherSélectionnez
package org.dvp.module;
 
import org.dvp.core.IModule;
import org.dvp.core.LogManager;
import org.dvp.core.ViewManager;
 
/**
 * Module d'exemple tout simple. 
 * 
 * @author Baptiste Wicht
 */
public class SimpleModule implements IModule {
    @Override
    public void plug() {
        LogManager.getInstance().log("I'm in !");
 
        ViewManager.getInstance().displayText("Je suis pluggé. ");
        ViewManager.getInstance().displayText("J'affiche du texte. ");
    }
 
    @Override
    public void unplug() {
        LogManager.getInstance().log("I'm out !");
    }
 
    @Override
    public String getName() {
        return "Module super simple";
    }
}

Rien de spécial non plus à ce niveau là.

6.3. L'application

Maintenant qu'on a un core et un module, on va créer l'application permettant de configurer le core et de le lancer.

ApplicationModulaire.java
CacherSélectionnez
package org.dvp.application;
 
import org.dvp.core.Core;
 
/**
 * L'application de notre projet modulaire.
 *
 * @author Baptiste Wicht
 */
public class ApplicationModulaire {
    /**
     * Lance le core de l'application.
     *
     * @param args Les argument de ligne de commandes. Ignorés. 
     */
    public static void main(String[] args) {
        Core.getInstance().setApplicationFolder(System.getProperty("user.dir"));
        Core.getInstance().setApplicationName("Application modulaire 1.0. ");
        Core.getInstance().launch();
    }
}

On donne donc le dossier courant et le nom de l'application au core et on le démarre.

6.4. Résultat

On va donc lancer maintenant notre application.

Pour cela, il va falloir déployer nos différentes parties sous forme de fichier .jar.

Le core : On va exporter notre core dans le fichier Core.jar à la racine du dossier de l'application. Rien de spécial à mettre dans le manifest pour cette partie.

Le module : On va exporter notre module dans le fichier SimpleModule.jar dans le sous-dossier modules du dossier de l'application. Il faut spécifier le nom de la classe de module dans le manifest :

MANIFEST.MF
Sélectionnez
Manifest-Version: 1.0
Module-Class: org.dvp.module.SimpleModule

L'application : On va exporter notre application dans le fichier Application.jar dans le dossier de l'application. Ici, il faut spécifier la classe main dans le fichier manifest et il faut ajouter Core.jar dans le classPath :

MANIFEST.MF
Sélectionnez
Manifest-Version: 1.0
Main-Class: org.dvp.application.ApplicationModulaire
Class-Path: Core.jar

Maintenant que nos différents fichiers sont créés, on va lancer notre programme. Pour cela, on peut soit lancer Application.jar comme une application normale, soit utiliser la commande java -jar pour bénéficier de la console.

Avec cette deuxième option voici le résultat :

Vue de l'application
Vue de l'application
Affichage console
Affichage console

Comme nous le voyons, notre module a bien affiché ses 2 lignes dans la vue et ses 2 messages dans la console. Notre test est concluant. Nous avons créé une application modulaire !


précédentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  










Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2009 Baptiste Wicht. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.