Skip to content
Snippets Groups Projects
Commit 764a56fa authored by Fabien Delecroix's avatar Fabien Delecroix
Browse files

sujet étudiant

parents
No related branches found
No related tags found
No related merge requests found
README.md 0 → 100644
# TP n°1 : Annuaire (Modularité)
Dans ce TP, nous allons repartir d’un TP réalisé au S2 en Développement Orienté Objet pour revoir les notions de polymorphisme et appliquer celles de modularité.
## Outillage et démarrage
### Groupe
Commencez par *créer un groupe* dans lequel ranger l'ensemble des TP de R3.04.
<a>![](img/group.png)</a>
### Fork
Sur ce dépôt, en plus du sujet (ce document), vous trouverez le code source de classes Java existantes desquelles nous allons repartir.
Créez une divergence (*fork* en anglais) de ce projet de manière à en avoir une copie sur un dépôt distant qui vous appartienne et sur lequel vous pourrez faire des modifications sans altérer l'original.
<a>![](img/fork.png)</a>
Vous veillerez à bien utiliser le groupe créé précédemment.
<a>![](img/espace_nom.png)</a>
### Clone
Depuis un terminal, positionnez vous dans le répertoire dans lequel vous avez choisi de ranger les TP de R3.04 dans votre session. Clonez le dépôt distant.
### Environnement de développement intégré
Pour réaliser ce TP, nous vous conseillons d'utiliser Eclipse ou VS Code comme Environnement de Développement Intégré (*IDE* en anglais).
Dans le cas où vous utilisez VS Code, pensez à installer l'*Extension pack for Java* si ça n'est pas déjà fait.
Ouvrez le projet dans votre IDE (en général, cela correspond au répertoire parent de *src*), ici **annuaire**.
## Replongeons dans ce code
Note : Pour la pertinence de ce sujet, des adaptations ont été apportées à ce code qui ne correspond pas tout à fait à celui que vous aviez à faire au S2 mais en reprend les grandes lignes.
### Exécution et fonctionnalités
Un point de départ qui permet de se mettre dans le contexte est souvent de passer par une première exécution, en particulier si un main est disponible à cette fin. C'est aussi un moyen de s'assurer d'une configuration correcte de notre IDE et du fonctionnement au moins partiel du logiciel avant toute modification.
### Documentation
Une manière d'appréhender le code en lui-même est de se référer à sa documentation (d'où l'intérêt de l'écrire...!). Ici, le code est plutôt bien documenté.
Générez la documentation, cela peut être fait simplement en ligne de commande depuis le répertoire *annuaire* :
```
javadoc -d doc src/main/java/devoo/tp07/*.java
```
Explorez ensuite la documentation générée en partant du fichier index.html dans le répertoire *doc*.
### UML
Un autre outil qui peut être pratique pour appréhender un programme développé en langage objets est le diagramme de classes.
Dessinez sur une feuille, de préférence au crayon, le diagramme de classes correspondant au code fourni et montrez-le à votre enseignant pour un retour de sa part. L'idée ici est de faire remonter à la surface vos connaissances tout en vous familiarisant avec le code de ce TP.
## Quelques améliorations
### Paquetages et modularité
Au S2, en Dév-oo, nous avions pris l'habitude de travailler au sein du même projet et d'associer à chaque sujet de tp un paquetage différent. Durant ce semestre, en qualité de développement, la règle générale sera que chaque sujet de TP constituera un projet à part entière. Ainsi, les paquetages serviront à l'architecture et à la modularité de votre code.
Dans cet esprit, nous allons ici commencer par réorganiser le code dans une optique d'API (*Application Programming Interface*). On sort donc de la dimension scolaire pour un nommage plus professionnel à visée universelle. On va donc inclure les classes présentes dans un paquetage **fr.univlille.phonebook**. Pour ce faire, on peut utiliser les outils de refactorisation de code, plus ou moins ergonomiques selon l'IDE, pour un renommage global sans aller changer chaque fichier "à la main".
À quoi la classe *ConsolePhoneBookManager* a-t-elle accès dans *PhoneBook* ?
Trouvez une solution de manière à mieux respecter le principe de modularité.
Une fois ces opérations effectuées, mettez à jour votre dépôt git distant en veillant à ne pas le polluer par d'éventuels fichiers non pertinents sur le dépôt. Vous pouvez reprendre vos cours précédents si vous avez oublié ou demander à votre enseignant.
#### Static
Dans la classe *ConsolePhoneBookManager*, différentes méthodes recourent à un même objet de manière relativement implicite. Revoyez le code de manière à éviter ce problème.
### Résolution de bug
Un bug a été signalé concernant l'application, il se produirait lorsqu'on veut consulter les coordonnées d'un correspondant absent de l'annuaire. Identifiez le problème et apportez lui une solution en terme de code et surtout de documentation. Une fois le correctif apporté, mettez à jour le dépôt distant.
### Qualité
Un des éléments majeurs pour augmenter la valeur du code et justement d'éviter la présence de bugs. Pour ce faire, recourez à des tests pertinents qui garantissent le bon comportement des méthodes.
Ici, des tests ont été réalisés pour la classe *PhoneNumber*. Réalisez-en pour les classes *ULillePhoneNumber* et *PhoneBook* afin de vous assurer du bon fonctionnement de ses méthodes dans divers scénarios. L'intérêt des tests est aussi de pouvoir détecter une éventuelle future erreur de manière automatisée (pour peu qu'on les exécute régulièrement), sans avoir à essayer "à la main" une série de scénarios.
Une fois vos tests réalisés, pensez de nouveau à mettre à jour votre dépôt distant.
Dans la suite de ce sujet, il n'y aura plus d'indication concernant les commits à réaliser. Ça sera donc à vous d'aviser pour les effectuer de manière régulière et pertinente.
## Ajout de nouvelles fonctionnalités
### Intégration de la classe ULillePhoneNumber
Pour l'instant, à quel endroit la classe *ULillePhoneNumber* est-elle utilisée ?
Pour répondre à cette question, les IDE proposent généralement une fonctionnalité permettant de lister les références via un clic droit sur la classe (ou la méthode) souhaitée.
Modifiez le logiciel pour qu'il soit désormais aussi possible d'ajouter un numéro interne à l'université à partir uniquement du département et du numéro de poste. Il s'agit d'un numéro plus court, uniquement constitué des 5 derniers chiffres, on considérera ici que les premiers sont les mêmes quelque soit le poste (ça n'est pas tout à fait vrai en réalité).
Vous essaierez de faire en sorte que le numéro soit instancié comme interne qu'il soit saisi sous une forme ou une autre, par exemple *+33.3.59.03.21.06*, *03.59.03.21.06* ou *32106*.
### Affichage du numéro de poste interne
On vous a commandé l'ajout d'une nouvelle fonctionnalité : la récupération du numéro interne à l'Université.
Le scénario est le suivant :
On souhaite passer un appel depuis l'Université vers un poste de l'Université. On récupére donc le numéro de poste interne plutôt que le numéro en entier.
Implémentez cette fonctionnalité.
### Mise à jour de numéro de téléphone
Il arrive parfois qu'un poste change de numéro de téléphone. Dans ce cas, il faut pouvoir mettre à jour l'ensemble des contacts concernés en remplaçant l'ancien numéro par le nouveau. Réfléchissez à une solution pour ajouter cette fonctionnalité à l'application, enrichissez votre diagramme de classes en conséquence et proposez-la à votre enseignant.
Après validation de votre enseignant, implémentez la fonctionnalité ainsi que les tests correspondants.
## Généralisation
L'application est aussi susceptible de servir pour d'autres universités, voire des entreprises, disposant elles aussi de numéros de postes internes. Les numéros internes de l'Université sont sur 5 chiffres mais il pourrait en être autrement pour d'autres structures. Qu'y a-t-il à changer dans notre prototype pour qu'il corresponde à ce besoin ?
Discutez-en avec votre enseignant puis effectuez ces modifications.
## Tests
Implémentez des tests pour les parties du code qui ne sont pas couvertes actuellement (hors entrées/sorties). À noter qu'il existe des outils pour mesurer la couverture de code des tests. L'IDE Eclipse intègre cette fonctionnalité sans installation supplémentaire. Pour VS Code, moins *java-friendly*, il ne semble pas y avoir d'équivalent, y compris via les extensions. La pratique est de passer par un outil d'automatisation tel que maven (abordé au S4-S5).
## Bonus
Choisissez une fonctionnalité à ajouter à votre application, réfléchissez à une solution en terme de conception puis essayez de l'implémenter.
img/espace_nom.png

13 KiB

img/fork.png

3.3 KiB

img/group.png

9.74 KiB

File added
package devoo.tp07;
import java.util.InputMismatchException;
import java.util.Scanner;
public class ConsolePhoneBookManager {
private static Scanner scan = new Scanner(System.in);
private static PhoneBook phoneBook;
public static void main(String[] args) {
phoneBook = new PhoneBook();
indicatifConfig();
menu();
}
private static void indicatifConfig() {
int choiceNum = askChoice("Souhaitez-vous passer des appels depuis", "la France", "l'international");
if (choiceNum == 0){
PhoneNumber.setCountryCodeReference(PhoneNumber.FRANCE_CODE);
}else if (choiceNum == 1){
PhoneNumber.setCountryCodeReference(askCountryCode());
}
}
private static int askCountryCode() {
System.out.println("Entrez l'indicatif du pays : ");
String answer = scan.nextLine();
try{
int userCountryCode = Integer.parseInt(answer);
if (userCountryCode<=0)
throw new NumberFormatException();
else
return userCountryCode;
}catch(NumberFormatException nfe){
System.out.println("Valeur incorrecte : nombre positif attendu");
return askCountryCode();
}
}
private static int askChoice(String question, String... choices) {
System.out.println(question);
int num=1;
for(String choice : choices)
System.out.println(num++ + " - " + choice);
try{
int choice = scan.nextInt()-1;
if (choice>=0 && choice < choices.length){
scan.nextLine();
return choice;
}
else
throw new InputMismatchException();
}catch(InputMismatchException ime){
scan.nextLine();
return askChoice(question, choices);
}
}
private static void menu() {
System.out.println("Que souhaitez-vous faire ?");
System.out.println("\t* : afficher tout le répertoire");
System.out.println("\t+ : ajouter une entrée");
System.out.println("\t- : supprimer une entrée");
System.out.println("\t? : consulter les coordonnées téléphoniques d'un correspondant particulier");
System.out.println("\tq : quitter le programme");
chooseAction();
}
private static void chooseAction() {
String input = scan.nextLine();
switch(input){
case "*": System.out.println(phoneBook); break;
case "+": addEntry(); break;
case "-": removeEntry(); break;
case "?": getNumber(); break;
case "q": scan.close(); System.exit(0); break;
default : break;
}
menu();
}
private static void getNumber() {
String name = askName();
System.out.println(phoneBook.getNumber(name));
}
private static String askName(){
System.out.println("Entrez un nom ");
return scan.nextLine();
}
private static void removeEntry() {
String name = askName();
phoneBook.remove(name);
}
private static void addEntry() {
int area, sector, one, two, three;
String name = askName();
System.out.println("Entrez un indicatif");
int country = askCountryCode();
System.out.println("Entrez un numéro sous la forme xx.xx.xx.xx.xx");
scan.useDelimiter("\\.|\n");
area = scan.nextInt();
sector = scan.nextInt();
one = scan.nextInt();
two = scan.nextInt();
three = scan.nextInt();
phoneBook.put(name,new PhoneNumber(country, area, sector, one, two, three));
scan.nextLine();
scan.reset();
}
}
package devoo.tp07;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
/**
* @author Antoine Nongaillard, Fabien Delecroix
* Répertoire téléphonique permettant la gestion de numéros de téléphone de correspondants.
*/
public class PhoneBook {
//implements pour version corrigée uniquement, TODO : retirer dans sujet
/** Le répertoire sous la forme d'une table associative */
protected Map<String, PhoneNumber> directory;
/**
* Constructeur pour instancier un répertoire vide.
*/
public PhoneBook() {
this.directory = new HashMap<>();
}
/**
* Ajoute un correspondant et son numéro de téléphone
* @param label le libellé du correspondant à ajouter
* @param tel le numéro de téléphone du correspondant à ajouter
*/
public void put(String label, PhoneNumber tel) {
this.directory.put(label, tel);
}
/**
* Supprime un correspondant donné
* @param label le libellé du correspondant à supprimer
*/
public void remove(String label) {
this.directory.remove(label);
}
/**
* Donne le numéro de téléphone d'un correspondant donné à partir de son nom
* @param label le libellé du correspondant
* @return le numéro de téléphone du correspondant
*/
public PhoneNumber getNumber(String label) {
return this.directory.get(label);
}
@Override
public String toString() {
StringBuilder res = new StringBuilder();
final String NL = System.getProperty("line.separator");
for(Map.Entry<String, PhoneNumber> e: this.directory.entrySet()) {
res.append(e.getKey() + " : " + e.getValue() + NL);
}
return res.toString();
}
//pour corrigé uniquement, TODO : retirer dans sujet
public void update(PhoneNumber oldNumber, PhoneNumber newNumber) {
for (Entry<String, PhoneNumber> entry : this.directory.entrySet()){
if (entry.getValue().equals(oldNumber)){
entry.setValue(newNumber);
}
}
}
}
\ No newline at end of file
package devoo.tp07;
import java.util.Arrays;
/**
* @author Antoine Nongaillard, Fabien Delecroix
* Un {@literal}{PhoneNumber} contient les informations d'un numéro de téléphone en incluant son indicatif de pays.
* Les méthodes fournies permettent son affichage au format standard ou international
*/
public class PhoneNumber{
/** Code du pays, par exemple 33 pour la France */
protected final int countryCode;
/** Code de la zone, par exemple 3 pour les hauts de France */
protected final int areaCode;
/** Code du secteur, par exemple 20 pour Lille et alentours*/
protected final int sectorCode;
/** Trois derniers duos de chiffres */
protected final int[] lastOnes = new int[3];
/** Code indicatif de pays pour la France */
public static final int FRANCE_CODE = 33;
private static int countryCodeReference = FRANCE_CODE;
/**
* Permet de modifier l'indicatif de pays duquel dépend le format d'affichage des numéros
* @param countryCode indicatif de pays souhaité
* @see #toString()
*/
public static void setCountryCodeReference(int countryCode) {
PhoneNumber.countryCodeReference = countryCode;
}
/**
* Crée un numéro de téléphone initialisé avec les nombres fournis.
* Dans le cas où l'un des paramètres commence par un zéro, on pourra aussi bien l'entrer ou non (par ex. 06 ou 6)
* @param country l'indicatif du pays
* @param area l'indicatif de zone géographique
* @param sector l'indicatif de secteur
* @param one le premier des 3 duos de chiffre finaux
* @param two le deuxième des 3 duos de chiffre finaux
* @param three le dernier des 3 duos de chiffre finaux
*/
public PhoneNumber(int country, int area, int sector, int one, int two, int three) {
this.countryCode = country;
this.areaCode = area;
this.sectorCode = sector;
this.lastOnes[0] = one;
this.lastOnes[1] = two;
this.lastOnes[2] = three;
}
/**
* Retourne le numéro de téléphone sous forme standard : {@code}{xx.xx.xx.xx.xx}
* @return le numéro de téléphone sous forme standard : {@code}{xx.xx.xx.xx.xx}
*/
public String standardFormat() {
StringBuilder res = new StringBuilder();
res.append('0');
res.append(this.areaCode);
res.append('.');
if(this.sectorCode<10) res.append('0');
res.append(this.sectorCode);
for (int num : lastOnes){
res.append('.');
if(num<10) res.append('0');
res.append(num);
}
return res.toString();
}
/**
* Retourne le numéro sous forme international : {@code}{+xx.x.xx.xx.xx.xx}
* @return le numéro sous forme international : {@code}{+xx.x.xx.xx.xx.xx}
*/
public String internationalFormat() {
StringBuilder res = new StringBuilder();
res.append('+');
res.append(this.countryCode);
res.append('.');
res.append(this.standardFormat().substring(1));
return res.toString();
}
/**
* Retourne la représentation sous forme d'une String du numéro de téléphone dans son format standard ou international selon
* que l'indicatif de pays du numéro corresponde à celui de référence ou non.
* @return la représentation sous forme d'une String du numéro de téléphone dans son format international.
* @see #internationalFormat()
* @see #standardFormat()
* @see #countryCodeReference
*/
public String toString() {
if (this.countryCode == PhoneNumber.countryCodeReference)
return standardFormat();
else
return internationalFormat();
}
//pour corrigé uniquement, TODO : retirer dans sujet
/**
* Compare le numéro de téléphone à l'objet donné
* @param obj l'objet avec lequel comparer ce {@code PhoneNumber}
* @return {@code true} si l'objet donné est un numéro de téléphone composé des mêmes indicatifs et numéros, {@code false} sinon
*/
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof PhoneNumber))
return false;
PhoneNumber other = (PhoneNumber) obj;
if (countryCode != other.countryCode)
return false;
if (areaCode != other.areaCode)
return false;
if (sectorCode != other.sectorCode)
return false;
if (!Arrays.equals(lastOnes, other.lastOnes))
return false;
return true;
}
//pour corrigé uniquement, TODO : retirer dans sujet
/**
* Retourne une valeur de hachage pour ce numéro de téléphone en se basant sur l'ensemble des informations qui le composent
* @return la valeur de hachage du numéro de téléphone
*/
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + countryCode;
result = prime * result + areaCode;
result = prime * result + sectorCode;
result = prime * result + Arrays.hashCode(lastOnes);
return result;
}
}
\ No newline at end of file
package devoo.tp07;
public class ULillePhoneNumber extends PhoneNumber{
public static final String U_LILLE_PREFIX = "3596";
public ULillePhoneNumber(String fiveDigits) {
super(PhoneNumber.FRANCE_CODE,
Integer.valueOf(U_LILLE_PREFIX.substring(1)),
Integer.valueOf(U_LILLE_PREFIX.substring(1, 3)),
Integer.valueOf(U_LILLE_PREFIX.substring(3, 4)+fiveDigits.substring(1, 1)),
Integer.valueOf(fiveDigits.substring(1, 3)),
Integer.valueOf(fiveDigits.substring(3, 5)));
}
}
\ No newline at end of file
package devoo.tp07;
import static org.junit.Assert.assertEquals;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class PhoneNumberTest {
private PhoneNumber nationalNumber;
@BeforeEach
public void setUp(){
nationalNumber = new PhoneNumber(33, 3, 59, 03, 21, 06);
}
@Test
public void testStandardFormat(){
assertEquals("03.59.03.21.06", nationalNumber.standardFormat());
}
@Test
public void testInternationalFormat(){
assertEquals("+33.3.59.03.21.06", nationalNumber.internationalFormat());
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment