TP 3 — Qualité de code + branches Git
PMD sur le code du R2.01, branches Git avec conflit volontaire et résolution — puis une refactorisation guidée vers une classe abstraite Student, et le planning des soutenances.
Objectif du TP : évaluer votre code avec un analyseur statique, manipuler branches et conflits Git hors de tout projet, corriger une erreur de conception par une classe abstraite — et finir sur un planning de soutenances validé par des tests fournis.
1. PMD sur le code du R2.01
Récupérez PMD (site officiel, archive à décompresser) et le fichier de règles
pmd-rules_r203.xml sur Moodle, puis analysez votre paquetage tp03 de R2.01 :
pmd check -d <répertoire-source du tp03> -R pmd-rules_r203.xml -f html > rapport.htmlQ3. Analysez le rapport HTML, corrigez toutes les erreurs signalées — des règles
du type OnlyOneReturn, TooManyFields — puis repassez PMD sur le même paquetage pour
valider vos corrections. Q4 — intégrez PMD à votre IDE pour l'utiliser de manière
récurrente.
Ce que l'exercice montre
Le rapport décrit votre dette technique accumulée en R2.01, en noir et blanc. La corriger maintenant coûte peu ; la corriger dans deux semestres coûtera beaucoup plus — les intérêts de la dette.
2. Branches et conflit volontaire
Scénario dans un répertoire git-branches, dépôt initialisé, gitk --all ouvert en tâche
de fond (F5 après chaque commande).
La base. fileA contenant « azerty », commité en « fileA submission ».
Diverger. Branche develop ; sur elle, fileB « azerty again ». Retour sur
master ; là, fileC « always azerty ». Les branches ont divergé.
Première fusion. Sur master, merge de develop — « first merging » : commit de
fusion à deux parents, un fast-forward étant impossible.
Fabriquer le conflit. Sur master, ajoutez « new line added » à fileB (commit
« fileB updated master »). Sur develop, écrivez « conflicting line added » au
même fichier (commit « fileB updated develop »). Comparez les deux versions de
fileB avec git diff.
Fusionner, résoudre, nettoyer. Le merge échoue : même partie du même fichier
modifiée des deux côtés. git mergetool --tool-help liste les outils d'aide à la
fusion ; lisez les marqueurs, combinez les deux versions, committez — puis supprimez
la branche (git branch -d develop).
Le conflit est mécanique, pas une panne
Git fusionne tout seul les fichiers différents et les parties distinctes d'un même fichier. Le TP fabrique exprès une collision : même fichier, même partie, deux modifications. Les marqueurs rendent les deux prétendants visibles — vous tranchez.
3. Person, Student1, Student2 — et l'erreur de conception
Des soutenances à planifier, des étudiants de types différents aux durées propres — et
chaque soutenance évaluée par un « candide », d'où une classe Person en base de la
modélisation. Écrivez Person (name, forename), accesseurs et toString sous la
forme <forename> <name>. Puis :
Student1: constanteDEFAULT_DURATION_STUDENT1à 20 minutes, booléenthirdTimeCredit(faux par défaut), deux constructeurs,getDefaultDuration(),getDuration()— la durée à laquelle l'étudiant a droit selon son tiers-temps — ettoString()sous la forme<forename> <name> (<duration>);Student2: même structure et mêmes signatures, durée par défaut à 30 minutes.
Dans UseStudent, affichez « Alice a », « Bruno b » (BUT1) et « Clément c » (BUT2).
Affichage attendu :
a Alice
b Bruno (20)
c Clement (30)Q6 — le diagnostic. Le code fonctionne, mais une erreur de conception s'est
glissée : par héritage, une relation de polymorphisme lie Student1 et Student2 — un
étudiant de 2e année serait un étudiant de 1re année. Indésirable ici.
4. La correction : une classe abstraite
Q6.1 — dupliquez vos classes en StudentBUT1/StudentBUT2 (trace pédagogique).
Q6.2 — puis refactorez : une classe abstraite Student portant le commun, deux
dérivées qui ne se distinguent que par leur durée par défaut.
public abstract class Student extends Person {
private boolean thirdTimeCredit;
public Student(String name, String forename) { this(name, forename, false); }
public Student(String name, String forename, boolean thirdTimeCredit) { /* ... */ }
public abstract int getDefaultDuration();
public int getDuration() { /* dépend du tiers-temps */ }
@Override public String toString() { /* <forename> <name> (<duration>) */ }
}StudentBUT1 et StudentBUT2 fournissent chacune leur constante
(DEFAULT_DURATION_BUT1, DEFAULT_DURATION_BUT2) et leur getDefaultDuration().
Q7 — second scénario dans UseStudent, mêmes tâches, même affichage : le polymorphisme
est désormais voulu, non subi.
5. Room, Defense, Planning
Q8 — l'enum Room (r0A18, r0A20, r0A51, r0A49) doit s'afficher « room 0A18 »
et non r0A18 : redéfinissez toString. Q9 à Q11 — la classe Defense porte
whenDay (LocalDate), whenHour (LocalTime), where (Room), who (Student),
title (String), avec un constructeur complet et un constructeur sans date ni heure ;
son toString respecte scrupuleusement la forme
<jour>:<heure> in <lieu> --> <étudiant> :: <titre>, nécessaire à la validation des
tests. Ajoutez tous les accesseurs, les mutateurs setDay/setHour, et une méthode
déléguée pour la durée de la soutenance. Q12 — récupérez DefenseTest.java,
intégrez-le au répertoire test, validez.
Q13 à Q17 — le Planning. Un jour (when) et une liste de soutenances ;
defaultStart à 8:00, pauses breakDuration de 5 minutes. addDefense(...)
ajoute une soutenance ou une liste d'un coup ; getDefense(idx) retourne la soutenance
d'indice donné, null sinon ; getNbDefense() compte. scheduling(...) re-planifie : à
chacune sa date et son heure de début, à partir de l'heure fournie ou par défaut, en
ajoutant à chaque fois la durée de l'étudiant puis la pause — c'est getDuration(),
invoquée via le polymorphisme, qui décide. Q18 et Q19 — swapDefense(d1, d2) permute
deux soutenances ; delayDefense(d1, minutesToAdd) retarde toutes les soutenances à
partir de d1 incluse — décomposez bien votre code. Q20 — récupérez
PlanningTest.java et validez. Q21 — commit : « tpQU-03::exo-planning », puis
poussez.
L'héritage crée une relation « est un » : ici, un étudiant de 2e année serait un
étudiant de 1re année. Faux au sens métier, dangereux au sens polymorphisme —
n'importe quel Student1 pourrait recevoir un Student2. La classe abstraite met les
deux types au même niveau, sous un ancêtre commun.
Parce que Student porte du code commun — booléen thirdTimeCredit,
constructeurs, getDuration(). Une interface ne stocke ni état ni constructeurs.
Il part de defaultStart (8:00), affecte ce début à la première soutenance, ajoute la
durée de son étudiant puis la pause de 5 minutes, et ainsi de suite. Les durées
viennent de getDuration() invoquée sur chaque Student : BUT1 et BUT2 mélangés,
chacune rend la sienne par polymorphisme.
tpQU_03.pdf
R2.03 · TP 3 — Qualité de code + branches Git · 4 pages
Chargement du document…
Dernière mise à jour le
TP 2 — Classe, documentation et tests
Javadoc complète et tests JUnit 5 fournis sur WarriorCard — puis Event chaîné déléguant à une ToDoList, et Agenda anti-chevauchement dont les tests sont à écrire soi-même.
TP 4 — Manipulation des exceptions
Diagnostics de six fragments fautifs, division blindée jusqu'au calcul réussi, conteneurs à transvaser automatiquement — et le complément « presque-biathlon » avec trois exceptions personnalisées.