TP3 POO: Création de classes

Exercice 1 : La classe Student

Le but de l’exercice est de reproduire ce que nous avions fait au TP précédent avec les listes d’étudiants, mais cette fois en utilisant une structure de classe.

Création du package et de la classe

  1. Créez un nouveau projet Java intitulé “BaseEtudiants”

  2. Dans ce projet, créez les classes (avec les bons packages)

     fr.upsaclay.bibs.students.Student
     fr.upsaclay.bibs.BaseEtudiants

    La classe BaseEtudiants contiendra la fonction main

  3. Dans la classe Student, créez deux champs firstName et lastName pour stocker le prénom et le nom de l’étudiant. Les champs doivent être publics et finaux.

  4. Créez un constructeur qui prend en paramètre un prénom et un nom et initialise les champs de telle sorte à ce que

    • les espaces de début et de fin soient supprimés (méthode strip des objets de type String)
    • le nom de famille lastName soit enregistré en majuscule
  5. Créez une fonction main dans la classe BaseEtudiants et créez un objet de type étudiant. Vérifiez que vous arrivez bien à exécuter

Premiers tests

  1. Créez un package

     fr.upsaclay.bibs.students.test

    Dans ce package, créez une classe StudentTest et ajoutez les import suivant

    import static org.junit.jupiter.api.Assertions.*;
    import org.junit.jupiter.api.Test;
    import fr.upsaclay.bibs.students.Student;

    Dans Intellij, cliquez sur le mot souligné en rouge dans l’import : corrigez l’erreur en ajoutant JUnit 5 à votre “classpath” (attention ! Il faut ajouter JUnit5 et non pas JUnit4 qui est parfois proposé par défaut)

  2. Ajoutez la méthode testCreation suivante

       @Test
       void testCreation() {
           assertEquals(new Student("Ada","Lovelace").firstName, "Ada");
       }

Puis lancez l’exécution de l’unité de test 8. Complétez votre méthode et votre classe avec le contenu présent ici. Décommentez les tests de la méthode testCreation et vérifiez que cela passe bien.

Equals, toString et hashCode

  1. On considère (sans doute à tort) que deux étudiants sont “égaux” s’ils ont les mêmes noms et prénoms. Ajoutez et complétez les 3 méthodes suivantes. La méhode toString doit renvoyer le nom (en majuscule) suivi du prénom séparé par un espace.
    @Override
    public String toString() {

    }
    
    @Override
    public boolean equals(Object obj) {

    }
    
    @Override
    public int hashCode() {

    }
  1. Vérfier la méthode toString en faisant un affichage dans la fonction main
  2. Décommentez les tests correspondants aux 3 méthodes et lancez les.

Test validName

  1. Implantez la méthode suivante dans la classe Student. Puis décommentez les tests correspondants dans StudentTest et lancez-les.
    /**
     * Checks that a given String is a valid name
     * ie it contains only: letters, spaces, or "-" 
     * @param name a String representing a name 
     * @return true if it's a valid name, false otherwise
     */
    public static boolean validName(String name)
  1. Dans le constructeur de la classe Student, rajoutez un test pour la validité du prénom et du nom. En cas de nom invalide, vous léverez une exception avec la ligne suivante :

    throw new IllegalArgumentException("Not a valid name");

    Décommentez et lancez les tests de la méthode testExceptionCreation dans StudentTest

Nouveau champ : getter et setter

  1. Créez un nouveau champ de type String: email avec visibilité private
  2. Créez deux méthodes public void setEmail(String email) et public String getEmail() qui permette de donner une valeur / récupérer la valeur du champ email.

Il est très courant d’utiliser la visibilité private pour des champs et d’en contrôler l’accès par des méthodes appelées “getter” et “setter”. D’ailleurs, une fois qu’on a créé un champ, on peut générer automatiquement ces méthodes avec intellij (bouton droit sur le nom de la classe puis “Generate”).

  1. Ajoutez un constructeur public Student(String firstName, String lastName, String email) qui fait appel au constructeur principal pour le prénom / nom puis initialise l’email.
  2. Décommentez et lancez les tests de la méthode testEmail dans StudentTest
  3. On peut tester la validité d’un email grâce à une “expression régulière” en utilisant la classe java.util.regex.Pattern. Ajoutez la méthode suivante à votre classe Student (il faut aussi importer la classe Pattern). (C’est une vérification très basique qui cherche simplement le caractère “@”)
    /**
     * Checks that a given String is a valid email using regular expression
     * @param email a String representing an email 
     * @return true if it's a valid email, false otherwise
     */
    public static boolean validEmail(String email) {
        String regexPattern = "^(.+)@(\\S+)$";
        return Pattern.compile(regexPattern)
          .matcher(email)
          .matches();
    }
  1. Utilisez cette méthode pour vérifier la validité d’un email avant de mettre à jour le champ. Si l’email est invalid, on lèvera une exception :

    throw new IllegalArgumentException("Not a valid email");

Décommentez et lancez les tests des méthodes testValidEmail et testExceptionEmail

Implémenter Comparable

  1. Changez la déclaration de la classe Student pour qu’elle implémente l’interface Comparable :

    public class Student implements Comparable<Student>
  2. Intellij affiche maintenant une erreur : il faut ajouter la méthode compareTo(Student st) : implémentez la méthode en utilisant la méthode compareTo des chaînes de caractères.

  3. Décommentez et lancez les tests de la méthode testCompareTo

Une variable statique

Comme nous avons codé l’égalité avec la comparaison des noms, il se peut que nous ayons des instances avec des informations différentes (l’email) mais considérées comme égales. On voudrait savoir laquelle est la plus récente. Pour cela, on va rajouter un champ instanceId : chaque instance créée reçoit un nouvel id. Plus l’id est grand, plus l’instance est récente.

  1. Créer deux champs dans la classe Student

    private int instanceId;
    private static int count = 0;
  2. A chaque création, augmenter la variable statique count et utiliser la valeur courante comme id du nouvel objet. Le champ count est une variable statique qui compte le nombre d’instances crées.

  3. Ajoutez une méthode getInstanceId puis décommentez et lancez les tests de la méthode testInstanceId.

Exercice 2 : la classe Grade

  1. Dans le package fr.upsaclay.bibs.students, créez une nouvelle interface Gradable ainsi qu’une classe Grade et copiez les codes de l’interface ici et de la classe ici
  2. Dans le package fr.upsaclay.bibs.students.test créez une nouvelle classe GradeTest et copiez le code présent ici
  3. La classe Grade représente une note que l’on peut calculer sur la valeur souhaitée et qui peut aussi stocker un coefficient

Complétez toutes les méthodes de la classe Grade de telle sorte que les tests définis dans GradeTest passent tous.

Code dans les interfaces

Au départ, le rôle d’une interface n’est pas d’implémenter du code (ça c’est le rôle des classes). Deux exceptions cependant :

Exercice 3 : La classe GradeList

  1. Dans le package fr.upsaclay.bibs.etudiants, créez une nouvelle classe GradeList et copiez le code présent ici. Dans le package fr.upsaclay.bibs.etudiants.test, créez une classe GradeListTest et copiez le code présent ici et vérifiez que les tests passent

Classes génériques

Cette classe sert à stocker une liste de notes et est elle-même une implantation de Gradable et de Iterable. Surtout, c’est elle-même une classe générique : elle stocke une liste d’éléments de type “E” où E doit être une implantation de Gradable. Cela vous donne un exemple d’implantation générique mais on ne vous demande pas de maîtriser ce système à ce stade.

  1. La plupart des méthodes de GradeList sont déjà implantées (regardez par exemple les constructeurs et le champ privé pour stocker la liste). Il ne reste que la méthode gradeOver à implanter : cette méthode renvoie la moyenne des notes stockées. Ne vous laissez pas impressionner par le type générique E, vous avez un exemple juste au dessus de parcours de la liste avec la méthode max. Implantez la méthode gradeOver de telle sorte que les tests à décommenter passent.

  2. Complétez les tests de la méthode testSetGetCoefficient dans GradeListTest

  3. On va maintenant éditer la classe Student pour qu’on puisse stocker pour chaque étudiant une liste de note. Ajoutez un champ privé final dans la classe Student :

    private final GradeList<Gradable> gradeList = new GradeList<Gradable>();
  4. Ajoutez une méthode dans la classe Student pour récupérer la liste de note.

    public GradeList<Gradable> getGradeList() {
        return gradeList;
    }

    Vérifiez que les tests de testGetGradeList fonctionnent.

  5. Modifiez votre déclaration de classe Student pour qu’elle implémente l’interface Gradable en plus de Comparable (on écrit les différentes interfaces implémentées en séparant par des virgules). La seule méthode à implanter est gradeOver qui doit renvoyer la note calculée par gradeList (c’est-à-dire la moyenne de l’étudiant). Vérifiez que les tests de testDefaultCoefficient et testGradeOver fonctionnent.

Polymorphisme

La classe Student est maintenant une implantation de Gradable : cela signifie que tout ce qui peut s’appliquer aux objets Gradable peut s’appliquer aux étudiants. En particulier, on peut créer une GradeList avec une liste d’étudiants ! C’est ce qu’on va faire juste après pour calculer la moyenne d’un groupe d’étudiants.

En fonction du type déclaré Student ou Gradable on aura accès à plus ou moins de fonctionnalités : c’est le polymorphisme.

Exercice 4 : une fonction main

  1. Récupérez le nouveau fichier grades.txt présent ici. Chaque ligne suit le format suivant

    NOM, Prénom, note, note, note

On considère que la première note a coefficient 2 tandis que les 2 autres notes ont chacune coefficient 1.

Écrivez une fonction main qui crée à partir du fichier une liste d’étudiants de type GradeList<Student> : chaque étudiant contiendra à son tour la liste de notes qui lui ait associée avec les bons coefficients.

  1. Affichez :

    • la liste des étudiants avec leur moyenne
    • la moyenne générale de la classe (la moyenne des moyennes des étudiants) (Vous devez trouver 15.079)
    • le nom et la note de l’étudiant-e ayant eu la meilleure moyenne (Vous devez trouver TSOGBEKAN Emeline avec la note 19.5)