<style> .reveal { font-family: Roboto, Source Sans Pro, Helvetica, sans-serif; font-size: 42px; font-weight: 300; } .reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 { margin: 0 0 20px 0; font-family: Roboto, Source Sans Pro, Helvetica, sans-serif; font-weight: 500; line-height: 1.2; letter-spacing: normal; text-transform: uppercase; text-shadow: none; word-wrap: break-word; } .reveal section img { margin: initial !important; background: initial !important; border: initial !important; box-shadow: initial !important; } </style> # Fonctions<br/> & <br/>inversion de dépendance --- ### SOLID Principles --- #### D Dependency Inversion Principle = Principe d'inversion de dépendance --- *Les implémentations de haut niveau, qui représentent des règles métier complexes ne devraient pas être affectées par des changements dans les implémentations de bas niveau qui représentent des détails techniques.* --- Si je change de base de donnée <small>*implémentation technique de bas niveau*</small> Je ne devrais pas avoir à modifier le code qui permet d'enregistrer un nouveau CNFS candidat <small>*implémentation métier de haut niveau*</small> --- <img src="https://i.ibb.co/hDnVXp9/D-principe.jpg"> --- #### La solution proposée --- Faire en sorte que les implémentations techniques de bas niveau soient représentées par des interfaces. --- Ainsi les implémentations métier de haut niveau dépendent des interfaces qui représentent les implémentations techniques de bas niveau et non leurs implémentations concrètes. --- #### C'est très bien Cette solution communément admise est tout à fait valide dans un contexte de programmation orientée objet. --- #### Mais... Bien que JavaScript soit un langage multi-paradigmes qui fonctionne en orienté objet, ce n'est pas le mode qui a été choisi dans Conseiller Numérique. --- ### Quels principes issus de la programmation fonctionnelle peuvent nous aider ? --- - Fermetures (Closures) - Fonctions d'ordre supérieur - Curryfication --- ### Fermetures --- Une fermeture est une fonction qui a accès au contexte d'appel. --- ```javascript const contexte = 'contexte défini en dehors de la fonction.'; const fermeture = () => { console.log(`J'ai accès au ${contexte}`); }; fermeture(); ``` ``` > J'ai accès au contexte défini en dehors de la fonction. ``` --- ### Fonctions d'ordre supérieur --- Soit au moins une fonction en argument : ```javascript let numbers = [4, 2, 5, 1, 3]; numbers.sort((a, b) => a - b); console.log(numbers); ``` ``` > Array [1, 2, 3, 4, 5] ``` --- Soit retourne une fonction comme résultat : ```javascript const ordreCroissant = (a, b) => a - b; const ordreDecroissant = (a, b) => b - a; const ordre = croissant => croissant ? ordreCroissant : ordreDecroissant; let numbers = [4, 2, 5, 1, 3]; let valeurBoutonTri = true; numbers.sort(ordre(valeurBoutonTri)); console.log(numbers); valeurBoutonTri = false; numbers.sort(ordre(valeurBoutonTri)); console.log(numbers); ``` ``` > Array [1, 2, 3, 4, 5] > Array [5, 4, 3, 2, 1] ``` --- ### Curryfication --- <img src="https://upload.wikimedia.org/wikipedia/commons/8/86/HaskellBCurry.jpg"> Haskell Brooks Curry Logicien et mathématicien américain. Ses travaux ont posé les bases de la programmation fonctionnelle --- Transformation d'une fonction à plusieurs arguments en une fonction à un argument qui retourne une fonction --- ```javascript const uncurriedAdd = (x, y) => x + y; const uncurriedAddResult = uncurriedAdd(4, 3); console.log(uncurriedAddResult) const curriedAdd = x => y => x + y; const curriedAddResult = curriedAdd(4)(3); console.log(curriedAddResult) ``` ``` > 7 > 7 ``` --- ### Et quand on combine les trois --- On peut utiliser la curryfication pour séparer les paramètres d'une fonction en plusieurs groupes. --- Par exemple - un groupe pour les paramètres liés aux détails techniques - un autre groupe pour les paramètres liés au métier. ```javascript const getConseillersSansCurry = async (db, id, statut) => await ... const getConseillers = db => async (id, statut) => await ... ``` --- Le principe de fermeture nous permet d'accéder aux paramètres techniques dans la fonction qui reçoit les paramètres métier. ```javascript const getConseillers = db => async (id, statut) => await db .collection('conseillers') .findOne({ _id: id, statut: statut }); ``` --- La fonction devient alors une fonction d'ordre supérieur dont les paramètres sont liés aux détails techniques et qu'on évitera d'utiliser dans le code métier. ```javascript // Code non métier : préparation technique. const db = await app.get('mongoClient'); const getConseillersMetier = getConseillers(db); codeMetier(id, statut, getConseillersMetier); ``` --- Cette fonction retourne une fonction dont les paramètres sont exclusivement liés au métier, on peut ainsi l'utiliser sans risque de changement si l'implementation technique évolue. ```javascript // Code métier : impossible de savoir si mongo est utilisé. const conseillersRecrutes = await getConseillersMetier(id, 'RECRUTE'); ``` --- Pour le nommage plutôt que `getConseillers` on peut utiliser `getConseillersRepository`. --- Le terme `Repository` représente un dépôt où l'on stocke et récupère des données, il peut avoir plusieurs impémentations : *mongo*, *postgres*, *système de fichier*, *api externe*, et ... autant que l'on veut dans nos tests avec des stubs ou des fakes ! --- Le nom `getConseillers` étant maintenant libre on peut l'utiliser à la place de `getConseillersMetier` qui n'était pas idéal. --- Merci de votre attention :)
{"metaMigratedAt":"2023-06-16T14:44:30.917Z","metaMigratedFrom":"YAML","title":"Fonctions & inversion de dépendance","breaks":true,"slideOptions":"{\"transition\":\"slide\",\"controls\":false,\"progress\":true,\"slideNumber\":false}","contributors":"[{\"id\":\"01d6a5d9-edd0-4d27-b734-061799130618\",\"add\":8956,\"del\":2984},{\"id\":\"5d87520c-c463-4068-a514-5e0d85b2a660\",\"add\":1,\"del\":2}]"}
    368 views
   Owned this note