Nous avons plusieurs options pour modifier notre architecture. Nous pouvons foncer tête baissée et faire des changements de façon erratique. Mais cette approche pose problème comme vous le savez car elle a conduit à nos problèmes actuels…
Intéressons-nous plutôt à une façon plus organisée pour incorporer des changements. Cette approche pas à pas s’appelle la refactorisation, ou refactoring en anglais.
Refactorisez du code existant
La refactorisation implique de faire des changements au code existant pour qu’il continue à faire la même chose ; sauf que le code sera de meilleure qualité.
Avant de nous pencher sur notre application de compagnie aérienne, voyons quelques exemples.
En voici un provenant d’un système médical sur lequel j’ai travaillé. Est-ce que vous arrivez à comprendre ce qu’il est censé faire ?
public void handlePatientVisit(Patient patient, PatientVisit patientVisitRecord) {
LocalDate today = LocalDate.now();
LocalDate dob = patient.getDateOfBirth();
Period period = Period.between(dob, today);
if (period.getYears() < 18) {
patientVisitRecord.segment[“5.1”].append(patient.parentInfo != null ?
patient.parentInfo : lookupNext(patient));
}
}
Pas si simple ! La logique est entièrement sur la même ligne, et le programmeur suppose que vous connaissez le format de l’objet patientInfo
(d’ailleurs, qu’est-ce que c’est que cet objet ?).
Voici une façon de refactoriser le code pour qu’il soit plus facile à comprendre. Voyez si vous arrivez à comprendre ce qu’il fait maintenant :
public void handlePatientVisitRefactored(Patient patient) {
if (isPatientAMinor(patient)) {
patientVisitRecord.addNextOfKinInfo(patient);
}
}
Tout d’abord, le code vérifie si le patient est mineur. Si c’est le cas, alors ajoutez une section pour les informations sur le membre de sa famille le plus proche ("next of kin" en anglais). Tous les détails sur le calcul de l’âge du patient, et sur les modifications de la section appropriée du dossier du patient ont été abstraits et placés en appels de fonction, à un haut niveau.
Attendez, où sont les autres parties ?
Bien entendu, les détails pour déterminer si un patient est mineur, et pour l’ajout du segment sur le parent le plus proche doivent exister. Mais nous allons les déplacer vers des fonctions qui ne remplissent que leur tâche uniquement. Regardons le calcul qui détermine si le patient est mineur :
private boolean isPatientAMinor(Patient patient) {
Period period = Period.between(patient.getDateOfBirth(), LocalDate.now());
return period.getYears() < 18;
}
Le code sert toujours la même fonctionnalité. Néanmoins, nous l’avons modifié pour qu’il soit plus compréhensible et maintenable. Il est aussi beaucoup plus facile à tester. Dans la version originale, nous ne pouvons pas tester indépendamment la partie du code portant sur le calcul de l’âge. Nous devrions passer un dossier patient, puis vérifier si une section nextOfKin
(parent le plus proche) a été ajoutée ou non. Dans la version refactorisée, nous pouvons tester la méthode de l’âge du mineur toute seule. La séparation en responsabilités uniques est cruciale.
Nous pouvons effectuer des changements similaires au niveau architectural également.
Refactorisez avec le Modèle-Vue-Contrôleur
Notre principale refactorisation architecturale sera la mise en place du pattern Modèle-Vue-Contrôleur.
L’objectif du MVC est d’obtenir des responsabilités très spécifiques pour chaque couche ou section de notre système. Nous diviserons le code en trois sections distinctes. L’une sera le modèle, l’autre sera la vue, et la troisième sera le contrôleur. Nous aurons aussi une couche de données qui gère la persistance des objets de modèle. Nous verrons les détails de chacun de ces éléments dans les chapitres suivants !
Ça a l’air de représenter beaucoup de travail. Pourquoi se fatiguer à mettre cela en place ?
Voici un exemple de bénéfices, issu de la vraie vie :
Il y a quelques années, j’ai écrit un programme de quiz de mathématiques. Il fonctionnait sur Palm Pilot (oui, c’était il y a longtemps). Au fil du temps, j’ai mis à jour ce programme pour qu’il fonctionne sur iPod, iPhone, sur les appareils Android, et même sur PC et Mac. Étant donné que j’avais suivi le MVC, il était facile de générer chaque version. Voici pourquoi.
Le modèle représente l’état de notre système. Dans mon jeu de quiz, l’un des objets du modèle était la question du quiz. Un élément doit savoir que nous demandons combien font 5 + 3, et la réponse est 8. Ceci était placé dans une classe
ProblemeDeMaths
.Le contrôleur s’assure que le cas d’usage est correctement séquencé. Dans le jeu de maths, quelque chose doit poser la question, puis attendre que l’utilisateur saisisse une réponse. C’est le travail du contrôleur. Une fois saisie, la réponse est vérifiée, et l’emoji adéquat — souriant ou non — est affiché.
Le noyau dur de l’application de maths, à savoir le modèle et le contrôleur, est resté le même. (D’accord, j’ai quand même dû réécrire le code du C++ en Objective-C, en Swift et en Java. Mais les classes étaient faciles à transposer.)
Enfin, la vue est responsable des interactions avec l’utilisateur — pour l’input et pour l’output. Dans mon jeu de quiz, la vue devait changer pour supporter les différents environnements, mais les changements étaient faciles à faire. Cependant, vu que j’avais abstrait cette couche du reste, il était facile de changer l’interface sans toucher au reste du code.
D’accord, je comprends, mais comment cela s’applique-t-il à notre application pour la compagnie aérienne ?
Voici quelques éléments sur lesquels nous allons nous concentrer :
De nombreux éléments devraient être des objets du modèle, mais sont condensés dans ces quatre objets : client, réservation, pilote et avion. Nous allons devoir les modifier pour être plus en accord avec les principes de conception SOLID.
Nous allons voir plusieurs opportunités d’introduire des contrôleurs dans notre application de charter. Ceci coupera la connexion directe entre les composants du modèle et de la vue, et nous permettra d’insérer de la logique business au milieu.
De plus, en parlant des composants de la vue, nous devrons transformer les composants Java Swing en élément qui affiche des pages HTML accessibles depuis des navigateurs ou des clients mobiles.
Heureusement, le framework Spring Boot, qui utilise le MVC, peut être utilisé pour nous aider à atteindre notre objectif. Commençons dès le chapitre suivant.
En résumé
La refactorisation consiste à remplacer le design existant par un design avec une meilleure architecture.
L’utilisation du MVC nous aide à séparer les responsabilités et à créer une application plus maintenable.