Organisation du code

Les sources d'un projet doivent être suivies par un logiciel de contrôle de versions tel que git, ce qui est plus facile si on les isole. La façon recommandée est de placer tous les fichiers .java dans un répertoire src.

Les fichiers générés (typiquement, les fichiers .class) qui font partie des délivrables sont également isolés pour faciliter les tests et le conditionnement final. On les place en général dans un répertoire build.

En ce qui concerne les fichiers délivrables mais non générés, par exemple les images, on les considère comme des ressources, et le plus simple est de les rassembler dans un répertoire res.

Plus rarement, on génère des fichiers non délivrables, en particulier si on utilise un outil créant du code source comme Bison. Ces fichiers seront placés dans un répertoire tmp.

Pour tenir compte de cette organisation au moment de la compilation et de l'exécution, il sera nécessaire d'ajouter des options sur la ligne de commande :

bob@box:projet$ javac -d build -classpath build -sourcepath "src:tmp" ...
bob@box:projet$ java -classpath build ...

En Java, on considère qu'une classe proprement écrite doit appartenir à un package, ce que nous n'avons pas pratiqué jusqu'ici. Pour déclarer qu'une classe appartient à un package, il est nécessaire de placer une instruction au début de son fichier :

package fr.iutfbleau.projet;

Le nom du package peut être divisé en plusieurs parties séparées par des points. On va du descripteur le plus général à gauche, jusqu'au descripteur le plus spécifique à droite.

Il est recommandé de s'inspirer du nom de domaine du propriétaire du code, en l'inversant et en retirant les caractères fâcheux. Dans le cas d'un grand groupe, un ou plusieurs descripteurs supplémentaires peuvent être ajoutés à la suite. On inclut après un descripteur spécifique au projet, et si nécessaire des descripteurs pour différentes parties du code.

Chaque descripteur doit correspondre à un répertoire, de sorte que la déclaration de package serve de chemin pour retrouver le fichier source. À la compilation, le fichier bytecode sera également placé dans un chemin similaire, mais en partant de la destination de la compilation (les répertoires manquants seront créés).

Ceci complique un peu les commandes de compilation et d'exécution :

bob@box:projet$ javac ... src/fr/iutfbleau/projet/Main.java
bob@box:projet$ java ... fr.iutfbleau.projet.Main ...

Vu qu'un projet va rapidement accumuler de très nombreux fichiers source, il est judicieux d'avoir recours à de la compilation séparée. Afin de ne pas alourdir les phases de test, il est préférable d'automatiser cette compilation, par make ou un outil similaire.

Pour éviter que le compilateur ne tente de traiter plus d'un fichier source à la fois, il faut désactiver la compilation en cascade (qui ne marche pas correctement de toute façon).

bob@box:projet$ javac -implicit:none ...

Attention En Java, il n'y a pas besoin de faire l'édition de lien. Par contre, Vous devez quand même fournir une règle de dépendance pour chaque fichier bytecode à générer.

Lorsque l'on veut déployer le projet chez le client, avoir une multitude de fichiers peut être source de lourdeurs. À défaut d'utiliser un installeur/rustineur, vous pouvez inclure les fichiers bytecode et les fichiers ressources dans une archive. Le JDK vous procure même un protocole adapté et l'outil qui va avec : jar, une variante de la commande tar.

Une archive jar n'est rien d'autre qu'une archive zip contenant les packages du projet, les ressources associées et un manifeste qui décrit le contenu de l'archive et ses propriétés. En particulier, pour les archives exécutables, le manifeste contient le point d'entrée, c'est dire le nom de la classe qui possède le main.

bob@box:projet$ jar cvfe projet.jar fr.iutfbleau.projet.Main -C build fr -C res images

Comme pour tar, la lettre c veut dire create et indique donc que l'on désire créer une archive. La lettre v pour verbose signifie que la commande doit afficher une description du travail accompli. La lettre f pour file implique que le prochain argument donne le nom de l'archive. Et la lettre e (entry point), qui est nouvelle, veut dire que l'argument d'après sera le point d'entrée à mettre dans le manifeste.

Remarque Notez l'usage de l'option -C pour déplacer le répertoire courant, uniquement pour les arguments suivants. Cela permet de choisir soigneusement quelle partie du chemin sera dupliquée à l'intérieur de l'archive.

L'archive ainsi créée peut être envoyée au client telle quelle. Pour l'exécuter (si elle est exécutable), on a quand même besoin de recourir à la JVM :

bob@box:projet$ java -jar projet.jar

Si votre code est destiné à tourner depuis une archive jar, vous devez prendre des précautions particulières pour accéder aux ressources qui y seront incluses. Dans la classe ClassLoader, vous trouverez la méthode getResource qui vous permettra d'obtenir l'URL d'un fichier, ainsi que la méthode getResourceAsStream qui vous donnera accès au fichier sous la forme d'un flux d'octets en entrée. Dans les deux cas, indiquez le chemin de votre cible en prenant l'archive comme origine. Pour obtenir l'instance avec laquelle invoquer ces méthodes, utilisez le code suivant :

ClassLoader loader = Thread.currentThread().getContextClassLoader();

  1. Galerie. Reprenez le premier exercice du sujet sur les transitions (n’importe quelle version) et générez une archive exécutable. Attention aux images…

  2. Quiz. Écrivez une application qui propose un QCM à l’utilisateur. Un QCM est une série de questions. Une question est composée du texte de la question, du texte de chaque réponse proposée (entre 2 et 5), et de l’identification de la bonne réponse.

    L’utilisateur devra répondre à 10 questions. Chaque question aura son propre écran, et l’utilisateur pourra naviguer de l’un à l’autre sans contrainte. Une fois qu’il a sélectionné une réponse pour chaque question, il peut valider ses choix. On lui donne alors le pourcentage de bonnes réponses, et si il revient sur les écrans précédents, il peut comparer ses réponses avec la solution attendue (mais ne peut plus les changer).

    Pour simplifier, on choisit de générer les questions aléatoirement. Elles seront toutes de la forme « AxB=? », où A et B seront des chiffres. Ce choix ne doit pas influer sur le code de vos classes (à part le générateur).

    Pour le modèle, vous définirez :

    • une classe qui représente une question
    • une classe qui représente les choix de l’utilisateur
    • une classe qui représente la source des questions

    Pour la vue, vous définirez :

    • une classe qui affiche une question, soit en mode choix, soit en mode solution
    • une classe qui demande la validation des choix, puis affiche le résultat
    • une classe qui représente la fenêtre entière du quiz

    Pour le contrôleur, vous définirez :

    • une classe qui gère le démarrage
    • une classe qui gère la navigation entre les écrans (suivant/précédent)
    • une classe qui gère la validation des choix

    Pour cet exercice vous devrez respecter toutes les consignes indiquées plus haut : répertoires du projet, packages, Makefile, archivage.

retour à la page d'accueil

retour au sommet