[Actualité] Fonction recevant et retournant un objet
par
, 30/03/2018 à 21h46 (3901 Affichages)
Auteur : Bill Sourour
Source : Elegant patterns in modern JavaScript: RORO
Ce billet résume, en français, les avantages du modèle RORO (recevoir un objet, retourner un objet) de Bill Sourour.
- Paramètres nommés
- Paramètres par défaut et requis plus propres
- Valeurs de retour plus riche et composition de fonction plus facile
Paramètres nommés
Supposons que nous ayons une fonction qui renvoie une liste d'utilisateurs dans un rôle donné et supposons que nous devons fournir une option pour inclure les informations de contact de chaque utilisateur et une autre option pour inclure les utilisateurs inactifs, traditionnellement nous pourrions écrire : function findUsersByRole ( role, withContactInfo, includeInactive ) {...}.
Un appel à cette fonction pourrait ressembler à : findUsersByRole( 'admin', true, true ).
En passant un objet notre fonction semble presque identique sauf que nous mettons des accolades autour de nos paramètres : function findUsersByRole ( { role, withContactInfo, includeInactive } = {} ) {...}.
Cela fonctionne en raison d'une fonctionnalité JavaScript introduite dans ES2015 appelée la déstructuration (destructuring).
Déstructuration : const {x, y} = {x: 11, y: 8}; ce qui est pareil que : const {x: x, y: y} = {x: 11, y: 8};.
Vous pouvez également combiner des raccourcis de valeur de propriété avec des valeurs par défaut : const {x, y = 1} = {}; // x = undefined; y = 1.
Maintenant, nous devons appeler notre fonction comme ceci : findUsersByRole( { role: 'admin', withContactInfo: true, includeInactive: true } ).
Nous utiliserons cette méthode (RORO) lorsqu'elle ajoute de la valeur en rendant une liste de paramètres plus claire et flexible et en rendant une valeur de retour plus expressive. Bien entendu, si vous écrivez une fonction qui n'a besoin de recevoir qu'un seul paramètre, la réception d'un objet est trop lourde. De même, si vous écrivez une fonction qui peut communiquer une réponse claire et intuitive à l'appelant en retournant une valeur simple, il n'est pas nécessaire de retourner un objet.
Paramètres par défaut et requis plus propres
Code JavaScript : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 function findUsersByRole ({ role = kRequiredParam('role'), // fonction incluse dans dvjhUtilities-1.7.0.js, voir le billet précédent. withContactInfo = true, includeInactive = true } = {} ) {...}
Maintenant, nous pouvons appeler notre fonction comme ceci : findUsersByRole( { role: 'admin' } ) ou comme ceci : findUsersByRole( { role: 'admin', includeInactive: false } ) ou comme ceci : findUsersByRole( { includeInactive: true, role: 'admin' } ).
N'oubliez pas que l'on passe un objet, nous pouvons donc donner les propriétés dans un ordre aléatoire et omettre une propriété lorsqu'elle a une valeur par défaut.
La propriété "role" ne pouvant recevoir une valeur par défaut valide, on lui donne la valeur de retour (Error) de kRequiredParam('role'). On peut également utiliser la fonction kRequiredParamVerbose() (fonction incluse dans dvjhUtilities-1.7.0.js, voir le billet précédent). Exemple :
Code JavaScript : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 function findUsersByRole ({ role = kRequiredParamVerbose( { param : "role", type : "String", info : "rôle de l'utilisateur" } ), withContactInfo = true, includeInactive = true } = {} ) {...}
Si quelqu'un appelle findUsersByRole sans fournir de rôle, il obtiendra l'erreur suivante :.Required parameter is missing : param = role, type = String, info = role jouer par le personne at findUsersByRole
Valeurs de retour plus riche et composition de fonction plus facile
Imaginez une fonction qui enregistre un utilisateur dans une base de données. Lorsque cette fonction renvoie un objet, elle peut fournir beaucoup d'informations à l'appelant. Lorsque nous insérons des lignes dans une table de base de données (si elles n'existent pas déjà) ou les mettons à jour (si elles existent), il serait utile de savoir si l'opération effectuée par notre fonction "Save" était un "INSERT" ou un "UPDATE". Il serait également bon d'obtenir une représentation exacte de ce qui était stocké dans la base de données, et il serait bon de connaître l'état de l'opération ; a-t-elle réussi, est-ce qu'elle est en attente dans le cadre d'une transaction plus importante, a-t-elle expiré ?
Lorsque vous renvoyez un objet, il est facile de communiquer toutes ces informations à la fois.
La composition de fonction est le processus consistant à combiner deux ou plusieurs fonctions pour produire une nouvelle fonction. Composer des fonctions ensemble, c'est comme assembler une série de tuyaux pour que nos données circulent ("Function composition is the process of combining two or more functions to produce a new function. Composing functions together is like snapping together a series of pipes for our data to flow through.” — Eric Elliott).
Utiliser RORO lève la limitation de la composition de fonctions qui est que chaque fonction de la liste ne peut recevoir qu'un seul paramètre.
Nous utiliserons la fonction kFunctionPipe( ...funcs ) qui est incluse dans dvjhUtilities-1.7.0.js, voir le billet précédent. Cette fonction prend une liste de fonctions et renvoie une fonction qui peut appliquer la liste de gauche à droite, en commençant par un paramètre donné, puis en transmettant le résultat de chaque fonction de la liste à la fonction suivante de la liste.
Voici un exemple où nous avons une fonction saveUser qui canalise un objet userInfo à travers 3 fonctions distinctes qui traitent les informations de l'utilisateur en séquence.
Code JavaScript : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70 function validate ( { id = kRequiredParam( "id" ), firstName = kRequiredParam( "firstName" ), lastName = kRequiredParam( "lastName" ), email = kRequiredParam( "email" ), username = kRequiredParam( "userName" ), pass = kRequiredParam( "pass" ), address = kRequiredParam( "addres" ), ...rest } = {} ) { // valider les données return { id, firstName, lastName, email, username, pass, address, ...rest } } function normalize( { email, username, ...rest } = {} ){ // "normaliser" les données return { email, username, ...rest }; } function persist( { upsert = true, ...info } = {} ){ // sauvegarder userInfo dans la base de données return { operation : "INSERT", status : "Success", saved : info }; } function saveUser( userInfo = kRequiredParam( "userInfo" ) ) { return kFunctionPipe( validate, normalize, persist )( userInfo ); } let userInfo = { id : "id42", firstName : "Daniel", lastName : "Hagnoul", email : "moi@ici.be", username : "danielhagnoul", pass : "motdepasse", address : "adresse", rue : "ma rue", codePostal : 4242, ville : "Les Mimosas en fleurs" }; let result = saveUser( userInfo ); for ( const key in result ){ if ( key === "saved" ){ for ( const key2 in result[ key ] ){ console.log( key2, " = ", result[ key ][ key2 ] ); } } else { console.log( key, " = ", result[ key ] ); } } /* * operation = INSERT * status = Success * email = moi@ici.be * username = danielhagnoul * id = id42 * firstName = Daniel * lastName = Hagnoul * pass = motdepasse * address = adresse * rue = ma rue * codePostal = 4242 * ville = Les Mimosas en fleurs */
Licence Creative Commons Attribution 2.0 Belgique