V. 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 connait 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 des 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 connaitra 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é 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é par plusieurs applications ?
En fonction des réponses, ce n'est pas très difficile de s'orienter vers un choix entre les deux 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.
VI. 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 quelle 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.
VI-A. Le core▲
On va maintenant implémenter le Core de l'application.
VI-A-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'application, 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() :
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 :
- Chargement des modules ;
- Chargement des services ;
- Utilisation d'un service pour logguer quelque chose ;
- Démarrage des modules ;
- Affichage de la vue de l'application.
On n'y fait rien de bien compliqué.
Méthode exit() :
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 :
- Extinction des modules ;
- Fermeture de la vue ;
- Logge d'un message de fermeture.
Et voici maintenant le code complet avec les getters/setters, les champs et la javadoc. Ça se passe de commentaire :
VI-A-2. Le chargement des modules▲
On va maintenant s'occuper du chargement des modules. On reprend ici le code proposé au chapitre 4. dans une seule et même classe :
La seule différence est qu'on parcourt le dossier « modules » du dossier de l'application. Pour plus d'explications, référez-vous au chapitre 4 où ce code est expliqué.
VI-A-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 :
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 :
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 :
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.
VI-B. 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 ajouter deux 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 :
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à .
VI-C. 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.
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 arguments 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.
VI-D. 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-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-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 :
Comme nous le voyons, notre module a bien affiché ses deux lignes dans la vue et ses deux messages dans la console. Notre test est concluant. Nous avons créé une application modulaire !