====== TD2 : qui est Responsable? ======
**Objectifs :** Travailler, sur de petits exemples, différents points d'un développement pragmatique.
==== ENONCE ====
**Le forum est maintenant composé de //canaux//.**
* Un administrateur demande à créer un forum en précisant le nom du forum. Si un forum avec ce nom existe déjà, il ne se passe rien.
* Un membre peut demander s'il existe un forum de nom donné.
* Un administrateur peut lister (Cela veut dire obtenir la liste des noms de forum, pas les afficher!) les noms des forums existants qu'il a créé.
* Un membre demande à créer un canal dans un forum en précisant le nom du canal. Si le canal existe déjà, il ne se passe rien. Par défaut un canal ne peut pas contenir plus de "MAX_MESSAGES", mais il est possible de modifier le nombre maximum de messages autorisés. ((Comment //gérez-vous// le dépassement du nombre de messages alors que rien de vous est dit dans ce cas?))
* Un membre demande à créer un canal de "brèves" (Messages) associé à un forum en précisant le nom du canal et la taille maximum des brèves, par exemple 140 caractères. Si un canal de même nom existe déjà, il ne se passe rien.
* Un producteur poste un message dans un forum et //éventuellement// vers un canal spécifique du forum, en précisant leurs noms (forum et canal) et le contenu du message. En l'absence de canal, le message est posté dans le //canal par défaut//.
* Un consommateur peut demander à lister((obtenir la liste des messages)) les messages qui se trouvent dans un canal sur un forum.
* Un consommateur peut demander à lister tous les messages qui se trouvent sur le forum, dans ce cas tous les messages dans tous les canaux lui sont retournés.
* Un canal ne peut pas être associé à plusieurs forums. Un forum peut évidemment avoir plusieurs canaux.
Pour vous aider vous pouvez, dans un premier temps, ne pas tenir compte du canal par défaut et n'en tenir compte qu'à la fin du TD en identifiant bien les impacts sur le modèle et sur le code, en particulier avec les responsabilités qui en résultent.
===== Responsabilités : Analyse et développement de la partie "Métier" =====
Les questions ci-après sont là pour vous guider dans le développement. Il n'est pas demandé d'y répondre par écrit.
Utilisez **SonarLint** pour vous aider à améliorer vos codes, [[https://mbf-iut.i3s.unice.fr/doku.php?id=2018_2019:s3:methodo:td:env1|vous l'avez installé en 1e semaine]] !
Pour les S3A :[[https://mbf-iut.i3s.unice.fr/doku.php?id=2018_2019:s3:concprogobjet:td:s3atd1#generation_des_codes_10_mn| Utiliser la génération de code.]]
A la fin de cette page, vous avez des "tuyaux" pour manipuler des listes et des HashMap
**Dans ce qui suit //responsable// est utilisé au sens de GRASP.**
- Quels modèles utilisez-vous pour analyser le problème ?
- Dans vos diagrammes de séquences, faîtes bien apparaître les interfaces graphiques et les contrôleurs mais dans les diagrammes de classe concentrez vous uniquement sur la partie métier dans un premier temps. Cela ne signifie pas que vous devez modéliser tous les diagrammes de séquences.
- Qui est responsable de retrouver à partir de son nom un forum? un canal de messages? (Pattern expert)
- Qui est responsable de créer un forum? un canal de message? pourquoi? (Pattern créateur)
- Qui est responsable de créer un message ? pourquoi? (Pattern créateur)
- Avez-vous des relations bi-directionnelles? Si oui, avez-vous décidé de qui est le "maître" et de qui est l'"esclave"?
- Avez-vous bien prévu les tests unitaires? Que se passe-t-il si vous demandez
- à lire les messages sur un canal qui n'existe pas?
- à créer un forum d'un nom déjà utilisé?
- ...
- A partir de maintenant, on s'intéresse au scénario suivant, sans tenir compte de l'interface graphique. Est-ce que vous pouvez l'implémenter, revenez sur votre code si besoin (Vous pouvez vous aider des tuyaux donnés plus bas si besoin):
- //créer un forum// "NiceInformation" pour l'administrateur //Jean-Pierre//
- //créer une canal// "@soirees" sur le forum "NiceInformation"
- //créer un canal de Breves// "@dept-info-iut-Nice" qui autorise uniquement des messages de 140 caractères.
- //poster un message// "Siesta" dans le canal "@soirees" du forum "NiceInformation"
- //poster un message// "ShadowBar" dans le canal "@soirees" du forum "NiceInformation"
- //poster un message// "ShadowBar" dans le canal "@soirees" du forum pareil pour le forum "AntibesInformation", ça passe?
- //lire les messages// qui se trouvent //dans le canal// "@soirees", i.e. récupèrer les messages.
- //effacer les messages du canal// "@soirees" du forum "NiceInformation"
- //poster un message// "SoireeIUT" sur le forum "NiceInformation" (donc sans préciser le canal)
- //poster un message// "WEI ... apres les cours de ..." dans le canal "@dept-info-iut-Nice"
- Yoann plus bavard //poste un message de plus de 140 caractères// " N'oubliez pas d'emmener ...... et de choisir votre filleul ....." dans le canal "@dept-info-iut-Nice"... que se passe-t-il ?
- Attendre 2s (Thread.sleep(2000));
- Poster un message "SoireeIntegrationIUT Choisissez vos parrain...." sur le forum "NiceInformation"
- Attendre 2s (Thread.sleep(2000));
- Effacer tous les messages postés depuis plus de 4s sur le forum "NiceInformation"
- Fixer MAX_MESSAGES à 1... comment se comporte votre programme?
- Complétez les tests pour prendre en compte les "brèves".
- Comment avez-vous géré le canal par défaut? (Avez-vous pensé à définir des constantes?)
- Pouvez-vous décrire chacune de vos classes avec une seule phrase? Faîtes-le dans les commentaires de vos classes.
Aie aie votre "Product Owner" vous annonce que sur des forums de type "Community" il veut interdire d'avoir plus de 2 canaux, sur un forum "Premium" on ne peut pas en avoir plus de 5 et sur les forum "PRO" le nombre de canaux n'est pas limité. En plus, **il veut savoir quel membre a créé un canal**.\\
Comment réagissez vous à ce changement? Modifiez vos codes et modèles pour tenir compte de ces nouvelles fonctionnalités.
===== Architecture : Interface graphique et contrôleur =====
L'objectif est ici de décomposer l'application pour que :
* Le **programme principal** consiste à créer un contrôleur et à le démarrer.
public static void main(String[] args) throws Exception {
Controleur c = new Controleur();
c.start();
}
* L'**interface** n'interagit jamais directement avec le métier.
* Elle ne connait pas non plus le contrôleur dans cet exercice.
* Voici{{:2015_2016:s3:concprogobjet:td:userconsole.java| un exemple de code pour l'interface}}. Pour se concentrer sur l'architecture, l'interface est une classe Java qui utilise ''Scanner'' pour lire au clavier et ''System.out.print...'' pour l'affichage.
* Le **contrôleur** joue le rôle de chef d'orchestre entre le métier et l'interface.
* Il ne peut pas utiliser System.out...; tout affichage passe par l'interface.
* Il n'a pas le droit de modifier la partie métier sans passer par des objets métiers.
* Il n'est pas autorisé à stocker la liste des bus etc. Pensez à utiliser le principe de délégation.
* Voici des extraits de code pour vous aider ci-après.
public class Controleur {
private Gestionnaire registre = new Gestionnaire();
private UserConsole ui = new UserConsole();
public void start() throws Exception {
String commande = ui.lireCommande();
switch (commande.charAt(0)) {
case UserConsole.COM_CREER_FORUM:
creerForum();
start();
break;
case UserConsole.COM_CREER_CANAL :
creerCanal();
start();
break;
case UserConsole.COM_POSTER_MESSAGES:
posterMessage();
start();
break;
case UserConsole.COM_LIRE_MESSAGES:
lireMessage();
start();
break;
case UserConsole.COM_STOP:
ui.afficher("Au revoir ");
Memoire.save(registre, "sauvegarde.txt");
exit(0);
break;
default :
ui.afficher("La commande spécifiée n'existe pas\n");
sleep(1000);
start();
}}
- Développez le code correspondant.
- Quel type de contrôleur avez-vous défini en respectant les directives données?
- Vous devez à présent pouvoir jouer le scénario précédent en utilisant votre interface graphique.
- Visualisez votre code correspondant à la création d'un forum (et uniquement cela) sous la forme d'un diagramme de séquences.\\
- Pouvez-vous décrire chacune de vos classes avec une seule phrase? Faîtes-le dans les commentaires de vos classes.
===== Architecture : Persistance simplifiée =====
On désire sauvegarder l'ensemble des forums à chaque fin d'exécution du programme principal et recharger l'état des forums à chaque lancement du programme principal.
Vous pouvez utiliser {{:2018_2019:s3:concprogobjet:memoire.java|le code suivant}}. Pour l'utiliser il suffit que vos classes (gestionnaire, Message, Forum, ...) **implements Serializable**.
**Exemple de sauvegarde :**
Memoire.save(registre, NomFichier);
**Exemple de lecture :**
Object o = Memoire.read(NomFichier);
if (o instanceof Gestionnaire){
registre = (Gestionnaire) o;}
}
===== Evolutions =====
- Dans certains canaux dès qu'un message est lu, il est effacé. Modifier votre code pour permettre la création de tels canaux. Attention, les autres canaux sont toujours utilisables. Utilisez bien les tests unitaires, pour vérifier que vous ne "cassez" pas vos codes. Comment votre modèle est-il modifié?
- Pour certains forums, lorsque l'on poste un message en donnant comme nom de canal "*", le message est posté dans tous les canaux. Modifier votre code pour tenir compte de ce nouveau besoin.
- Analyser les dépendances entre vos classes.
==== Tuyaux ====
Vous avez besoin dans ce TD de manipuler des collections et des dates, voici quelques tuyaux que vous pourriez retrouver sur le Web et qui sont extraits des codes que nous avons mis en oeuvre pour ce TD. Vous pouvez évidemment avoir d'autres solutions :
=== Pour manipuler des HashMap ===
HashMap canaux = new HashMap<>();
//Ajout d'un object dans la hasmap
canaux.put(c.getNom(), c);
ArrayList messageList = new ArrayList<>();
// obtenir les objets qui se trouvent dans la hashmap (.values) et les parcourir (for)
for (Canal c : canaux.values())
messageList.addAll(c.getMessages());
// retrouver un objet de nom donné
Canal c = canaux.get(nom);
//Obtenir toutes les clefs (ici le nom des canaux)
Set noms = canaux.keySet();
=== Pour manipuler des collections ===
* Pour effacer un element dans une liste si une condition est vérifiée :
public void detruireMessagePerime(int dureeEnSecondes) {
messageList.removeIf(message -> message.estPerime(dureeEnSecondes));
}
=== Pour gérer les dates ===
* Une manière "facile" pour savoir si un message est périmé de plus de x secondes (utilisation de ''java.util.Date''):
public boolean estPerime(int second){
Date d = new Date();
long ms = d.getTime();
ms = ms - second*1000;
d.setTime(ms);
return this.dateEmission.before(d);
}