Master 2 CCI 2012-2013
POO, langage Java
Henri Garreta 

2. Objets

        1. Les articles du supermarché
        2. Dates
        3. Flacons et bouteilles...
        4. Points du plan
        5. Objets membres d’autres objets
        6. Numérophagie
        7. Fractions
Le signe © renvoie à la correction

2.1. Les articles du supermarché ©

A. Écrivez une classe Article pour représenter les articles vendus dans un supermarché. Chacun comporte quatre variables d’instance

et dispose d’un certain nombre de méthodes

B. Pour tester cette classe écrivez une méthode main qui crée un tableau comportant trois articles (au moins) et qui essaie toutes ces méthodes.

N.B. Attention, la méthode equals vraiment « correcte » n’a pas exactement l’en-tête que nous avons utilisé ici (on verra cela plus loin).

2.2. Dates ©

Pour cet exercice faites semblant de ne pas savoir qu’il existe dans la bibliothèque Java des classes java.util.Date et java.util.Calendar qui offrent probablement les services demandés ici.

Écrivez une classe Date dont chaque instance représente une date précise. Mettez-y quatre variables d’instance privées : annee (un entier non négatif), mois (un entier compris entre 1 et 12), jour (un entier compris entre 1 et une borne supérieure qui dépend du mois) et jourSemaine (un entier indiquant le jour de la semaine : 0 pour lundi, 1 pour mardi, etc.

Écrivez les méthodes

Pour simplifier, supposez qu’une année est bissextile si et seulement si son millésime est divisible par quatre (en réalité c’est un peu plus compliqué).

Indications, 1 – Pour abandonner le programme vous pouvez appeler la méthode System.exit(code). Un tel traitement des erreurs est naïf, nous verrons plus loin (à l’occasion des exceptions) une meilleure manière de faire cela.

Indications, 2 – Pour vous éviter de perdre votre temps sur des choses qui ne sont pas l’objet de cet exercice, voici une expression évaluant la validité d’une date (avec la simplication sur les années bissextiles mentionnée). Vous pouvez vous contenter de la copier-coller dans votre programme, puis de tester la valeur de dateInvalide :

boolean dateInvalide = mois < 1 || mois > 12 
        || jour < 0 || jour > 31
        || jour == 31 && (mois == 4 || mois == 6 || mois == 9 || mois == 11)
        || jour >= 30 && mois == 2 
        || jour == 29 && mois == 2 && annee % 4 != 0;

Indications, 3 – Pour vous éviter de perdre votre temps sur des choses qui ne sont pas l’objet de cet exercice, voici une manière simple de calculer le jour de la semaine correspondant à une date donnée (en trichant un peu par rapport à notre engagement de ne pas utiliser les outils de la bibliothèque Java) :

private int jourSemaine(int jour, int mois, int annee) {
    Calendar c = Calendar.getInstance();
    c.set(annee, mois - 1, jour);
    return c.get(Calendar.DAY_OF_WEEK);
}

Attention, cette méthode renvoie 1 pour dimanche, 2 pour lundi, ... 7 pour samedi.

2.3. Flacons et bouteilles... ©

Vous travaillez chez Bobard & Co., fabriquant exclusif du sirop Mirifik, un élixir issu de la recherche spatiale qui rend jeune, beau et intelligent et qui empêche la chute des cheveux et les tâches de transpiration. Dilué à différentes concentrations, le sirop Mirifik est commercialisé dans diverses sortes de flacons.

On vous demande d’écrire une classe Flacon dont les instances représentent les flacons de sirop Mirifik en stock. Cette classe comportera :

Écrivez également une méthode main pour tester toutes ces opérations.

Que pensez-vous des qualifieurs que nous avons appliqué aux variables d’instance ? Pourquoi capacite a été qualifiée final ? Est-il intéressant de qualifier private le volume et la concentration ?

N.B. Comme à l’exercice précédent, pour abandonner le programme vous pouvez appeler la méthode System.exit(code).

2.4. Points du plan ©

A. Définissez une classe Point pour représenter les points du plan rapporté à une origine fixée. Les coordonnées d’un point sont ici deux nombres flottants x, y, mémorisés dans deux variables d’instance privées (par exemple de type double). La classe Point comportera au moins les méthodes d’instance suivantes, toutes publiques :

  • Point(double x, double y), constructeur d’un point à partir de ses coordonnées cartésiennes,
  • double x(), double y() qui renvoient les coordonnées cartésiennes du point,
  • double r(), double t(), qui renvoient les coordonnées polaires du point (voir ci-dessous),
  • String toString(), qui renvoie une expression textuelle du point, comme « (2.0,3.0) »,
  • boolean equals(Object o), qui effectue la comparaison : a.equals(b) répond à la question « a et b représentent-ils deux points égaux ? »,
  • void homothetie(double k), qui applique au point une homothétie de centre (0, 0) et de rapport ; pour cela, il suffit de remplacer (x, y) par (k × x, k × y), 
  • void translation(double dx, double dy), qui applique au point une translation de vecteur (dx, dy) ; cela consiste à remplacer (x, y) par (x + dx, y + dy).
  • void rotation(double a), qui applique au point une rotation de centre (0, 0) et d’angle a.
    Une manière – qui n’est pas la plus efficace – de faire cela consiste à calculer les coordonnées polaires (r, t) correspondant à (x, y) puis les coordonnées cartésiennes (x’, y’) correspondant à (r, t + a)

Rappel de formules : x = r × cos(t), y = r × sin(t), r = sqrt(x2 + y2), t = atan(y / x) [ou, mieux, t = atan2(y, x) ], où cos, sin, sqrt, atan2 sont des méthodes statiques membres de la classe java.lang.Math.

B. Application de la classe Point. Une ligne polygonale est définie par un certain nombre de points, ses sommets. Définissez une classe LignePol pour représenter de tels objets. Les sommets y seront mémorisés sous forme de tableau de points. Il en découle que le nombre de sommets d’une ligne polygonale ne pourra pas augmenter au cours de la vie de celle-ci, mais cela ne sera pas un problème dans le cadre de cet exercice.

Cette classe comportera au moins deux variables d’instance privées :

et les méthodes publiques :

  • LignePol(int n), constructeur d’une ligne polygonale à n sommets (initialement indéterminés),
  • LignePol(Point[] sommets), constructeur d’une ligne polygonale ayant les sommets indiqués,
  • Point getSommet(int i), renvoie le ième sommet (faites en sorte que cette méthode ne puisse pas être utilisée pour modifier la ligne polygonale),
  • void setSommet(int i, Point p), donne p pour valeur du ième sommet,
  • String toString(), renvoie une expression de la ligne polygonale sous forme de texte,
  • void homothetie(double k), qui applique à chaque sommet de la ligne polygonale une homothétie de centre (0, 0) et de rapport k,
  • void translation(double dx, double dy), qui applique à chaque sommet de la ligne polygonale une translation de vecteur (dx, dy)
  • void rotation(double a), qui applique à chaque sommet de la ligne polygonale une rotation de centre (0, 0) et d’angle a

En supposant (c’est tout à fait fictif) que vous disposiez d’un environnement graphique dans lequel on obtient le tracé du d’extrémités (x0,y0) et (x1,y1) par l’expression

tracerLigne(double x0, double y0, double x1, double y1)

ajoutez à la classe LignePol une méthode tracer() qui produit le tracé de la ligne polygonale. Ensuite, écrivez une méthode main qui construit et « trace » la cocotte ci-dessus.

N.B. Pour faire tourner une sorte de simulation, vous pouvez définir :

void tracerLigne(double x0, double y0, double x1, double y1) {
    System.out.println("tracer de (" + x0 + "," + y0 + ") à (" + x1 + "," + y1 + ")");
}

C. Imaginons (c’est de la fiction) qu’une enquête effectuée auprès des utilisateurs de votre classe Point a montré que cette classe est correcte et pratique mais que, du point de vue des performances, il aurait mieux valu prendre pour représentation interne des points leur coordonnées polaires (r, t) au lieu des coordonnées cartésiennes (x, y).

Réécrivez donc la classe Point de manière que les variables d’instance soient les coordonnées polaires du point, et non plus ses coordonnées cartésiennes, en respectant la contrainte suivante :

La vue publique de la classe doit être inchangée, afin que les applications de la classe Point restent valides sans nécessiter la moindre modification. Une confirmation qu’on a respecté cette contrainte sera que le programme de la question précédente fonctionne sans qu’il y ait besoin de modifier quoi que ce soit.

2.5 Objets membres d’autres objets ©

Cet exercice est surtout une petite réflexion sur la notion de duplication ou clonage d’objets, surtout quand il s’agit d’objets qui ont pour membres d’autres objets (ce qui est le cas de la majorité).

Vous devez disposer d’une classe Point, même très simple (par exemple, celle du début de l’exercice précédent). Assurez-vous que cette classe n’a pas de constructeur sans arguments.

A. Un rectangle est déterminé par la donnée de deux points, respectivement son angle supérieur gauche et son angle inférieur droit. Définissez une classe Rectangle ayant deux variables d’instance privées coinNO (pour « coin Nord-Ouest ») et coinSE (pour « coin Sud-Est ») et, au moins, les méthodes suivantes :

B. Faites le test suivant :

public static void main(String[] args) {
    Rectangle r1, r2;
    r1 = new Rectangle(1, 2, 3, 4); 
    r2 = r1; 
    System.out.println("r1: " + r1 + ", r2: " + r2); 
    r1.coinNO = new Point(0, 0); 
    System.out.println("r1: " + r1 + ", r2: " + r2); 
    r1.coinSE.homothetie(2);    
    System.out.println("r1: " + r1 + ", r2: " + r2);
}

Si on pensait que l’expression « r2 = r1; » affectait à r2 une duplication de r1, qu’est-ce que l’exécution du code précédent démontre ?

C. Écrivez la première ligne de la classe Rectangle comme ceci

public class Rectangle implements Cloneable { 

et l’en-tête de la fonction main comme ceci

public static void main(String[] args) throws CloneNotSupportedException {

Maintenant remplacez la ligne « r2 = r1; » par

r2 = (Rectangle) r1.clone();

Votre classe utilise donc maintenant la version prédéfinie de la méthode clone. Reexécutez le test : qu’est-ce que vous constatez ?

D. Ajoutez à votre classe la méthode suivante (vous pouvez effacer ou non les déclarations « implements » :

public Rectangle clone() { 
    return new Rectangle(coinNO.x(), coinNO.y(), coinSE.x(), coinSE.y());
}

et refaites le test. Que constatez-vous à présent ?

2.6 Numérophagie ©

L’objet de cet exercice est la réalisation d’une version orientée objets du bon vieux crible d’Eratosthène.

Le crible d’Eratosthène est un algorithme pour obtenir les nombres premiers inférieurs à un certain nombre n. Pour cela, on considère la suite des entiers de 2 à n. On prend le premier (2) et on fait disparaître ses multiples (4, 6, 8, etc.) ; après quoi on prend le suivant de ceux qui restent (3) et on supprime ses multiples (9, 15, 21, etc.) ; puis le suivant (5) et on enlève ses multiples (25, 35, 55, etc.) et ainsi de suite. C’est comme si 2 « mangeait » tous ses multiples, puis 3 mangeait ses multiples restants (ceux que 2 n’a pas mangé), puis 5 mangeait les siens, etc.

D’où l’idée de définir chaque nombre premier comme un objet mangeur de nombres, ou Mangenombres. Les mangenombres sont arrangés en une sorte de liste chaînée : chacun a un successeur. De plus, sauf le premier qu’il faut créer « à la main », chacun est créé le moment venu par un mangenombres préexistant, son prédécesseur :


On donne 17 à manger à 2. Une opération qui provoquera la création d’un Mangenombres 17, successeur de 13.

Écrivez la classe Mangenombres, comportant

La création d’un mangenombres mq doit provoquer l’affichage de q.

Écrivez un programme principal affichant les nombres premiers entre 2 et un certain n. Ce programme doit créer un premier mangenombres, associé au nombre premier 2, puis exécuter une boucle dans laquelle les nombres de 2 à n sont donnés à manger à ce premier mangenombres.

2.7 Fractions ©

La bibliothèque Java ne comporte pas de classe pour représenter les fractions (comme celles de l’école primaire, avec un numérateur et un dénominateur). On se propose ici de pallier ce manque, en définissant une classe Fraction munie des méthodes suivantes :

Pour éviter les problèmes de débordement, il est conseillé de représenter le numérateur et le dénominateur d’une fraction par des objets BigInteger.