Objectif: L’objectif de ce TP est de découvrir les bases de la programmation orientée objet avec Java. Nous nous attacherons à construire et à manipuler une classe très simple pour mettre en pratique les notions de classe, d’objet, d’attribut, de méthode, de rétention d’information (encapsulation en anglais), de constructeur, ...

La première partie de ce sujet (Nombres Rationnels) est une reprise d'un sujet donné dans le cours de Programmation Orientée Objet en 2ème année ENISMAG (le sujet original).

Vous connaissez probablement tous la définition de l’ensemble \(\mathbb{Q}\) des rationnels. Un nombre rationnel est un nombre qui peut être défini par un ratio \(n/d\) , où \(n\), appelé numérateur, est un entier relatif, et \(d\), appelé dénominateur, est un entier relatif non nul. Nous allons dans ce TP construire une classe représentant les nombres rationnels.

Avant tout chose organisez votre espace disque afin de pouvoir par la suite retrouver facilement le travail effectué lors de cette séance de TP. Pour cela, dans le répertoire PLAI/Java créé lors du TP1, ajoutez un sous répertoire TP04 dans lequel vous rangerez le travail effectué au cours ce TP.

1ère Partie : Nombres Rationnels

Première version d’une classe Rational

Commençons doucement en créant une première version très simple d'une classe Rational qui représente un nombre rationnel.

  1. Créez une classe Rational, possédant deux attributs entiers \(num\) et \(denom\) . (Reportez vous au TP 03 pour voir comment créer un projet Java et une classe avec l'IDE VSCode).
  2. Écrivez un programme de test qui crée une fraction \(3/2\), affiche son numérateur, et affiche son dénominateur (dans la console).
  3. Compilez et exécutez ce programme pour vérifier que votre classe Rational fonctionne correctement.

Représentation textuelle d’un rationnel

  1. Modifiez votre classe Rational afin d’y ajouter une méthode toString() sans paramètre, et renvoyant une chaîne de caractères (type String) représentant le nombre rationnel (par exemple, cette méthode, appelée sur le rationnel \(3/2\), devrait renvoyer la chaîne de caractères "3 / 2").
  2. Modifiez votre programme de test pour utiliser la méthode toString() créée précédemment. Compilez et exécutez.

Exceptions

  1. Modifiez votre programme de test pour créer un autre rationnel de dénominateur égal à 0. Est-ce possible ? Si oui, en quoi est-ce un problème ?

  2. Pour empêcher la création et la manipulation d’objets rationnels ayant pour dénominateur 0. Vous ajouterez au constructeur de votre classe Rational un test qui au cas où le dénominateur serait nul exécute l'instruction suivante :

    throw new IllegalArgumentException("Dénominateur nul");

    Modifiez votre programme de test pour créer un rationnel avec un dénominateur nul, que ce passe-til ? D'après vous quel est le rôle et l'effet de l'instruction précédente ?

Opérations arithmétiques

Pour rappel (au cas où...), la multiplication de deux rationnels est définie de la manière suivante : \begin{equation} \frac{n}{d} \times \frac{n'}{d'} = \frac{nn'}{dd'} \end{equation}

  1. Ajoutez à votre classe Rational une méthode mult prenant en paramètre un autre rationnel, et le multipliant au rationnel sur lequel elle est appliquée. La méthode ne renvoie rien (type de retour void).

    Cette opération modifie l’état de l’objet sur lequel la méthode est invoquée : a.mult(b) revient à faire \(a \leftarrow a*b\). Il n’est pas possible en Java de redéfinir des opérateurs (comme en C++), sinon ce serait équivalent à \(a *= b\).

  2. Modifiez votre programme de test en créant un second rationnel (\(1/3\) par exemple), en le multipliant au premier, et en affichant le résultat. Compilez et exécutez.

  3. Mêmes questions pour l’addition. Pour rappel, l’addition de deux rationnels est définie de la manière suivante : $$ \frac{n}{d}\ + \frac{n'}{d'} = \frac{nd' + n'd}{dd'}$$

Fractions irréductibles

En plus d’interdire la création et la manipulation de rationnels dont le dénominateur est à \(0\), nous souhaitons que tout rationnel soit toujours sous sa forme irréductible. Il s’agit d’un invariant de classe : à tout instant, et ce dès sa création, un objet Rational est toujours sous forme irréductible.

Pour rappel, un rationnel \(n/d\) est sous forme irréductible si et seulement si \(pgcd(n, d) = 1\), avec :

fonction pgcd(a,b)
     si b est égal à 0 
        renvoyer a
     sinon 
        renvoyer pgcd(b, reste(a,b))

Par exemple, la forme irréductible du rationnel \(8/12\) est \(2/3\). avec :

  1. Modifiez votre classe Rational en conséquence. Testez.

  2. Ecrivez une fonction qui teste l'égalité de deux nombres rationnels. Testez.

2ème Partie : Séquence d'entiers

Il s'agit ici d'écrire en Java une implémentation d'un type abstrait séquence d'entiers.

Les opérations possibles sur une séquence sont les suivantes :

  • Créer une séquence vide

  • Ajouter une nouvelle valeur en tête de séquence

  • Ajouter une nouvelle valeur en fin de séquence

  • Obtenir la longueur de la séquence (le nombre de valeurs présentes)

  • Tester si la sequence est vide

  • Récupérer la valeur (entier) d'un élément de la séquence désigné par son rang (entier compris en tre 1 et la longueur de la séquence

  • Retirer le premier élément de la séquence.

  • Retirer le dernier élément de la séquence

  • Rechercher le rang d'une valeur donnée dans la séquence. Cette fonction retourne un entier qui correspond au rang de la première occurence de la valeur dans la séquence en partant du début de celle-ci. Si la valeur n'est pas présente la valeur retournée est -1.

  • Rechercher le rang d'une valeur donnée dans la séquence à partir d'un rang donné. Cette fonction retourne un entier qui correspond au rang de la première occurence de la valeur dans la séquence en partant du rang donné. Si la valeur n'est pas présente la valeur retournée est -1.

Question 1: Ecrivez le code d'une classe SequenceEntiers qui impélémente ce type abstrait à l'aide d'une liste chaînée et un programme permettant de la tester.

Indication : Pour la réalisation de SequenceEntiers, écrivez une deuxième classe ElementSeqEntierqui permet de représenter un élément de la séquence (une valeur entière et un pointeur (une référence) sur l'élement suivant).

Question 2: Rajouter à votre classe SequenceEntiers un deuxième constructeur qui permet de créer une séquence en faisant la copie d'une séquence passée en paramètre.

Ecrivez un programme de test de ce constructeur.

Question 3: Rajoutez à votre classe SequenceEntiers une méthode qui permet d'obtenir une nouvelle séquence ne contenant que les valeurs de rang impairs dans la séquence.

Ecrivez un programme de test de cette méthode.