:: Enseignements :: Licence :: L3 :: 2016-2017 :: Programmation Objet avec Java ::
![[LOGO]](http://igm.univ-mlv.fr/ens/resources/mlv.png) |
Monad or not Monad ?
|
On s'intéresse à la partie validation d'une application web.
La validation est l'étape qui consiste à vérifier que les données saisies par l'utilisateur
sont valides (que le champ 'mot de passe' a la bonne longueur, que le champ 'nom' n'est pas vide, etc...).
On cherchera, dans un premier temps, à reporter une erreur dès qu'un champ est invalide. Puis
dans un second temps on récupérera les différents messages d'erreur de champs invalides
pour afficher un message à l'utilisateur indiquant toutes les erreurs de saisie.
Pour la suite, on pré-supposera que le code tourne sur un serveur d'applications et que lorsque
l'utilisateur entre les différentes informations pour s’inscrire sur le site web, ces informations sont décodées par le serveur web et stockées dans un objet
SignInForm
dont le code est ci-dessous.
De plus, un de vos collègues (on va dire pas très réveillé) a écrit le code de validation suivant:
Exercice 1 - Validation
Sachant qu'il n'y a pas que ce formulaire sur le site web (il y a, par exemple, d'autres formulaires
qui requièrent aussi la validation d'email), le chef de projet décide qu'il serait bon
de factoriser un peu le code de la méthode validateForm en sortant les différentes
validations sous forme de méthodes statiques (validEmail, validPassword, ...) dans une classe Validation dans le package fr.umlv.validation.
-
Créer la classe Validation et modifier le code de validateForm
pour utiliser les méthodes de validation.
Note: on remarquera que tester si une chaîne n'est pas vide est équivalent à tester si
sa longueur est supérieure à zéro.
-
Modifier le code de sorte à ce qu'une seule exception soit levée, quel que soit le nombre d'erreurs.
Exercice 2 - Validation 2
Le directeur technique passant dans le coin, celui-ci pense que créer une classe
FooFormValidator pour chaque formulaire est inutile.
Il vous demande de créer une seule classe
Validator qui pourra
être utilisée pour valider tous les formulaires d'entrée.
Il propose d'écrire le code comme ceci
SignInForm form = new SignInForm("foo@bar.com", "XXXXXXXXXXX", "bob", "foo", 20);
Validator<SignInForm> validator = new Validator<>(form);
validator.validate(signin -> Validation.validEmailAddress(signin.getEmail()));
-
Écrire le code de la classe Validator correspondante.
-
Le chef de projet fait remarquer que l'on peut utiliser le même mécanisme
de builder que la classe StringBuilder pour chainer les appels
SignInForm form = new SignInForm("foo@bar.com", "XXXXXXXXXXX", "bob", "foo", 20);
Validator<SignInForm> validator = new Validator<>(form)
.validate(signin -> Validation.validEmailAddress(signin.getEmail()))
.validate(...)
Modifier le code de la classe Validator en conséquence.
-
Puis, votre chef de projet vous fait remarquer que cela serait sympa
de séparer l'appel au getter de l'appel à la validation comme ceci:
SignInForm form = new SignInForm("foo@bar.com", "XXXXXXXXXXX", "bob", "foo", 20);
Validator<SignInForm> validator = new Validator<>(form)
.validate(SignInForm::getEmail, Validation::validEmailAddress)
.validate(...)
Ajouter une nouvelle méthode validate (avec 2 paramètres) pour que le code ci-dessus fonctionne.
- Compléter le code donné dans l'énoncé de la question précédente pour faire également la validation du mot de passe.
Pourquoi il n'est pas possible d'utiliser des method references pour référencer les autres méthodes de validation ?
En fait, il est plus flexible de mettre les lambdas dans les méthodes Validation.validEmailAddress
ou Validation.validPassword() qu'à l'endroit on l'on fait l'appel.
Changer le code et les signatures des méthodes dans Validation pour
que le code ci-dessous fonctionne.
SignInForm form = new SignInForm("foo@bar.com", "XXXXXXXXXXX", "bob", "foo", 20);
Validator<SignInForm> validator = new Validator<>(form)
.validate(SignInForm::getEmail, Validation.validEmailAddress())
.validate(SignInForm::getPassword, Validation.validPassword(10))
.validate(...)
-
Quel est le problème avec le getter getAge ?
Comment corriger le problème ?
Il y a deux solutions ! Quelles sont les avantages et les inconvénients ?
Implanter la solution qui n'utilise qu'une seule méthode validate avec 2 paramètres.
-
En fait, le code de la méthode validate avec 2 paramètres pourrait appeler le
code de la méthode validate avec 1 seul paramètre. Comment faire ?
Modifier le code en conséquence.
-
Optionnel: Comment écrire le code de la question précédente avec des méthodes références
et en utilisant la composition de fonctions?
Exercice 3 - Validation 3
Enfin, on souhaiterait associer à chaque validation un message d'erreur et ne lever l'exception
(si il y a au moins une erreur de validation) qu'à la fin de toutes les validations
avec l'ensemble des messages d'erreur.
En terme de code, on veut modifier le
Validator pour que le code suivant fonctionne.
SignInFrom validatedForm =
new Validator<>(form)
.validate(SignInForm::getEmail, Validation.validEmailAddress(), "email adress should contain a '@'")
.validate(...)
.get(); // lève un erreur ou renvoie le formulaire validé
-
Modifier le code du Validator existant pour qu'il fonctionne avec l'exemple ci-dessus.
Note: on sauvegardera les différents messages d'erreur, s'ils existent, dans une ArrayList.
-
La classe Validator est-elle un
monad ?
© Université de Marne-la-Vallée