• 8 hours
  • Medium

Free online content available in this course.

course.header.alt.is_video

course.header.alt.is_certifying

Got it!

Last updated on 8/20/24

Extrayez le Modèle

Est-ce que vous êtes en train de conduire ? Aïe aïe aïe, j’espère que non ! 😱 Mais pour répondre à ma prochaine question, imaginez que vous êtes en train de conduire…

Vous y êtes ? Alors, à quels éléments faites-vous attention ? Probablement votre vitesse, la quantité d’essence dans votre réservoir, la proximité des voitures autour de vous, ou encore les panneaux de signalisation…

Tous ces éléments constituent l’état de votre système de conduite actuel. Nous qualifions toutes ces idées qui représentent l’état de “modèle”. Celui-ci correspond à tous les éléments qui sont vus ou manipulés. Nous voulons conserver toutes ces idées dans leur propre couche d’entités (tout comme nous avons séparé la couche de vue).

D’où viennent les objets du modèle ?

Vous vous souvenez de tout ce temps que nous avons passé à déterminer ce qu’étaient les entités en lisant toutes les descriptions de cas d’usage ? Ce seront les classes qui appartiennent au modèle.

Nous suivrons une série d’étapes similaires à celles que nous avons effectuées pour la vue pour extraire le modèle :

  • Examiner l’architecture et le code existants pour voir où se situent les problèmes.

  • Définir nos solutions.

  • Appliquer nos solutions.

Allons-y !

Examinez les problèmes de l’architecture actuelle

Dans l'application pour notre compagnie aérienne, le modèle est contenu dans les classes Java que nous avons vues dans notre premier chapitre :

Le diagramme représente l'architecture avec la vue extraite. On voit que les classes Réservation et Client sont reliées à la base de données SQLite mais pas à la couche HTML.
Le modèle dans notre architecture

Néanmoins, ces classes ont quelques responsabilités supplémentaires, qui vont au-delà du fait d’être des classes de modèle. Par exemple, client, réservation, pilote, et avion créent tous leurs propres objets de connexion SQL, puis exécutent leurs propres requêtes. Et, bien que les objets du modèle aient généralement besoin d’être persistés, ce n’est pas le travail des classes du modèle de s’en charger elles-mêmes. La responsabilité du stockage de données enfreint le S des principes de conception SOLID. Nous devons y remédier !!

Par exemple, regardons la classe client de plus près. Elle a des valeurs de données pour contenir un prénom et un nom de famille, une adresse, un numéro de téléphone, une liste de réservations, et des impayés.

Toutes ces informations sont essentielles. Si nous n’éteignions jamais notre appareil, un objet client pourrait vivre éternellement en mémoire. Cependant, ce n’est pas une attente raisonnable. Nous devons sauvegarder cela (et d’autres objets du modèle) dans un mécanisme de persistance. Habituellement, il s’agit d’une base de données. Et il semblerait que c’est SQLite qui est utilisé pour l’application existante.

Voici du code trouvé dans Client.java qui gère son propre stockage de données :

    private static DefaultTableModel buildTableModel() {
        ResultSet rs = AirBusinessDb.getResultSet("SELECT * from clients);
        try {
            // details removed
           while (rs.next()) {
                // processing this record
           }
           return new DefaultTableModel(data, columnNames);
        } catch (SQLException e) {
            e.printStackTrace();
        }
       return null;
   }

Encore une fois, c’est une classe qui est difficile à modifier, tester, et maintenir. Si le type de stockage de données est modifié, alors tout cela doit être modifié. L’infrastructure de stockage des données doit exister (ou au moins être simulée) pour tester un objet client. Voilà qui représente plus de travail que nécessaire pour changer ou tester la classe.

Définissez des solutions

D’un point de vue architectural, nous voulons que la persistance constitue une couche de données indépendante. Nous voulons couper toute connexion directe entre nos objets du modèle (la couche d’entités) et la façon dont ils sont sauvegardés ou récupérés dans une source de données (la couche de données). Mettons à jour le diagramme de classe UML pour couper la connexion entre le client et SQLite :

Le diagramme représente l'architecture pour laquelle on a coupé les connexions. On voit que les classes Réservation et Client ne sont plus reliées à la couche de données, ni à la couche HTML.
Coupez les connexions dans notre architecture

Très bien, nous avons coupé toutes les connexions directes entre nos classes Java (les classes du modèle qui composent notre couche d’entités) et la base de données SQLite (la couche de données). Néanmoins, pour que cela fonctionne, nous devons :

  • Modifier les classes existantes du modèle pour qu’elles puissent supporter l’intégration dans le framework Spring Boot.

  • Faire persister nos objets du modèle.

Appliquez les solutions

Heureusement, Spring Boot fournit un mécanisme pour faire cela correctement. On peut le faire en seulement quelques étapes.

  1. Étape 1 : Modifiez le fichier POM.xml existant de l’application Spring Boot pour inclure une couche de persistance.

  2. Étape 2 : Copiez les classes du modèle (client, réservation, pilote et avion) dans l’application Spring Boot. Dans celles-ci, nous allons :

    • Retirer les appels vers la base de données SQLite.

    • Ajouter des annotations aux variables membres de nos classes. Spring Boot est capable de reconnaître ces annotations comme éléments ayant besoin d’être persistés.

    • Ajouter des getters et des setters pour chacune des variables membres s’il n’en existe pas.

  3. Étape 3 : Ajoutez une classe Repository correspondante.

Allons-y.

Étape 1

Voici les dépendances à ajouter au fichier pom.xml

<dependency>
    <groupId>org.springframework.boot</groupId>
   	<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>

Étape 2

Voici un exemple de la classe client modifiée. Elle va dans un nouveau paquet :

com.airbusiness.airbusinessmvc.entities;

package com.airbusiness.airbusinessmvc.entities;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.validation.constraints.NotBlank;

@Entity   // Added Data Storage Annotation
public class Client {
   @Id   // Added Data Storage Annotation
   @GeneratedValue(strategy = GenerationType.AUTO)  // Added Data Storage Annotation
   private long id;
   
   @NotBlank(message = "First Name is mandatory")  // Added Data Storage Annotation
   private String firstName;
   
   @NotBlank(message = "Last Name is mandatory")  // Added Data Storage Annotation
   private String lastName;
   private String address;
   private String telephone;
   private double outstandingBalance;
   
   // Added Data Storage Getter and Setter
   public void setId(long id) {
       this.id = id;
   }
   public long getId() {
       return id;
   }
   
   // Added Data Storage Getter and Setter
   public String getFirstName() {
       return firstName;
   }
   public void setFirstName(String firstName) {
       this.firstName = firstName;
   }
   
   // Added Data Storage Getter and Setter
   public String getLastName() {
       return lastName;
   }
   public void setLastName(String lastName) {
       this.lastName = lastName;
   }
   
   // Added Data Storage Getter and Setter
   public String getAddress() {
       return address;
   }
   public void setAddress(String address) {
       this.address = address;
   }
   
   // Added Data Storage Getter and Setter
   public String getTelephone() {
       return telephone;
   }
   public void setTelephone(String telephone) {
       this.telephone = telephone;
   }
   
   // Added Data Storage Getter and Setter
   public double getOutstandingBalance() {
       return outstandingBalance;
   }
   public void setOutstandingBalance(double outstandingBalance) {
       this.outstandingBalance = outstandingBalance;
   }
}

Étape 3

Enfin, nous devons introduire une classe repository qui connecte les clients à une base de données (que nous n’avons pas encore choisie). Voici cette classe pour client :

@Repository
public interface ClientRepository extends CrudRepository<Client, Long>  {
}

Eh oui, c’est tout ce qu’il nous faut ! Spring Boot nous a automatiquement créé toutes les opérations CRUD.

À vous de jouer

 

Pour vous entraîner à ce que nous venons de faire, lancez-vous et apportez des modifications similaires à d’autres classes du modèle (réservation, pilote, avion). La clé ici est de déterminer quelles parties des classes Java existantes sont en effet des parties du modèle (état), et quelles parties doivent aller ailleurs. Ensuite, refactorisez à nouveau le code pour que les objets du modèle se reposent sur la couche de persistance pour réaliser le travail de persistance.

Nous avons mené à bien la deuxième série d’étapes de notre modification MVC. Nous avons identifié le code qui sera difficile à modifier, tester, ou maintenir. Nous avons introduit les couches de modèle et de persistance. Et nous avons tiré profit du framework Spring Boot pour nous simplifier le travail.

En résumé

  • Le modèle contient l’état de l’application.

    • Le modèle est composé d’entités.

    • Le stockage d’informations du modèle doit être abstrait du reste.

  • Extrayez le modèle en introduisant des couches de modèle et de persistance.

  • Utilisez un framework MVC pour vous simplifier le travail.

Maintenant que nous avons extrait notre modèle, nous sommes prêts à travailler sur notre contrôleur  !

Example of certificate of achievement
Example of certificate of achievement