:: Enseignements :: ESIPE :: E3INFO :: 2018-2019 :: Programmation Objet avec Java ::
[LOGO]

Examen INFO1 - Programmation Objet Java


Cette épreuve est constituée d'une partie de questions de cours (pour un tiers des points), auxquelles vous répondrez de manière concise (maximum 4 ou 5 lignes par question) dans un fichier question_cours.txt, et d'une partie de programmation (les exercices suivants pour environs deux tiers des points) pour laquelle vous devrez écrire des classes Java. Ces deux parties sont indépendantes, mais les exercices de programmation se suivent.
Tout ce que vous devez rendre doit être situé dans le répertoire EXAM qui existe à la racine de votre compte; tout ce qui n'est pas dans ce répertoire à la fin de l'épreuve sera perdu (vous aurez 0).
Il vous est conseillé d'utiliser Eclipse, et de créer un nouveau projet Java en décochant la case "Use default location" et en spécifiant plutôt ce répertoire EXAM; ainsi, toutes vos sources et classes seront récupérées.

Questions de cours (6,5 points)

Exercice 1 - Constructeur (2 points)

  • Quel intérêt présente la notion de "constructeur" en Java (par opposition à une méthode d'inititalisation de structure comme on peut en écrire en C par exemple)?
  • Pourquoi dit-on, lorsque plusieurs constructeurs surchargés existent au sein d'une même classe, qu'il faut si possible qu'il en existe un "plus général" auquel les autres font appel?
  • A quoi peut servir une classe dont tous les constructeurs sont private? Quels problèmes cela pose-t-il?

Exercice 2 - Mais à quoi ça sert? (1 point)

Donnez un exemple (pas trop compliqué) dans lequel le polymorphisme est utile/pratique?

Exercice 3 - Comment choisir? (2 points)

Pouvez vous expliquer brièvement dans quel cas on utilise l'héritage entre classes et dans quel cas on utilise plutôt l'implémentation d'interface? Comment utilise-t-on les classes abstraites le plus souvent? Donnez quelques arguments pour étayer vos réponses avec quelques avantages et inconvénients.

Exercice 4 - Listes (1,5 points)

  1. En fonction des usages que vous souhaitez faire d'une liste, comment choisissez vous entre une LinkedList et une ArrayList ? Expliquez vos critères de choix.
  2. Quel est le problème du code de la méthode print ci-dessous, qui doit afficher chaque élément de la liste, un par ligne?
    void print(List<String> list) {
        for(int i=0; i<list.size(); i++) {
            System.out.println(list.get(i));
        }   
    }
    
    Par quel code faudrait-il le remplacer?

Programmation (13,5 points)

Vous vous attacherez à toujours mettre les champs avec des modificateurs d'accessibilité les plus restreints possibles, et à factoriser le code au maximum, de sorte d'éviter autant que possible toute duplication de code. Vous écrirez toutes les classes dans un package nommé fr.upem.postal. Vous devez tester toutes les méthodes demandées et vous écrirez tous vos tests dans la classe Test de ce package.

Exercice 1 - Adresse postale (2 points)

Nous considérons qu'une adresse postale, pouvant être utilisée comme destinataire d'un envoi postal, est représentée par la classe Address avec les quatre champs suivants, tous de type String, et tous fixés une fois pour toute lors de la création de l'objet à une valeur différente de null (ils ne peuvent pas changer au cours de la vie de l'adresse) :
  • name
  • detail
  • zip
  • country

  1. Créez la classe Address et ajoutez-y juste ce qu'il faut pour que le code suivant (à mettre dans la méthode main d'une classe Test) fonctionne et affiche comme indiqué dans les commentaires:
            Address dudu = new Address("Duris", "Copernic", "77454", "FRANCE");
            System.out.println(dudu); // Duris Copernic 77454 FRANCE
            Address bobo = new Address("Borie", "Copernic", "77454");
            System.out.println(bobo); // Borie Copernic 77454 FRANCE   
    
    L'affichage doit indiquer chacun des champs dans l'ordre séparés par un espace. Lorsque le constructeur ne spécifie pas le pays, comme dans le cas de la variable bobo ci-dessus, l'objet sera créé avec la chaîne "FRANCE" en guise de champ country.

  2. Ajouter une méthode getCountry() permettant de connaître la valeur du champs country(). Testez avec
     
            System.out.println(dudu.getCountry()); // FRANCE
            System.out.println(bobo.getCountry()); // FRANCE
    

Exercice 2 - Envois postaux (5 points)

On souhaite pouvoir gérer différents types d'envois postaux (de type Delivery), sachant que pour l'instant seuls deux cas concrets nous intéressent (d'autres cas concrets pourront être ajoutés à l'avenir):
  • d'une part les colis (type Parcel) qui sont définis par une adresse de destinataire (recipient) de type Address différente de null, un poids (weight) positif en grammes de type int et un volume (volume) positif en cm3 de type int,
  • d'autre part les lettres (type Letter) qui sont définies par une adresse de destinataire (recipient) de type Address différente de null et un poids (weight) positif en grammes de type int.
Ces deux types d'envois postaux peuvent être éventuellement caractérisés comme "urgent" à leur création, mais par défaut ils ne le sont pas.
Trois méthodes doivent être disponibles sur les objets de type Delivery:
  • getRecipient() qui retourne le destinataire;
  • isUrgent() qui retourne true si l'envoi est urgent et false sinon;
  • price() qui retourne le prix d'affranchissement de cet envoi (un entier en centimes d'euros);
Le prix d'affranchissement d'une lettre est de 70 cents jusqu'à 20g; au delà, il est de 1 euro plus un dixième du poids en gramme (par exemple 1,34€ pour une lettre de 348 grammes). Si la lettre est urgente, c'est 1€ de plus.
Le prix d'affranchissement en centimes d'euros d'un colis est la somme de son poids et de son volume. Si le colis est urgent, le prix est le double.

Proposez une implémentation en Java des différents types ci-dessus (vous pouvez en créer d'autres) qui vous semble respecter les critères de la programmation objet et de la factorisation de code, de sorte que le code suivant, à coller à la suite de votre méthode main de la classe Test, se comporte comme indiqué dans les commentaires:
        Delivery p4D = new Parcel(dudu, 200, 1000);
        System.out.println(p4D.price()); // 1200
        System.out.println(p4D.getRecipient()); // Duris Copernic 77454 FRANCE
        System.out.println(p4D.isUrgent()); // false
        Delivery p4DU = new Parcel(dudu, 200, 1000, true);
        System.out.println(p4DU.price()); // 2400
        
        Delivery l4B = new Letter(bobo, 40);
        System.out.println(l4B.price()); // 104
        Delivery l4BU = new Letter(bobo, 40, true);
        System.out.println(l4BU.getRecipient()); // Borie Copernic 77454 FRANCE
        System.out.println(l4BU.isUrgent()); // true
        System.out.println(l4BU.price()); // 204

Ajoutez les méthodes nécessaires qui permettent d'afficher les caractéristiques de chaque type d'envoi postal comme illustré en commentaire dans le code ci-dessous; respectez le format indiqué, c'est à dire "[URGENT] " si c'est urgent, puis "To: " suivi du destinataire, puis "Parcel" avec le poids et le volume ou "Letter" et le poids, selon le cas:
        System.out.println(p4D);
        // To: Duris Copernic 77454 FRANCE: Parcel 200g 1000cm3
        System.out.println(p4DU);
        // [URGENT] To: Duris Copernic 77454 FRANCE: Parcel 200g 1000cm3
        System.out.println(l4B);
        // To: Borie Copernic 77454 FRANCE: Letter 40g
        System.out.println(l4BU);
        // [URGENT] To: Borie Copernic 77454 FRANCE: Letter 40g

Exercice 3 - Bureau de poste (6,5 points)

La classe PostOffice représente un bureau de poste auprès duquel les clients enregistrent des envois postaux avec la méthode register(Delivery delivery) qui retourne le prix d'affranchissement, en centimes d'euros, à payer par le client. Le bureau de poste stocke chaque envoi postal ainsi enregistré dans une liste.

  1. Écrire la classe PostOffice et sa méthode int register(Delivery delivery)), ainsi qu'une méthode affichant le contenu de la liste, comme dans le code ci-dessous (vous n'avez pas besoin de gérer les retours à la ligne comme dans l'exemple: ils ont été mis dans le commentaire pour des raisons de mise en page).
            PostOffice po = new PostOffice();
            po.register(p4D);
            po.register(p4DU);
            po.register(l4B);
            po.register(l4BU);
            System.out.println(po);
            // [To: Duris Copernic 77454 FRANCE: Parcel 200g 1000cm3, 
            //  [URGENT] To: Duris Copernic 77454 FRANCE: Parcel 200g 1000cm3, 
            //  To: Borie Copernic 77454 FRANCE: Letter 40g, 
            //  [URGENT] To: Borie Copernic 77454 FRANCE: Letter 40g]
    
  2. Pour vérifier sa caisse en cours de journée, le postier aimerait disposer d'une méthode int totalPrice() qui retourne la somme des prix des envois postaux qui ont été enregistrés. Implémentez cette méthode avec une solution performante en terme de complexité au cas où le postier vérifierait fréquemment sa caisse.


  3. On veut maintenant une méthode List<Delivery> deliveriesForRecipient(Address recipient) dans la classe PostOffice, qui retourne la liste des envois postaux enregistrés dans ce bureau qui sont à destination de l'adresse passée en argument. Testez avec le code suivant:
            System.out.println(po.deliveriesForRecipient(new Address("Borie", "Copernic", "77454")));
            // [To: Borie Copernic 77454 FRANCE: Letter 40g, 
            //  [URGENT] To: Borie Copernic 77454 FRANCE: Letter 40g]
    

  4. On voudrait pouvoir utiliser le type PostOffice dans une boucle à la manière suivante:
            for(Delivery d : po) {
                if(d.isUrgent()) {
                    System.out.println("Urgent delivery for " + d.getRecipient());
                }
            }
    
    Faites le nécessaire pour que ce code fonctionne.

  5. On veut enfin doter la classe PostOffice d'une méthode void sortDeliveries(Comparator<? super Delivery> c) qui, à la manière de la méthode sort() sur les listes, accepte un comparateur pour trier les envois postaux du bureau de poste en fonction de critères spécifiques.
    Écrire cette méthode et testez-la depuis le main en lui passant une lambda expression qui demande un tri par ordre croissant des prix:
    po.sortDeliveries(/* a vous d'écrire la lambda qui compare sur les prix */);
    // la liste des envois postaux doit ensuite être triée du moins cher au plus cher
    
    Ensuite, faites en sorte que si le comparateur passé en argument vaut null alors la méthode sortDeliveries trie la liste des envois postaux selon l'ordre naturel des colis, qui repose sur l'ordre naturel des destinataires de type Address, à savoir, l'ordre lexicographique des pays (country), puis des code postaux (zip), puis du détail de l'adresse et enfin du nom. A destinataire égal, on classera en premier les envois urgents.
    po.sortDeliveries(null);
    // la liste des envois postaux doit être triée par destinataire (Borie avant Duris), 
    // puis les urgents avant les non urgents