:: Enseignements :: ESIPE :: E3INFO :: 2021-2022 :: Programmation Objet avec Java ::
[LOGO]

Examen de Java - Session


Le but de ce TP noté est d'écrire quelques classes permettant la gestion de propriétés.

À lire absolument

Tout ce que vous devez rendre devra obligatoirement être placé dans le répertoire EXAM à la racine de votre compte ; sinon, ce n'est pas récupéré et vous aurez 0.

Tout document papier est proscrit.
La javadoc 17 est https://igm.univ-mlv.fr/~juge/javadoc-17/api/index.html.
Les seuls documents électroniques autorisés sont les supports de cours à l'url http://igm.univ-mlv.fr/~forax/ens/java-avance/cours/pdf/.

Vous vous attacherez à toujours mettre les champs avec des modificateurs d'accessibilité les plus restreints possibles, à éviter la création d'objet faux en utilisant judicieusement les pré-conditions, à factoriser le code au maximum, de sorte à éviter autant que possible toute duplication de code et à ne pas ajouter de méthodes qui ne sont pas nécessaires par rapport à ce qui est demandé dans le sujet.

Vous écrirez toutes les classes de ce TP noté dans un package nommé fr.uge.estate. Vous devez tester toutes les méthodes demandées et vous écrirez tous vos tests dans la classe fr.uge.estate.main.Main d'un sous-package.
Attention, il est très important de respecter les noms des paquetages, des classes, des champs et des méthodes que l'on vous demande : une partie de la correction est automatique.

Exercice 1 - Gestion de Propriétés

Une propriété (Estate) est composée d'un ensemble de terrains constructibles Land qui ont une taille (parcel) et sur lesquels peuvent être construit une habitation (building). Il y a 3 sortes de buildings, NoBuilding qui indique qu'il n'y a pas encore de building, House qui indique qu'il y a une maison et Hotel qui indique qu'il y a un hôtel. House et Hotel possèdent chacune un propriétaire (owner) et une surface en m2 (surface).

  1. On va, dans un premier temps, définir un terrain Land avec une taille parcel qui sera un double en m2. Bien sûr, la taille du terrain ne peut être ni négative, ni nulle. Puis définir un Estate qui regroupe des terrains. Ces terrains seront ajoutés avec la méthode addLand. L'affichage d'un Estate correspond aux différentes tailles des terrains dans l'ordre d'insertion, séparées par des '|'. Enfin, un Estate possède une méthode allParcels qui renvoie la somme de toutes les parcels des différents terrains.
    Écrire Land et Estate afin que le code suivant fonctionne et affiche les bonnes valeurs.
          var land1 = new Land(500);
          var land2 = new Land(750);
    
          var estate = new Estate();
          estate.addLand(land1);
          estate.addLand(land2);
          System.out.println(estate);  // 500.0 m2 | 750.0 m2
          System.out.println(estate.allParcels());  // 1250.0
        

    Note : vous devez utiliser des Stream pour implanter l'affichage et la méthode allParcels. Si vous n'y arrivez pas, faites autrement, mais vous n'aurez pas tous les points de la question.
  2. Faites en sorte qu'il ne soit pas possible d'ajouter deux fois le même terrain.
          var estate2 = new Estate();
          estate2.addLand(land1);
          estate2.addLand(land1);  // java.lang.IllegalStateException: this land was already added
        
    Attention : le test pour savoir si un terrain est déjà présent doit se faire en temps constant (en O(1)) au pire cas.
  3. Vérifier que votre implantation n'est pas trop stricte et permet d'ajouter deux terrains différents même s'ils ont la même taille.
           var estate3 = new Estate();
           estate3.addLand(new Land(300));
           estate3.addLand(new Land(300));  // Ok !
        
  4. On souhaite ajouter une méthode listLand qui prend une lambda en paramètre et qui est appelée avec la taille (parcel) de chaque terrain et renvoie une liste de terrains (List<Land>) pour lesquels la lambda renvoie vrai.
    Par exemple, dans le code ci-dessous, le premier terrain a une taille de 500 qui est inférieure à 700, il va donc être dans la liste renvoyée alors que le second terrain qui a une taille de 1 500 ne va pas être dans la liste renvoyée.
          var estate4 = new Estate();
          estate4.addLand(new Land(500));
          estate4.addLand(new Land(1_500));
          System.out.println(estate4.listLand(parcel -> parcel <= 700));  // [500.0 m2]
        
    Note : la liste renvoyée doit être non-modifiable.
  5. On souhaite ajouter une méthode parcelIsLessThan pour que le code soit un peu plus lisible dans le cas où l'on cherche les terrains dont la taille est inférieure à une valeur.
          var estate5 = new Estate();
          estate5.addLand(new Land(850));
          estate5.addLand(new Land(1_500));
          System.out.println(estate5.listLand(Land.parcelIsLessThan(2_000)));  // [850.0 m2, 1500.0 m2]
        
    Écrire la méthode parcelIsLessThan telle que le code ci-dessus fonctionne.
  6. On souhaite modifier Land pour ajouter une propriété building avec deux méthodes toutes les deux nommées building, la première ne prend pas de paramètre et renvoie le building associé au terrain, la seconde prend un building et l'ajoute comme building pour le terrain.
    Définir une maison House que l'on peut utiliser comme building. Un House possède un propriétaire owner de type String ainsi qu'une surface surface en m2 de type double. De plus, on souhaite utiliser un objet NoBuilding comme valeur si il n'y a pas de building sur un Land.
    Faites en sorte que le code ci-dessous fonctionne :
          var land3 = new Land(800);
          var building = land3.building();
          System.out.println(building);  // NoBuilding[]
    
          var land4 = new Land(800);
          land4.building(new House("John", 100));
          System.out.println(land4.building());  // House[owner=John, surface=100.0]
        
  7. Par rapport à l'implantation précédente, on souhaite ajouter deux contraintes. Premièrement, il n'est pas possible d'ajouter une maison sur un terrain si la surface de la maison est plus grande que la taille du terrain.
          var land5 = new Land(800);
          land5.building(new House("Jane", 1_000));  // java.lang.IllegalStateException: building too big
        
    Deuxièmement, il n'est pas possible d'ajouter une maison sur un terrain qui possède déjà une maison.
          var land6 = new Land(500);
          land6.building(new House("Lois", 300));
          land6.building(new House("Iga", 300));  // java.lang.IllegalStateException: already a building
        
    Faites en sorte que les deux codes ci-dessus fonctionne.
  8. On souhaite ajouter une méthode listBuildingBySurface qui renvoie tous les buildings pour lesquels la lambda passée en paramètre est vraie. La lambda est appelée avec les surfaces des buildings. Pour simplifier, on va considérer que NoBuilding a une surface de 0.
    Au passage, on introduit aussi un nouveau type de building appelé Hotel qui possède comme House un propriétaire owner de type String et une surface surface de type double.
    Enfin, on souhaite que la méthode building de Land qui prend en paramètre un building, renvoie le terrain sur lequel elle est appelée afin de pouvoir chaîner les appels comme dans le code ci-dessous (c'est le même comportement que la méthode append de StringBuilder).
          var estate6 = new Estate();
          estate6.addLand(new Land(600).building(new House("John", 500)));
          estate6.addLand(new Land(850).building(new Hotel("Jane", 450)));
          System.out.println(estate6.listBuildingBySurface(surface -> surface <= 470));
            // [Hotel[owner=Jane, surface=450.0]]
        
    Faites en sorte que le code ci-dessus fonctionne.
  9. On souhaite modifier un peu l'implantation pour que la méthode listBuildingBySurface renvoie les buildings dans l'ordre de leur surface (la surface la plus grande d'abord).
    Faites en sorte que le code suivant fonctionne
          var estate7 = new Estate();
          estate7.addLand(new Land(600).building(new House("Gil", 450)));
          estate7.addLand(new Land(550).building(new Hotel("Gillian", 500)));
          System.out.println(estate7.listBuildingBySurface(surfaceInSquareMeters -> surfaceInSquareMeters <= 700));
            // [Hotel[owner=Gillian, surface=500.0], House[owner=Gil, surface=450.0]]
        
    Note : il existe une méthode Comparator.reversed() que vous pouvez vouloir utiliser.
  10. Enfin, on souhaite ajouter à Estate une méthode owner qui renvoie la liste des propriétaires de building, par ordre de la surface de leurs buildings. Si un propriétaire possède plusieurs buildings, on fait la somme des surfaces.
    Pour cela, vous commencerez par écrire dans un premier temps une méthode owner qui prend en paramètre un building et renvoie son owner sous forme d'un Optional (NoBuilding n'a pas de propriétaire). Vous pouvez utiliser un switch sur les types si vous en avez envie et que vous n'utilisez pas de default.
    Une fois la méthode owner écrite, utilisez celle-ci pour écrire la méthode owners telle que le code suivant fonctionne.
          var estate8 = new Estate();
          estate8.addLand(new Land(600).building(new House("Elisa", 450)));
          estate8.addLand(new Land(850).building(new Hotel("Janet", 600)));
          estate8.addLand(new Land(850).building(new Hotel("Elisa", 500)));
          System.out.println(estate8.owners());  // [Elisa, Janet]