Collection
Modification de notre classe Travel
Après avoir élaboré les classes pour structurer nos objets, nous allons maintenant explorer les structures de données avancées, notamment les collections. Les collections en Java constituent des outils puissants pour gérer et manipuler des ensembles de données de manière dynamique.
API Collection
Java met à disposition l'API Collection qui propose un ensemble d'interfaces et d'implémentations permettant de stocker des ensembles d'objets.
On y trouve 3 interfaces principales (avec les implémentations principales correspondantes) :
- List : ArrayList et LinkedList
- Set : HashSet, LinkedHashSet et TreeSet
- Queue : LinkedList (implémenté par List et Queue), ArrayDeque et PriorityQueue
Pour plus d'informations, vous pouvez consulter la documentation officielle de Java sur les collections.
Ajout d'une collection de participants
Actuellement, notre classe Travel
ne contient pas de collection de participants, allons en rajouter une.
Pour notre besoin, on veut qu'un utilisateur ne puisse pas s'inscrire deux fois (un ensemble sans répétitions, il faut donc mettre une collection qui interdise les doublons).
À vous de jouer !
- Essayez d'ajouter un nouvel attribut participants dans notre classe
Travel
.
Indice
Ici, participants est un Set d'utilisateurs Set<User>
, mais pour l'implémentation, nous utilisons
HashSet<User>
. Cela offre une certaine flexibilité, car si vous souhaitez changer l'implémentation
sous-jacente à l'avenir, par exemple, passer de HashSet à LinkedHashSet, vous pouvez le faire facilement sans modifier le reste du code qui utilise l'attribut participants.
Lorsqu'une classe utilise une autre classe comme type d'attribut, cela est appelé la composition. Cette
approche permet à notre classe Travel
de manipuler et de gérer ses utilisateurs User
de manière indépendante.
En programmation orientée objet, privilégier la composition plutôt que l'héritage permet une meilleure modularité, une réutilisabilité accrue du code et une gestion plus souple des dépendances entre les classes.
Nous sommes confrontés à un problème : avec une disponibilité fixe, en cas de désistement d'un voyageur déjà enregistré, nous perdons de la valeur, alors que nous pourrions bien instaurer une vérification et éventuellement inscrire le surplus d'utilisateurs dans une file d'attente.
À vous de jouer !
À la manière de l'ajout de la liste de participants au voyage, ajoutez un attribut à la classe Travel
qui
représenterait une collection de participants dans une file d'attente en choisissant la collection la plus
appropriée.
Quelle serait la collection à choisir ? Pourquoi ?
Ici, on pourrait très bien imaginer l'utilisation d'une Queue. Comme nous l'avons vu en point tech, une FIFO (first in, first out) permettrait de modéliser cette file d'attente et les méthodes proposées par l'API facilitent la modélisation de ce cas d'utilisation.
En effet, il suffit d'appeler la méthode poll()
sur la collection pour récupérer l'utilisateur suivant
dans la file d'attente et de l'ajouter dans la liste des participants.
Bug possible sans equals
et hashCode
Comme vu dans la partie POO, si vous ne surchargez pas vos méthodes equals
et hashCode
,
vous pouvez introduire des bugs.
Ajoutez ces deux user identiques dans votre Set
de participants
:
User sameUser1 = new User(0, "Same", "User", "sameuser@gmail.com");
User sameUser2 = new User(0, "Same", "User", "sameuser@gmail.com");
hashCode
et equals
de User
puis loggez les user de participants
, que remarquez-vous ?
Désormais, nous sommes en mesure de stocker les différents participants à notre Travel
. Il serait intéressant de pouvoir agir sur cette collection.
Nous vous proposons d'ajouter deux méthodes dans la classe Main
pour pouvoir inscrire et désinscrire un User
.
À vous de jouer !
Voici leur signature respective :
Les points à prendre en compte lorsque vous implémenterez ces deux méthodes :
Pour l'inscription :
- retourner true si l'inscription a réussi (donc dans le cas ou l'utilisateur est bien ajouté à la liste des participants).
- retourner false sinon
L'inscription ne doit réussir que si l'utilisateur n'est pas déjà inscrit dans le voyage !
Cette méthode aura la responsabilité de récupérer le voyage en question, vérifier la disponibilité de ce dernier et inscrire l'utilisateur dans la bonne file (participants ou file d'attente).
Pour la désinscription :
Nous ne mettrons pas de retour sur la méthode.
- La première garantie est qu'après un appel à cette méthode, l'utilisateur n'est pas dans la liste des participants : il est donc supprimé s'il est trouvé.
- La seconde garantie est que si un utilisateur est effectivement désinscrit du voyage et qu'il y a un/plusieurs utilisateur(s) en file d'attente, le premier utilisateur de cette file passe en liste d'inscription effective.
Super ! Nous pouvons maintenant inscrire autant d'utilisateurs que nous voulons pour notre voyage ! Par contre, remarquez que ça commence à faire beaucoup de logique dans Main.java
et le code manque de structure. Pour
remédier à cela, il serait temps de déplacer le code dans des classes dédiées, appelées services.
Services
L'objectif de cette partie est de réaliser la couche service de notre diagramme.
La couche service va contenir la logique métier de notre application. Elle utilise les DAOs pour agir sur la base de données et transmettra les données à la couche présentation.
Création du service Travel
À vous de jouer !
- Créez un package
service
qui comportera l'ensemble de nos services. - Créez la classe
TravelService
et déplacez les fonctionssubscribe
etunsubscribe
.
Maintenant que nous avons utilisé les collections, modifiez le tableau de discounts pour utiliser une List (Les collections sont plus aisées à manipuler que les tableaux).
- Créez une fonction
applyDiscounts
dans le service en reprenant le code déjà écrit dans le main.
Nous sommes confrontés à une lacune : la simple indication "true" ou "false" lors de l'inscription ne clarifie pas suffisamment la situation. Cela peut poser un problème, que ce soit pour nous (risque d'oublier l'implémentation) ou pour toute personne extérieure qui n'est pas familière avec le code. Ainsi, pour enlever cette ambiguïté, nous opterons pour le renvoi d'une exception afin de mieux communiquer.
À vous de jouer !
- Modifier
subscribe
pour qu'elle ne renvoie rien et qu'elle propage une Exception.
Pour utiliser le service, on doit faire comme suit :
...
// Instanciation du TravelService
TravelService travelService = new TravelService();
// Utilisation des fonctionnalités du service
travelService.subscribe(user, travel);
...
Cependant, en raison de la possibilité d'une exception, il est nécessaire d'encadrer cet appel avec un bloc try/catch pour la capturer et la gérer :
try {
travelService.subscribe(user, travel);
} catch (Exception e) {
System.out.println(e.getMessage());
}
Essayez votre code dans le Main
Ça y est ! Nos utilisateurs peuvent désormais s'inscrire aux différents voyages. En fonction de la disponibilité de chaque voyage, l'utilisateur est possiblement inscrit en file d'attente.
À vous de jouer !
- Inscrivez les utilisateurs à un voyage en passant par la méthode
subscribe
du serviceTravel
. - Variez la disponibilité d'un voyage et vérifiez que la file d'attente est bien remplie dans le cas où un voyage serait déjà plein.
- Désinscrivez un utilisateur et vérifiez que le premier utilisateur dans la file d'attente passe bien en participant.
Récapitulatif
Bravo ! Vous avez ajouté avec succès la fonctionnalité d'inscription à un voyage pour un utilisateur ! Si vous avez tout compris, vous pouvez passer au chapitre suivant !
N'oubliez pas de commit
votre travail !