- Cours: M2103 - support ici
- Enseignants: Marin Bougeret, Sébastien Gagné, Jérôme Palaysi, Victor Poupet, Petru Valicov, Bruno Yun
- Le forum Piazza de ce cours pour poser vos questions
- Email pour une question d'ordre privée concernant le cours.
Pendant les TP nous vous conseillons d'utiliser un environnement de développement intégré (IDE en anglais). Les avantages sont multiples : le refactoring du code, l'auto-complétion, la suggestion des méthodes/attributs/classes, la documentation intégrée, le debuggage, une bonne intégration de Git et GitHub, des tests unitaires etc. Dans ce cours nous utiliserons l'IDE IntelliJ IDEA de chez JetBrains, installé sur les machines du département. Il devrait vous faciliter la vie au quotidien dans la réalisation de vos TP. Bien évidement, rien ne vous empêche d'en utiliser un autre (Eclipse, NetBeans etc.).
D'abord, et comme pour tous les tps de POO qui vont suivre, vous allez forker ce dépôt pour pouvoir commencer à travailler localement :
https://classroom.github.com/a/sOEtP2b1
Contrairement au TP1, ne clonez pas tout de suite vous même ce fork, l'IDE s'en chargera tout seul (cf ci-dessous).
IntelliJ IDEA possède deux versions, la première dite 'communautaire' est totalement open source et peut être utilisée gratuitement par n'importe qui et la seconde dite 'ultimate' qui est plus riche en fonctionnalité et qui n'est pas gratuite pour les individus lambda. Les deux versions du logiciels sont présentes sur les machines du département. Par chance, comme dans le cas de GitHub, en tant qu'étudiant, vous avez la possibilité de pouvoir bénéficier d'une licence pour tous les produits JetBrains. Pour bénéficier de cette remise, il vous suffit de remplir ce formulaire.
Quelques minutes après, vous recevrez un email de confirmation suivi d'un second email d'activation ou vous devrez accepter les conditions d'utilisation et choisir un nom d'utilisateur et un mot de passe. Conservez précieusement ces informations dans un coin identifié de votre cerveau car c'est grâce à elle que vous pourrez importer votre licence chez vous et dans les salles de TP.
Premier lancement de l'IDE
Sur les postes Linux du département informatique de l'IUT de Montpellier, IntelliJ IDEA Ultimate est installé dans /opt/idea-IU-183.5153.38/
. Si la version Ultimate (IU) n'est pas disponible, vous pouvez toujours utiliser la version Community installée dans /opt/idea-IC-181.5281.24/
:
~/RepertoireCourant$ cd /opt/idea-IU-183.5153.38/bin
~/opt/idea-IU-183.5153.38/bin$ ./idea.sh
Après le chargement, vous devriez tomber sur une première fenêtre vous proposant d'importer vos paramètres. Vous pouvez laisser sur 'Do not import settings' et valider :
À partir de là vous allez tomber sur la partie de personnalisation de votre installation pour qu'elle corresponde à vos besoins. Sur l'aspect esthétique, choisissez ce qui vous convient le mieux. En ce qui concerne les différents plugins à intégrer, à priori, les options installées par défaut sont suffisantes, donc vous choisirez l'installation par défaut.
À la fin, si vous avez lancé la version IntelliJ IDEA Ultimate, vous allez saisir vos données de connexion pour activer la licence et pour terminer le paramétrage de votre IDE. Pour la version IntelliJ IDEA Community, vous n'avez pas à vous authenthifier et pouvez passer cette étape.
Import du projet dans l'IDE
Pour pouvoir maintenant travailler sur votre TP, il vous faut cloner votre dépôt Git :
Le repository sera celui correspondant à votre TP sur GitHub :
Sur certaines machines, au lancement du projet, il se peut que l'IDE vous demande d'indiquer le SDK à utiliser (grosso modo c'est l'ensemble d'outils logiciels qui permettent de faire tourner des programmes Java). Vous choisirez le SDK 1.8. Lorsque vous ouvrez votre projet Java, la fenêtre d'affichage de votre IDE devrait rassembler à quelque chose comme ceci :
Prenez quelques minutes pour observer l'interface utilisateur. Les éléments principaux :
- à gauche : l'arborescence de votre projet Java
- centre-droite : la fenêtre d'édition ou vous allez taper vos programme
- en bas : le terminal où seront affichés les messages concernant l'exécution de vos programmes
Maintenant que vous savez utiliser Git en ligne de commande, que vous avez forké, importé et ouvert le TP dans votre IDE, vous êtes en capacité de travailler sur vos exercices. Pour cette séance nous vous proposons de suivre un workflow particulier pour résoudre vos exercices progressivement et en consolidant progressivement un filet de sécurité qui vérifiera que votre code continue à bien faire ce qu'il doit. Il s'agit d'un premier apperçu de ce qu'on appelle le Développement Piloté par les Tests (Test-Driven Development ou TDD pour faire court).
Le TDD est une méthode de développement de logiciels qui consiste à travailler sur des cycles de développement très courts. La conception apparaît au fur et à mesure du développement en commençant à écrire les tests avant le code applicatif. Ceci favorise une meilleure compréhension du problème. Pour le développeur, les tests vont constituer une spécification technique exécutable et vérifiable à tout moment. Ainsi en rajoutant des tests, le développeur converge progressivement à la fois vers une spécification plus fine et un code fonctionnel associé.
Ci-dessous le déroulement d’un cycle :
- Écrire un premier test automatique qui est censé définir une nouvelle fonctionnalité. Le test doit s’écrire avant la fonctionnalité voulue, il est donc censé échouer initialement (normal, car vous n'avez encore rien programmé).
- Écrire le code minimal suffisant pour passer le test (principe BabySteps).
- Vérifier que le test passe.
- Réfactoriser (simplifier, améliorer, optimiser etc.) tous le code tout en gardant la fonctionnalité. Durant cette étape les tests écrits préalablement sont exécutés en permanence pour certifier la validité des changements.
Ce workflow se décrit par le triptyque "RED, GREEN, REFACTOR" dans le schéma suivant :
Le workflow que vous allez devoir suivre va reprendre la même structure, mis à part qu'au début, les tests vous seront donnés pour vous faire découvrir cette méthode sans trop de douleur.
Un test unitaire est une méthode écrite par le programmeur afin de vérifier qu'une fonctionnalité précise et élémentaire fonctionne correctement. Le terme unitaire insiste sur le côté "élémentaire" de la fonctionnalité (unité) à tester.
L'outil le plus répandu pour gérer les tests unitaires en Java est le framework JUnit.
Les tests sont écrits dans une classe Java comme vous avez l'habitude.
La différence entre le fonctions "normales" et les fonctions de test est l'annotation @Test
.
Celle-ci doit préceder chaque test unitaire. Exemple :
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
class MaPetiteClasseDeTests {
@Test
void monJoliPremierTest() {
assertEquals(3, 1 + 2); // On vérifie que 1 + 2 vaut bien 3
}
}
Pour exécuter les tests, la façon la plus simple est d'utiliser votre IDE : ouvrir la classe contenant le test et utiliser le bouton 'Play' exécuter les tests contenues dans cette classe. Le résultat de l’exécution s'affiche en bas de la fenêtre de l'IDE :
- les tests ayant réussi sont affiché en vert
- les test ayant échoué sont en rouge
Votre IDE vous affiche également la cause de l'échec en vous indiquant le résultat attendu de la fonctionnalitée testée et le résultat effectif.
Pour bien séparer le code de ses tests, nous allons utiliser la convention Maven dans l'ensemble des TPs de POO. Ainsi le code est organisé de la façon suivante :
- l'intégralité des sources du projet se trouve dans le répertoire
src/
- le code source et fichiers source principaux se trouvent dans
src/main
- tous les fichiers de tests sont dans dans
src/test
- le code source (ou code applicatif) se trouve dans
src/main/java
- le code source de test se trouve dans
src/test
Maven est un system de build et administration de projets Java. Pour faire une analogie vous pouvez le comparer
à l'outil bien connu make avec lequel vous serez bercés tout au long de vos études.
Utiliser Maven permet de gérer facilement toutes les dépendances du projet (comme les librairies de tests unitaire par exemple).
Il est également utile pour créer le fichier .jar
du projet : une archive contenant l'ensemble de classes Java et de ressources
d'un projet informatique (comme .AppImage
sous Linux ou .exe
sous Windows).
Comme indiqué précédemment, dans ce TP les tests unitaires vous seront données dans le repertoire de test correspondant.
Vous allez les activer un par un en commentant l'annotation @Disabled
.
La règle principale à retenir : avant de faire un commit tous les tests qui ne sont pas annotés avec @Disabled
doivent
passer (pas de messages en rouge dans la console de l'IDE).
Le kata Fizz Buzz est un des katas les plus connus pour l'apprentissage du TDD.
Une série 'FizzBuzz' de taille n
est une suite d'entiers positifs où lorsqu'un multiple de 3 est rencontré on imprime "Fizz" et lorsqu'un multiple de 5 est renctontré, on imprime "Buzz". Voici la série FizzBuzz de taille 20 :
1 ; 2 ; Fizz ; 4 ; Buzz ; Fizz ; 7 ; 8 ; Fizz ; Buzz ; 11 ; Fizz ; 13 ; 14 ; FizzBuzz ; 16 ; 17 ; Fizz ; 19 ; Buzz
Implémentez la classe FizzBuzz
qui vous est donnée dans le paquetage fr.umontpellier.iut.exercice1
. Cette classe contient deux fonctions qu'il faudra compléter :
getValue(int i)
qui retournera une chaîne de caractères correspondant au nombrei
dans la série FizzBuzzcomputeList(int n)
qui retournera toute la série 'FizzBuzz' jusqu'à la valeur passée en paramètre
La classe contenant les tests est situéé dans le répertoire correspondant au paquetage fr.umontpellier.iut.exercice1
dans l'arborescence src/test
de votre projet.
Vous activerez les tests les un après les autres et soumettrez (avec un commit) votre solution après chaque itération du cycle principal du workflow.
La classe principale (contenant le main
) est la classe App
, et se trouve dans le paquetage fr.umontpellier.iut.exercice1
. Implémentez là (supprimez la ligne qui lève une exception) pour simuler le bon fonctionnement de votre programme.
Même si l'exercice paraît facile, prêtez une attention particulière à l'étape de réfactorisation. Vous vous servirez de votre IDE pour renommer les méthodes ou variables, pour l'autocomplétion etc.
D'après le Théorème fondamental de l'arithmétique, tout entier strictement positif peut être écrit comme un produit de nombres premiers. Dans cet exercice il vous est demandé d'implémenter la méthode qui, pour un entier strictement positif donné, renvoie la liste de ces facteurs premiers en ordre croissant. Par exemple pour l'entier 5, la liste retournée devrait être [5], alors que pour l'entier 12, la liste retournée devrait être [2,2,3].
La méthode que vous devez implémenter est computeFactors(int)
. Elle est située dans la classe PrimeFactors
(paquetage fr.umontpellier.iut.exercice2
) qui renvoie la liste de facteurs premiers pour l'entier passé en paramètre.
Comme pour l'exercice précédent, vous activerez les tests les un après les autres. N'oubliez pas de refactoriser (i.e. simplifier/nettoyer/factoriser) le code à chaque étape. Vous soumettrez avec Git votre solution après chaque itération du cycle principal du workflow.
À la fin de l'exercice, pour vous convaincre de la validité de votre code, vous ajouterez un test supplémentaire pour un nouveau nombre que vous aurez choisi aléatoirement. Est-ce que votre programme passe ce nouveau test ?
Vous êtes chargé de réaliser une calculette simplifiée qui effectue des additions et des soustractions des nombres écrits en numérotation romaine. Dans cette numérotation on utilise des caractères parmi sept lettres de l'alphabet latin : I
, X
, L
, C
, D
et M
. La signification en numérotation décimale classique est donnée ci-dessous :
I | V | X | L | C | D | M |
---|---|---|---|---|---|---|
1 | 5 | 10 | 50 | 100 | 500 | 1000 |
Un nombre romain se lit de gauche à droite en faisant des additions et des soustractions des valeurs des chiffres. Tout symbole qui suit un symbole de valeur supérieure ou égale s’ajoute à celui-ci (exemple : 6 s'écrit VI
). Tout symbole qui précède un symbole de valeur supérieure se soustrait à ce dernier (exemple : 40 s'écrit XL
). Par exemple le nombre romain MLXIII
correspond à 1063 dans la numérotation décimale car il se décompose comme M
+L
+X
+I
+I
+I
= 1000+50+10+1+1+1. Alors que le nombre XXXIV
vaut 34 car il se décompose comme X
+X
+X
+IV
=10+10+10+4. Une meilleure façon de voir ce dernier exemple c'est d'utiliser la soustraction X
+X
+X
-I
+V
=10+10+10-1+5.
On va se fixer une représentation unique des nombres romains avec les principes suivants :
- Un même symbole n'est pas employé quatre fois de suite (sauf
M
) ; - Les soustractions s'effectuent sur un seul symbole (par exemple
XL
est correct et vaut 40, mais il est interdit d'écrireXXL̀
pour 30, et on écrira plutôtXXX̀
). - On écrira en repectant l'ordre suivant
- d'abord le chiffre des milliers (à l'aide uniquement de
M
) - puis le chiffre des centaines (à l'aide uniquement de
C
,D
,M
) - puis le chiffre des dizaines (à l'aide uniquement de
X
,L
,C
) - puis le chiffre des unités (à l'aide uniquement de
I
,V
,X
)
- d'abord le chiffre des milliers (à l'aide uniquement de
- Pour chacune des 4 étapes ci-dessus, on utilisera le moins de symboles possible
Par exemple :
IL
(pour 49) est interdit (I n'est pas autorisé pour décrire les dizaines), et 49 =XLIX
XCM
est interdit (car que l'on interprete commeX
CM
ouXC
M
, cela ne respecte pas l'ordre ci-dessus)VX
(pour 5) est interdit, carV
utilise moins de symbolesXCXX
(pour 110) est interdit, car il faut décrire le chiffre des centaines avecC
,D
,M
.
Faites très attention pour cet exercice de bien respecter le principe du TDD en ajoutant vraiment tout le temps la quantité minimale de code nécessaire à la validation des tests. Si vous suivez cette règle, il se résout très facilement alors qu'en l'abordant de manière générale, il comporte de nombreux pièges pouvant vous faire perdre un temps précieux.
Dans cet exercice, vous allez manipuler la classe String
. Cette classe possède de nombreuses méthodes utilitaires pour manipuler facilement les chaines de caractères.
Convertisseur de nombres romains
Écrivez la classe Java RomanToNumeral
donnée. Cette classe aura une méthode getNumeral()
qui prend un nombre romain en paramètre et retourne sa valeur en numérotation décimale (un type int
).
N'oubliez pas de faire des "commits" au fur et à mesure !
Convertisseur de nombres décimaux
Écrivez la classe NumeralToRoman
qui contiendra une méthode getRoman()
, qui prend un nombre entier en paramètre et retourne sa valeur en numérotation romaine (de type String
donc).
Est-ce que vos tests sont suffisants ? Que se passe-t-il lors de la conversion romain
-> décimal
-> romain
?
Additionneur romain
En utilisant les deux classes écrites précédemment, créez une classe RomanAdditionner
qui contiendra une méthode compute()
. Cette méthode prendra en paramètre une chaîne de caractères représentant une expression arithmétique romaine telle que :
- les opérandes sont écrits en numérotation romaine
- les opérations possibles sont
+
et-
Voici un exemple d'expression arithmétique : MMMXL
+ XII
- CIX
.
Le résultat correspondant devrait être : MMCMXLIII
.
Vous trouverez d'autres katas qui vous permettront de mieux apprendre la programmation en mode TDD :
Dans tous les cas gardez cette citation d'Edsger W. Dijkstra en tête :
Program testing can be used to show the presence of bugs, but never to show their absence.
Naturellement vous pouvez faire le tout en ligne de commande.
Supposons que vous êtes sur Linux (la démarche étant quasiment identique sur Windows ou MacOS) et que la racine de votre projet est le répertoire ~/POO/TP2
.
Positionnez-vous dans ce répertoire :
~/RepertoireCourant$ cd ~/POO/TP2
~/POO/TP2$ ls
~/POO/TP2$ pom.xml README.md src target tp2.iml
Pour compiler le code source de l'exercice 1 à partir de la racine de votre projet :
~/POO/TP2$ javac -d target/ src/main/java/fr/umontpellier/iut/exercice1/*.java
L'option -d target
permet d'indiquer que la destination des fichiers compilés est le répertoire target/
Également, avec l'option -sourcepath
, il est possible d'indiquer le chemin où le compilateur va chercher
l'ensemble du code source à compiler :
~/POO/TP2$ javac -sourcepath src/ -d target src/main/java/fr/umontpellier/iut/exercice1/*.java
Pour exécuter le programme compilé :
~/POO/TP2$ cd target/
~/POO/TP2/target$ java fr.umontpellier.iut.exercice1.App
L'avantage d'utiliser IntelliJ IDEA est qu'à priori vous n'avez pas à installer les divers dépendances, l'outil est "self-contained".
En revanche, si vous passez par la console, il se peut que vous soyez amenés à installer ou importer divers outils.
Notamment, pour exécuter les tests unitaires dans un terminal, il faudrait utiliser un exécutable .jar
correspondant
à la platforme JUnit et contenant l'ensemble de dépendances nécessaires :
~/POO/TP2$ java -jar junit-platform-console-standalone-<version>.jar <Options>
La solution ci-dessus peut s'avérer particulièrement pénible lorsque vous avez plusieurs classes de tests, dans différents packages. Le plus simple c'est d'utiliser un outil de "build" comme Maven, Gradle, Ant, make etc. Par exemple, si Maven est installé sur votre machine vous pouvez exécuter les tests en ligne de commande assez facilement. Pour cela placez-vous à la racine de votre projet et tapez la commande suivante :
~/POO/TP2$ mvn test
Le résultat obtenu devrait rassembler à quelque chose comme ceci :