TP Java 2

Transcription

TP Java 2
Master 2 BBSG
POO, langage Java
Laurent Tichit
2. Objets
1.
2.
3.
4.
5.
6.
Les articles du supermarché
Dates
Flacons et bouteilles...
Points du plan
Objets membres d’autres objets
Fractions
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
•
•
•
•
private
private
private
private
long reference – un numéro qui caractérise l’article de manière unique
String intitule – la description de l’article sous forme de texte
float prixHT – le prix unitaire hors taxes de l’article
int quantiteEnStock – le nombre d’unités de l’article disponibles
et dispose d’un certain nombre de méthodes
• public Article(long reference, String intitule, float prixHT, int
quantiteEnStock) – constructeur «immédiat» qui donne une valeur à chacune des variables
d’instance,
• public long getReference(), public String getIntitule(), public int
getQuantiteEnStock() – les « accesseurs » permettant d’obtenir la valeur de chaque
variable d’instance
• public void approvisionner(int nombreUnites) – méthode pour augmenter la
quantité disponible de l’article
• public boolean vendre(int nombreUnites) – cette méthode enregistre la vente
d’un certain nombre d’unités de l’article, dont il faut donc diminuer en conséquence la quantité
disponible en stock.
Si nombreUnites est supérieur à la quantité disponible alors le stock n’est pas modifié et la
méthode renvoie false ; autrement elle renvoie true.
• public float prixHT() – cette méthode calcule et renvoie le prix HT de l’article
• public float prixTTC() – cette méthode calcule et renvoie le prix TTC de l’article
• public String toString() – chaîne de caractères exprimant la référence, l’intitulé et le
prix de l’article.
• public boolean equals(Article unArticle) – a.equals(b) est true si et
seulement si a et b représentent le même article (c’est-à-dire s’ils ont la même référence).
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 « officielle » 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
• public Date(int jour, int mois, int annee) – initialisation d’une date, à partir
des trois nombres donnés. La validité des ces nombres doit être vérifiée (voyez ci-dessous
comment) et, en cas de date incohérente, un message doit être affiché et le programme abandonné.
C’est ici que le jour de la semaine doit être déterminé (voyez ci-dessous une manière simple de le
faire).
• int getJour(), int getMois(), int getAnnee(), int getJourSemaine() –
des « accesseurs » pour obtenir les valeurs correspondantes
• public String toString() – renvoie l’expression d’une date sous forme de texte, comme
"vendredi 8 décembre 2006"
• public boolean infeg(Date d) – comparaison : a.infeg(b) renvoie true si la date
a est antérieure ou égale à la date b, sinon cela renvoie false.
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(int
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);
}
Écrit comme cela, vous obtenez 1 : dimanche, 2 : lundi, ... 7 : samedi. Pour avoir 0 : lundi, 1 : mardi, ...
6 : dimanche, remplacez la dernière ligne ci-dessus, par :
return (c.get(java.util.Calendar.DAY_OF_WEEK) + 5) % 7;
Attention à vos éventuels import : un malencontreux import java.util.*; va créer un conflit
muet entre java.util.Date et votre propre Date. En règle générale, évitez asolument les import
du style import monPackage.*;
2.3. Flacons et bouteilles...
Vous travaillez chez Bobard & Co., producteur exclusif du sirop Mirifik, un élixir extraordinaire 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 :
• des variables d’instance :
• private final float capacite – la capacité du flacon (en ml) ,
• private float volume – le volume de liquide (en ml) que le flacon contient à un
instant donné,
• private float concentration – la concentration de ce liquide, c.-à-d. le rapport
(volume sirop) / (volume sirop+eau),
• String etiquette – un texte libre « affiché » sur le flacon
• des méthodes :
• public Flacon(String etiquette, float capacite) – Construction d’un
flacon ayant la capacité donnée et portant l’étiquette indiquée. Initialement, le volume est
mis à zéro.
• public void verser(float volumeSirop, float volumeEau) – Ajout à
un flacon des volumes de sirop et d’eau indiqués.
La possibilité d’une telle opération doit être vérifiée et, en cas d’impossibilité, on doit
produire l’affichage d’un message et l’abandon du programme.
• public void transvaser(Flacon autreFlacon, float volume) –
Ajout à un flacon du volume indiqué du liquide extrait de l’autreFlacon indiqué.
La possibilité d’une telle opération doit être vérifiée et, en cas d’impossibilité, on doit
produire l’affichage d’un message et l’abandon du programme.
• public String toString() – Affichage de l’étiquette, du volume et de la
concentration du contenu du flacon en question.
Écrire 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(int 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), 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 k ; 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) ]
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 :
• Point[] sommets – le tableau des points qui sont les sommets de la ligne polygonale,
• int nbSommets – le nombre de sommets (ici, cette information est redondante, puisque
nbSommets = sommets.length)
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,
• 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(int x0, int y0, int x1, int 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 la simulation vous pouvez définir :
void tracerLigne(int x0, int y0, int x1, int 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 :
Vous devez faire en sorte que la vue publique de la classe soit inchangée, afin que les
applications de la classe Point (par exemple, la classe LignePol) 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é des objets).
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 :
• Rectangle(double x0, double y0, double x1, double y1), constructeur d’un
rectangle ayant (x0,y0) et (x1,y1) pour coins,
• String toString(), qui renvoie une expression du rectangle sous forme de texte, comme
"[(1.0,2.0);(3.0,4.0)]",
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);
}
On aurait pu penser que l’expression « r2 = r1; » affectait à r2 une duplication de r1. Qu’est-ce que
l’exécution du code précédent 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 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 :
• public Fraction(BigInteger num, BigInteger den) – constructeur basique
• public Fraction(int n, int d) – construction d’une fraction à partir de deux entiers
(qu’il faudra convertir en BigInteger)
• public Fraction(int n) – construction d’une fraction à partir d’un entier (pour
transformer un entier en fraction on met 1 comme dénominateur)
• public Fraction add(Fraction f) – addition de deux fractions
• public Fraction sub(Fraction f) – soustraction de fractions
• public Fraction mult(Fraction f) – multiplication de fractions
• public Fraction divi(Fraction f) – division de fractions
• public String toString() – conversion d’une fraction en chaîne de caractères
• public double doubleValue() – obtention d’une valeur décimale qui est une
approximation de la fraction
C'est pour éviter les problèmes de débordement qu'il est conseillé de représenter le numérateur et le
dénominateur d’une fraction par des objets BigInteger.