Une application est composée de nombreux éléments qui peuvent changer : les données évoluent, des méthodes sont appelées, certaines interfaces visuelles sont mises à jour…
Il nous faut un moyen de suivre toute cette activité ! Nous avons besoin de coordination. Nous plaçons donc cette coordination dans un objet du contrôleur.
Précédemment, nous avons déconnecté les objets du modèle de la vue. Puis, nous avons séparé les objets du modèle de la couche de données. Nous avons une couche de plus à retirer. Ensuite, nous recollerons tout cela ensemble. Le contrôleur fera office de colle !
Nous utiliserons la même approche que dans les chapitres précédents.
Examinez l’architecture et le code existants pour déterminer où se trouvent les problèmes.
Identifiez les solutions.
Appliquez les solutions.
Examinez les problèmes posés par l’architecture existante
Actuellement, nous avons séparé les couches de la vue et du modèle, et nous avons introduit la couche de données :
Néanmoins, le contrôle (c’est-à-dire ce qui se produit quand l’utilisateur clique sur des éléments de l’interface utilisateur) est toujours imbriqué dans les classes Java Swing. Nous n’avons pas copié ce fichier dans notre nouveau projet, mais regardons ce qu’il faisait avant. Pour chaque bouton, la logique de ce qu’il devait faire était enfouie à l’intérieur de son gestionnaire d’événements individuel :
JButton btn = new JButton("Clients");
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Client.fillTableWithData(table);
}
});
De plus, comme nous l’avons vu précédemment, nous ne pouvons pas tester cette logique de manière isolée. Et si l’action à effectuer change, ce code sera difficile à modifier.
Identifiez des solutions
Nous devons plutôt nous assurer que la classe contrôleur s’occupera de la coordination entre la mise à jour des objets du modèle, la mise à jour de la source de données correspondante, et la mise à jour de la vue :
Pour faire cela, nous devons :
Rompre toutes les connexions directes entre nos classes Java Swing et la logique business qui gère les événements.
Créer des classes du contrôleur pour chaque écran, intégrées dans le framework Spring Boot.
Appliquez notre solution
Lorsque nous sommes dans une situation complexe, telle que l’assemblage d’un meuble en kit, nous avons besoin de savoir à quelle étape nous en sommes, et quel événement est attendu ensuite. Les contrôleurs doivent suivre l’état de l’application. Pour rester simple, notre application permettra aux utilisateurs de voir, modifier, ajouter et supprimer des éléments de la liste de clients, réservations, et problèmes de maintenance.
Tout d’abord, intéressons-nous à un cas d’usage concret : l’ajout d’une réservation dans le système.
En tant qu’opérateur d’agence de voyage, je veux réserver un vol pouvant transporter jusqu’à 12 personnes, pour qu’elles puissent découvrir un lieu unique.
Nous avons déjà une classe Reservation
, qui a déjà toutes les annotations Spring Boot pertinentes, et une classe ReservationRepository
qui gère toutes les opérations CRUD.
Nous devons ajouter un ensemble d’écrans d’interface utilisateur qui nous permettent d’effectuer les opérations CRUD. Puis, nous devons ajouter une classe ReservationController qui connectera tous les éléments de réservation.
Dans le projet GitHub, vous verrez un ensemble de fichiers HTML basés sur la réservation (add-reservation.html, reservations.html, et update-reservations.html). Ils sont configurés pour que le framework Spring Boot remplisse les données automatiquement. Cela semble compliqué, mais cela crée un tableau de données de réservation, avec les boutons pour ajouter, supprimer et modifier les réservations existantes. Les boutons indiquent quelle page correspondante doit être la prochaine à apparaître.
Nous avons maintenant besoin de la classe contrôleur, qui détermine ce qui se passe lorsqu’une page précise est requêtée. Une grande partie du code est faite pour récupérer un ensemble de données depuis la couche de données, puis répondre aux requêtes de Spring Boot sur les données à afficher.
Tout d’abord, nous devons créer un paquet pour contenir nos contrôleurs. Créez ce qui suit dans le projet : com.airbusiness.airbusinessmvc.controllers
. Puis ajoutez la classe ReservationController
au paquet.
package com.airbusiness.mvc.controllers;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import com.airbusiness.airbusinessmvc.entities.Reservation;
import com.airbusiness.airbusinessmvc.repositories.ReservationRepository;
@Controller
public class ReservationController {
private final ReservationRepository reservationRepository;
@Autowired
public ReservationController(ReservationRepository reservationRepository) {
this.reservationRepository = reservationRepository;
}
@GetMapping("/new-trip")
public String showNewTripForm(Reservation user) {
return "add-reservation";
}
@PostMapping("/reservation/add")
public String addReservation(@Valid Reservation reservation, BindingResult result, Model model) {
if (result.hasErrors()) {
return "add-reservation";
}
reservationRepository.save(reservation);
model.addAttribute("reservations", reservationRepository.findAll());
return "reservations";
}
@GetMapping("/reservation/edit/{id}")
public String showUpdateReservationForm(@PathVariable("id") long id, Model model) {
Reservation reservation = reservationRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("Invalid reservation Id:" + id));
model.addAttribute("reservation", reservation);
return "update-reservation";
}
@PostMapping("/reservation/update/{id}")
public String updateReservation(@PathVariable("id") long id, @Valid Reservation reservation, BindingResult result, Model model) {
if (result.hasErrors()) {
reservation.setId(id);
return "update-reservation";
}
reservationRepository.save(reservation);
model.addAttribute("reservations", reservationRepository.findAll());
return "reservations";
}
@GetMapping("/reservation/delete/{id}")
public String deleteReservation(@PathVariable("id") long id, Model model) {
Reservation reservation = reservationRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("Invalid reservation Id:" + id));
reservationRepository.delete(reservation);
model.addAttribute("reservations", reservationRepository.findAll());
return "reservations";
}
@RequestMapping("/reservations")
public String reservationsForm(Model model) {
model.addAttribute("reservations", reservationRepository.findAll());
return "reservations";
}
}
Regardons cela de plus près. Voici l’une des annotations clés :
@Autowired
public ReservationController(ReservationRepository reservationRepository) {
this.reservationRepository = reservationRepository;
}
Voici comment Spring Boot connectera les données (réservation) appropriées à ce contrôleur :
@GetMapping("/some-page-name")
est une autre annotation clé, qui connecte un événement (un clic sur un bouton) à un “gestionnaire” (handler) (une méthode) dans le contrôleur. Le contrôleur interroge ensuite la source de données (si nécessaire) et prépare les données, revenant vers Spring Boot. La page correspondante est ensuite affichée.
À vous de jouer !
Pour vous lancer un défi, apportez une série de modifications similaires à la zone client de l’application en ajoutant différents fichiers : client.html, add-client.html, update-client.html et un fichier ClientController.java.
Et maintenant, ajoutons un peu plus de fonctionnalités à notre contrôleur. Souvenez-vous de notre dernière user story de la première partie :
En tant que responsable financier, je veux voir une liste des clients qui nous doivent de l’argent, afin de pouvoir leur faire un rappel téléphonique.
Avec tout le travail d’architecture que nous avons déjà accompli, nous pouvons facilement ajouter un bouton qui montre les clients qui ont des impayés.
Premièrement, ajoutez un bouton à la page clients.html :
<a href="/clients/owe" class="btn btn-primary">Owe</i></a></p>
Puis, ajoutez une requête à notre ClientRepository :
@Query("SELECT c FROM Client c WHERE c.outstandingBalance > ?1")
List<Client> findByOustandingBalanceGreaterThan(double value);
Et enfin, un gestionnaire de page à notre classe ClientController :
@RequestMapping("/clients/owe")
public String clientOwe( Model model) {
model.addAttribute("clients", clientRepository.findByOustandingBalanceGreaterThan(0.0));
return "clients";
}
Revenons à nos cas d’usage principaux pour notre application de compagnie aérienne. Vous vous souvenez que deux des nouveaux cas d’usage que nous traitons sont : (1) l’ajout d’un problème de maintenance par le pilote :
En tant que pilote, je veux saisir tout problème de maintenance mineur dès l’atterrissage, pour qu’il puisse être réparé rapidement.
Et (2), la mise à jour du problème par un mécanicien :
En tant que mécanicien en charge de la maintenance de l’avion, je veux mettre à jour tout problème de maintenance dès qu’il a été traité, afin que l’avion puisse être considéré comme apte à voler.
Décomposons leur fonctionnement :
Alors, premièrement, le pilote saisit la date de détection, la zone qui pose problème, et une description.
Plus tard, un mécanicien résout le problème.
Il entre ensuite dans le système, change la date de réparation, et ajoute des informations complémentaires sur le problème. Le contrôleur de problème de maintenance affiche les écrans appropriés et valide les champs.
En vous basant sur cette description et sur le travail que nous avons déjà accompli, pouvez-vous essayer d’implémenter ces éléments dans notre application ?
Essayez par vous-même !
Et maintenant, il est temps pour vous d’ajouter également les différents fichiers maintenance.html (vue), MaintenanceIssue.java (modèle), MaintenanceRepository.java (couche de données) et MaintenanceController.java (contrôleur) !
Vous remarquerez que nous avons ajouté du CSS et une méthode au contrôleur pour la page d’accueil !
En résumé
Le contrôleur lie les couches de vue, de modèle, et de données les unes aux autres.
Créez des classes contrôleur pour chaque écran.
Utilisez un framework MVC pour vous simplifier le travail.
Maintenant que vous avez séparé les couches de votre application avec le MVC, vous pouvez tester vos connaissances avec le quiz !