TP 9-10 — Exceptions et génériques
Exceptions personnalisées, fichiers récursifs et Scanner — puis l'ordonnanceur de tâches générique en quatre phases, de la file simple à l'équité : la synthèse finale du semestre.
Objectif des TP : le TP 9 fait des exceptions personnalisées et des fichiers un réflexe ; le TP 10, le plus long du semestre, construit un ordonnanceur de tâches générique en quatre phases — la synthèse de tout le cours, et le sujet type du contrôle TP.
TP 9 — Login et exceptions personnalisées
LogInManagement boucle sur la saisie (login, mot de passe) jusqu'au couple correct,
défini par les constantes LOGIN/PWD. La consigne clé : getUserPwd() ne doit
contenir ni try ni catch — les exceptions sont propagées (throws) et les
captures ont lieu dans la méthode principale. On crée d'abord WrongLoginException et
WrongPwdException en dérivant Exception, avec un message spécifique à l'affichage ;
puis la méthode passe à un retour void en lançant explicitement le bon type ; enfin
WrongInputLengthException (saisie de plus de 10 caractères) ajoute un troisième type.
TP 9 — Extraction, récursion, Scanner
DicoJava.txt: chaque ligne contient trois champs séparés par des tabulations — un mot, son type, une description.BufferedReader.readLine()puis découpage par tabulation, sélection des lignes dont le type vaut « Mot-clé », construction d'une liste ordonnée, écriture deMotsJava.txtà raison d'un mot par ligne.printExecutable(File): lister, à toute profondeur, le chemin absolu des fichiers exécutables et non cachés. La méthode est récursive : sur un répertoire (isDirectory), on itère surlistFiles(); sur un fichier, on testecanExecute()et!isHidden(). Le générateur d'arborescenceHierarchyCreation(jar fourni, méthodehierarchyCreation(String aPath)) fabrique le jeu d'essai attendu — 4 fichiers à afficher, les cachés exclus.Validation.checkFile(String): valider un CSV typé — premier champ = type de données (1 pour les booléens, 2 pour les entiers, sinon des chaînes), deuxième = nombre de valeurs de la ligne, suivies des données. La méthode renvoietrueou lève :InputMismatchException(mauvais type lu),InvalidStructureException(à définir soi-même),FileNotFoundException. Toutes les captures ont lieu dans lemain.
TP 10 — Phase 1 : la file simple
Les objets d'abord : l'interface IPriority (getPriority) et la classe Task qui
l'implémente — priorité fixée à la création, toString basé sur getClass() qui
affiche Task:t1(3). Puis l'interface générique IScheduler<T> : addElement,
highestPriority (sélectionne et retire la tâche à traiter), isEmpty, size.
L'implémentation SchedulingQueue<T> est une file premier entré, premier sorti appuyée
sur une LinkedList<T> — choisie parce que les opérations majoritaires sont l'ajout
et la suppression — avec un ID automatique et l'affichage Queue0 -> []. Deux scénarios
(Book, puis Task) et la classe de tests SchedulingQueueTest fournie.
TP 10 — Phase 2 : le type générique borné
On ne veut plus accepter n'importe quoi dans la file :
PrioritySchedulingQueue<T extends IPriority> dérive de SchedulingQueue avec une
borne sur le paramètre de type — le compilateur garantit que tout élément sait donner
sa priorité.
L'implémentation reste minimaliste : trier la file à chaque ajout (rappel : priorité
faible = urgente), sans redéfinir autre chose. Scénario à écrire : trois tâches de
priorité décroissante sortent dans l'ordre inverse de leur ajout. Classe de tests
PrioritySchedulingQueueTest fournie.
TP 10 — Phase 3 : plusieurs files de priorité
Le tri systématique après chaque ajout ne passe pas à l'échelle. PriorityScheduler
gère une liste de files, dont le nombre est fixé à la création (l'aspect générique
est ici laissé de côté). Une tâche de priorité supérieure au nombre de files part dans
la dernière file — priorité 5 dans un ordonnanceur à 3 files : file numéro 2. À
écrire : size(int priority), l'affichage PriorityQueue: suivi des files, et le
scénario t1(2), t2(3), t3(1), t4(4), t5(2) sur 5 files, en vérifiant l'ordre de
sortie. Classe de tests PrioritySchedulerTest fournie.
TP 10 — Phase 4 : les jobs et l'équité
L'interface IJob étend IPriority (héritage entre interfaces) avec execute() et
isOver() — un processeur peut re-remettre une tâche non terminée dans l'ordonnanceur.
Puis une famille de travaux, tous dérivés de Task :
JobDisplay: un message, une seule exécution, priorité par défaut 3 — l'affichageDISPLAY:<msg>n'ajoute la priorité que si elle diffère du défaut ;JobLoop: s'exécute un nombre donné de fois, priorité par défaut 2, affichageLOOP:<msg>:<NB-EXECUTION>;JobIOdérive deJobDisplay: première exécution = saisie du message au clavier, seconde = affichage du message saisi ; priorité par défaut 1 ;JobDynamicdérive deJobLoop: priorité évolutive — elle diminue d'exécution en exécution si le travail est urgent, augmente sinon, la modification précédant l'affichage du message.
La vérification se fait par rejeu de fichiers de traces, format
IN|OUT:TYPE-JOB:<PARAM>:PRIORITY (séparateur :, priorité en dernier champ si
différente du défaut). Dans SchedulerChecking, checkSimpleLog() rejoue
schedulerDisplayJobOnly.log — instancier un IJob approprié à chaque ligne IN,
comparer le sortant à la ligne OUT, lever une exception au moindre écart — puis
checkLog() rejoue scheduler.log avec les trois types de jobs. Enfin FairScheduler
garantit l'équité : un tableau priorities[i] compte les invocations de
highestPriority() sans exécution d'un processus de priorité i — c'est la tâche qui a
le plus attendu qui sort, à égalité la plus prioritaire.
Le TP à ne pas commencer la veille
Interfaces, génériques bornés, dérivation d'interfaces, fichiers, exceptions : tout y passe, dans un ordre imposé. Chaque phase se valide avec sa classe de tests — ne passez à la suivante que lorsque la précédente est verte.
Parce que la méthode de saisie ne sait pas quoi faire d'un échec : afficher, réessayer,
quitter — c'est l'affaire de l'appelant. La propagation (throws) déplace la
responsabilité vers le main, qui centralise les messages : c'est le principe même
des exceptions.
Parce que seule la politique d'insertion change : la file, l'ID et les accesseurs
sont hérités. La borne T extends IPriority suffit à garantir que le tri à l'ajout
peut invoquer getPriority() sur chaque élément.
L'énoncé le justifie par les opérations dominantes : ajouter et retirer en tête.
LinkedList met à jour un chaînage en O(1) là où ArrayList devrait décaler tous
les éléments.
Il interdit la famine : une tâche de faible priorité ne peut plus attendre indéfiniment, puisque chaque passage sans exécution incrémente son compteur d'attente. La sélection devient « le plus long temps d'attente d'abord », la priorité ne départageant que les égalités.
tpOO_09.pdf
R2.01 · TP 9 — Exceptions et fichiers · 3 pages
Chargement du document…
tpOO_10.pdf
R2.01 · TP 10 — Ordonnanceur générique · 6 pages
Chargement du document…
Dernière mise à jour le