Les principales erreurs à ne pas refaire dans les prochains contrôles

Voici les principales erreurs repérées pendant la correction (les plus graves en premier) :

Un certain nombre d'entre vous ont un niveau faible et pourtant on me signale qu'il n'y a eu qu'un seul étudiant lors du dernier TP de soutien !

Si vous n'avez pas une bonne note pour ce premier contrôle, retenez qu'il ne compte que pour 20 % de la note finale et que vous pourrez donc facilement remonter d'une façon importante votre moyenne si vous vous améliorez. Tenez bien compte des remarques ci-dessus et passez beaucoup de temps à programmer ; il n'y a pas d'alternative si vous voulez avoir un bon niveau en programmation. Bon courage pour la suite.


Université de Nice Sophia-Antipolis
L3 - Programmation orientée objet

Contrôle 1- Durée : 2h.

Seuls documents autorisés : photocopies des transparents distribués en cours. Les énoncés et corrections des TP sont interdits. Éteignez les téléphones portables.

Important : la présentation et la lisibilité du code compteront dans la note finale. Vous êtes autorisé à écrire le code (et seulement le code) avec un crayon à papier si c'est parfaitement lisible (pas de crayon trop clair).

Ajoutez des commentaires quand vous pensez que ça peut être utile au correcteur. Ne mettez pas de commentaires évidents qui n'ajoutent rien à votre code.

Respectez le découpage en questions et l'ordre des questions. Les numéros des questions devront apparaître clairement sur votre feuille. Vous pouvez répondre à une question même si vous n'avez pas répondu aux questions précédentes mais si vous sautez une question, vous devez l'indiquer clairement.

Vous allez écrire une application pour gérer la répartition des charges dans des immeubles.

La société pour laquelle vous allez écrire cette application gère plusieurs immeubles. Chaque immeuble contient des appartements, des garages et des caves qui appartiennent à des propriétaires, et des parties communes qui appartiennent à la copropriété (c'est-à-dire qui appartiennent à tous les propriétaires). Dans les parties communes on peut trouver les voies d'accès aux appartements, les couloirs, l'entrée de l'immeuble, les ascenseurs, etc. On ne vous demande pas de modéliser les parties communes.

Chaque partie de l'immeuble qui appartient à un propriétaire s'appelle un lot. Chaque lot a un numéro qui l'identifie dans l'immeuble. Il y aura ainsi le lot 1, le lot 2, le lot 3, etc. Chaque lot a un certain nombre de tantièmes. Ce nombre de tantièmes permettra de calculer les charges dues par le propriétaire du lot. Par exemple si le lot 1 a 230 tantièmes, si le total de tous les tantièmes pour l'immeuble est 1000 et que les charges totales à payer sont de 2000 euros, le propriétaire du lot 1 devra payer (230 / 1000) x 2000 = 460 euros.

Il y a 3 sortes de lots :

Voici les règles utilisées pour le calcul des tantièmes (ces règles ont été simplifiée pour les besoins de l'exercice et ne reflètent pas exactement les règles utilisées dans la réalité) :

Autre exemple de calcul : le lot 34 est une cave de 10 m² ; son nombre de tantièmes final est de 10 x 1 x 14 = 140 tantièmes.

Si les charges totales pour tout l'immeuble se montent à 10000 euros et que le total des tantièmes pour tous les lots se monte à 100000, le propriétaire du lot 8 devra payer 10000 x 5580 / 100000 = 558 euros et le propriétaire du lot 34 devra payer 10000 x 140 / 100000 = 14 euros.

Vous allez modéliser les lots d'un immeuble avec des classes Java. Vous écrirez une classe pour les immeubles, une classe pour les appartements, une classe pour les garages et une classe pour les caves. Vous aurez peut-être aussi à écrire d'autres classes.

Informations pour un immeuble :

Voici les informations qui doivent être associées aux différents lots.

Pour les appartements :

Pour les garages :

Pour les caves :

Question 1 (6 points)

Dans cet exercice on ne s'occupe pas des tantièmes.

Donnez le code de classes pour modéliser les immeubles, les appartements, les garages et les caves. Pour vous éviter d'avoir trop de code à écrire on ne vous demande pas d'écrire le code des accesseurs et des modificateurs (getters et setters), à part ceux qui sont associés au propriétaire.

Toutes les classes devront appartenir au paquetage "tantiemes".

De même, écrivez seulement un constructeur dans la classe qui représente les immeubles : le constructeur prend en paramètre l'adresse de l'immeuble ainsi que le nombre maximum de lots que l'immeuble contiendra. Les lots seront ajoutés ensuite par une méthode (voir question 2).

Ecrivez aussi le constructeur de la classe associée aux garages, celui qui initialise toutes les variables d'instance. Vous aurez peut-être aussi à écrire du code dans d'autres classes pour que ce constructeur fonctionne.

Pour vous éviter d'avoir trop de code à écrire on ne vous demande pas de donner le code des autres constructeurs.

On ne vous demande pas d'écrire le code des méthodes toString().

Question 2 (1 point)

Dans la classe qui correspond à un immeuble vous écrirez une méthode ajouterLot qui permet d'ajouter un nouveau lot passé en paramètre ; vous supposerez pour simplifier votre code que le lot n'a pas déjà été ajouté.

Question 3 (2 points)

Vous écrirez aussi une méthode supprimerLot pour supprimer un lot. Cette dernière méthode prendra en paramètre le numéro du lot ; elle ne fera rien si le numéro du lot n'existe pas mais, dans ce cas, elle renverra la valeur booléenne false (elle renverra true si le lot existe).

Question 4 (4 points)

Vous allez modifier le code déjà écrit pour calculer les tantièmes de tous les lots d'un immeuble.

Ecrivez une méthode calculerTantiemes dans la classe associée aux immeubles, qui lancera le calcul de ces tantièmes pour tous les lots de l'immeuble. Vous donnerez aussi le code des autres méthodes éventuelles que vous devrez ajouter aux classes déjà écrites pour faire fonctionner cette méthode calculerTantiemes. Pensez à encapsuler au maximum vos classes et à bien choisir la classe qui va calculer les tantièmes d'un lot.

Les tantièmes calculés par la méthode devront être conservés dans des variables d'instance. Par exemple, les tantièmes d'un garage devront être enregistrés dans l'instance qui représente le garage.

Ne récrivez pas tout le code ; indiquez seulement très clairement le nouveau code, ainsi que l'endroit où ce code devra être ajouté.

Expliquez où vous avez utilisé le polymorphisme.

Question 5 (1 point)

Ecrivez une classe Lettre qui modélise très schématiquement un courrier envoyé aux propriétaires pour leur demander de payer les charges. Cette classe aura seulement 2 variables d'instance :

On ne vous demande pas d'écrire des accesseurs et des modificateurs associés aux variables d'instance. On ne vous demande pas d'écrire une méthode toString().

Question 6 (3 points)

Ajoutez une méthode genererLettres dans la classe qui représente les immeubles, qui prend en paramètre un nombre (double) qui correspondra au total des charges à payer et qui renvoie un tableau de Lettre qui correspondra aux lettres envoyées aux propriétaires pour leur demander de payer les charges (une lettre par lot). Vous supposerez que calculerTantiemes a déjà été exécuté au moment où genererLettres est exécuté.

Par exemple, si le total des charges à payer pour tout l'immeuble s'élève à 20000 euros, que le propriétaire Dupond possède un lot de 100 tantièmes et que le total des tantièmes de tous les lots se monte à 1000, une des lettres s'adressera à Dupond et aura le montant de 2000 euros.

Question 7 (3 points)

Ecrivez une classe Main qui contient une méthode main pour tester le code déjà écrit. Cette méthode crée un immeuble qui contient l'appartement et la cave donnés en exemple pour les tantièmes au début de l'énoncé (lots 8 et 34 ; vous supposerez écrits les constructeurs pour les caves) ; vous utiliserez la méthode ajouterLot. Elle génère ensuite les lettres à expédier aux propriétaires (une lettre par lot). Elle affiche ensuite les lettres en utilisant la méthode toStringde la classe Lettre, que vous supposerez écrite.

La classe Main appartiendra au paquetage fr.unice.test, donc à un paquetage différent des autres classes.

Annexe : les tantièmes

Les frais occasionnés par les parties communes et les travaux effectués sur l'immeuble (les ravalements de la façade, par exemple) ont un certain coût qu'il faut partager entre tous les propriétaires.

Cette répartition s'effectue proportionnellement aux tantièmes. Plus un appartement est grand et plus ses tantièmes sont importants ; ainsi le propriétaire d'un grand appartement paiera davantage que le propriétaire d'un petit appartement. Le calcul des tantièmes tient compte aussi d'autres critères ; ainsi le propriétaire d'un appartement situé au 10ème étage participera davantage (à cause des frais d'ascenseur) que le propriétaire d'un appartement situé au 1er étage, et le propriétaire d'un appartement paiera davantage que celui d'une cave.

Correction

Question 1 (6 points)

package tantiemes;

/**
 * Lot d'un immeuble.
 */
public abstract class Lot {
  private int numero;
  private int surface;
  private String proprietaire;
  
  public String getProprietaire() {
    return proprietaire;
  }
  
  public void setProprietaire(String proprietaire) {
    this.proprietaire = proprietaire;
  }
}
package tantiemes;

public class Appartement extends Lot {
  private int etage;
  private int surfaceBalcon;
}
package tantiemes;

public class Garage extends Lot {
  private boolean ferme;

  public Garage(int numero, int surface, String proprietaire,
      boolean ferme) {
    super(numero, surface, proprietaire);
    this.ferme = ferme;
  }
}
package tantiemes;

public class Cave extends Lot {
}
package tantiemes;

public class Immeuble {
  private String adresse;
  private Lot[] lots;
  /**
   * Nombre de lots déjà ajoutés à cet immeuble.
   */
  private int nbLots;

  /**
   * 
   * @param adresse adresse de l'immeuble.
   * @param nbMaxLots nombre maximum de lots de l'immeuble.
   */
  public Immeuble(String adresse, int nbMaxLots) {
    this.adresse = adresse;
    lots = new Lot[nbMaxLots];
  }

Question 2 (1 point)

  public void ajouterLot(Lot lot) {
    lots[nbLots] = lot;
    nbLots++;
  }

Question 3 (2 points)

  public boolean supprimerLot(int numeroLot) {
    for (int i = 0; i < nbLots; i++) {
      Lot lot = lots[i];
      if (lot.getNumero() == numeroLot) {
        // C'est ce lot qu'il faut supprimer
        // Décaler tous les lots suivants
        for (int j = i + 1; j < nbLots; j++) {
          lots[j - 1] = lots[j];
        }
        // Décrémenter le nombre de lots
        nbLots--;
        // Sortir de la boucle
        return true;
      }
    }
    // Lot pas trouvé
    return false;
  }

Question 4 (4 points)

Dans la classe Immeuble :
  /**
   * Calcule les tantièmes pour chacun des lots.
   * Cette méthode n'a rien à voir avec les méthodes
   * calculerTantiemes de Lot et de ses sous-classes ;
   * peut-être aurait-il fallu l'appeler calculerTousLesTantiemes
   * pour éviter des malentendus
   * mais le nom était imposé par l'énoncé.
   */
  public void calculerTantiemes() {
    for (int i = 0; i < nbLots; i++) {
      lots[i].calculerTantiemes();
    }
  }
Dans la classe Lot :
  private int tantiemes;
  /**
   * Calculer ses propres tantièmes.
   * Cette méthode est indispensable pour que le
   * polymorphisme soit possible.
   */
  public abstract void calculerTantiemes();

  /**
   * Retourne les tantièmes pour ce lot.
   */
  public int getTantiemes() {
    return tantiemes;
  }
  
  protected void setTantiemes(int valeur) {
    this.tantiemes = valeur;
  }
Dans la classe Appartement :
  @Override
  public void calculerTantiemes() {
    setTantiemes((3 * getSurface() + surfaceBalcon) * (10 + 4 * etage)); 
  }
Dans la classe Garage :
  @Override
  public void calculerTantiemes() {
    setTantiemes(getSurface() * (ferme ? 2 : 1) * 14);
  }
Dans la classe Cave :
  @Override
  public void calculerTantiemes() {
    setTantiemes(getSurface() * 14);
  }
Le polymorphisme est utilisé avec la méthode calculerTantiemes de Lot et de ses sous-classes, dans la boucle for de la méthode calculerTantiemes de la classe Immeuble.

Question 5 (1 point)

package tantiemes;

/**
 * Modélise une lettre envoyée aux propriétaires.
 * Une seule lettre est envoyée à chaque propriétaire,
 * même s'il possède plusieurs lots.
 */
public class Lettre {
  private String nomProprietaire;
  private double montantCharges;
  
  public Lettre(String nomProprietaire, double montantCharges) {
    this.nomProprietaire = nomProprietaire;
    this.montantCharges = montantCharges;
  }
}

Question 6 (3 points)

  /**
   * Simule le calcul et l'envoi des charges à payer
   * pour tous les propriétaires de l'immeuble.
   * une lettre par lot, même si un propriétaire a plusieurs lots.
   * @return les lettres à envoyer.
   */
  public Lettre[] genererLettres(double montantGlobalCharges) {
    // Calcul de total des tantièmes
    int totalTantiemes = 0;
    for (int i = 0; i < nbLots; i++) {
      totalTantiemes += lots[i].getTantiemes();
    }
    Lettre[] lettres = new Lettre[nbLots];
    for (int i = 0; i < nbLots; i++) {
      lettres[i] = 
        new Lettre(lots[i].getProprietaire(), 
                   lots[i].getTantiemes() * montantGlobalCharges / totalTantiemes);
    }
    return lettres;
  }

Question 7 (3 points)

package fr.unice.test;

import tantiemes.*;

public class Main {
  public static void main(String[] args) {
    Immeuble immeuble = new Immeuble("Nice", 12);
    immeuble.ajouterLot(new Appartement(8, 100, "Dupond", 2, 10));
    immeuble.ajouterLot(new Cave(34, 10, "Durand"));
    immeuble.calculerTantiemesImmeuble();
    Lettre[] lettres = immeuble.genererLettres(10000);
    for (int i = 0; i < lettres.length; i++) {
      System.out.println(lettres[i]);
    }
  }
}