Clean architecture: Refac d’un component pour utiliser un MVP (modelView/presenter)

On est donc reparti pour faire notre plus beau projet « todo list » en mode « clean architecture ». Comme vous le savez, les avancées seront mise sur mon github et sera directement visible sur https://naughty-banach-1ccbd2.netlify.app.

Au programme aujourd’hui, passer d’un component « simple » à un component qui utilise un modelView/Presenter. Je dis « simple », mais il est déjà pas si mal! On a des useState, des useMemo, des useEffect, des appels asynchrones, etc…

ps: Prenez votre temps pour lire les étapes de refac 😇

C’est quoi le but dans l’histoire?

En fait, l’idée est simple. Techniquement, il faudrait:

  • Donner à la vue un viewModel qui contient toutes les informations prêtes à être affichées. Si vous avez des dates, ce sont déjà des string bien formatées et pas des Date. Si vous avez des champs à griser, il y a un booléen du genre isButtonDisabled. Les prix ont déjà le bon format. Bref vous voyez l’idée
  • Il faut aussi donner à la vue un Presenter qui contient les méthodes que celle-ci appellera quand on veut effectuer des actions. Par exemple: loadTodos(), toggleTodo(), addTodo(), etc… Le Presenter se chargera de modifier le viewModel et d’en avertir la vue.

Le but étant de simplifier au maximum la vue, car son seul but sera d’afficher les infos du viewModel au bon endroit (comme un placeholder) et d’appeler les méthodes du Presenter au bon moment (via un onClick=>presenter.addTodo()).

Et concrètement?

On va partir en mode « refactoring » pour arriver à une solution vivable.

1. Création du Presenter et du HOC

Commençons par faire notre Presenter qui s’occupera de donner une liste statique à notre vue. Les injections de Presenter et de viewModel se feront via un HOC (Higher-Order Component).

Code complet dispo : https://github.com/Nikoms/clean-todo/pull/6/commits/26e99132790e5e69caa636c26b0e2995e36db4c1?diff=split&w=1

2. Interagir avec notre liste

Avoir une liste, c’est bien. Pouvoir interagir avec celle-ci, c’est mieux. Dans ce commit, nous allons refac deux choses:

  • Les fonctions addEmptyTodo et toggleDone vont appeler le Presenter.
  • La modification du viewModel devra re-renderer la vue avec les nouvelles données. Le Presenter aura désormais un « listener  » (une callback) qu’il appellera à chaque modification. Enfin, le HOC va créer un useState qui, quand il sera set, rafraichira le component. Les plus attentifs verront que le listener/callback du Presenter n’est autre que le setState 😎

Code complet dispo: https://github.com/Nikoms/clean-todo/pull/6/commits/98fb4046dc5ab5d819b2df34c232c02c522453f4

3. Rendre notre Presenter plus explicite

Dans l’exemple précédent, la vue appelle un presenter.setTodoList() à chaque opération de la liste. Le but étant de simplifier la vue, on va créer des méthodes spécifiques dans le Presenter: addEmptyTodo() et toggleDone(index)

Code complet dispo: https://github.com/Nikoms/clean-todo/pull/6/commits/4c9d426e01af0a912c2a9254c4d931d62ec47606

4. Et nos compteurs de ongoing/done ?

Il reste de la logique dans notre vue. Pour le moment, on a ceci:

Ce qui n’est pas bon, car toutes les infos affichées doivent venir du viewModel. C’est à notre Presenter de faire le boulot. Heureusement (il y a findus), tout est déjà centralisé dans la méthode Presenter._setTodoList(list). Il suffit de faire les filter+length à ce moment là:

Code dispo ici: https://github.com/Nikoms/clean-todo/pull/6/commits/396c1061933f9893d87322b525aeb7a3c360c91e

5. Async todos

Pour les besoins du refac, on avait initialiser notre liste de todos en dur dans le code (🤢). Nous allons repasser en mode asynchrone et demander à la vue d’appeler Presenter.loadTodos() quand elle est prête (onMount). On pourrait aussi charger la liste dans le constructeur du Presenter, mais je préfère laisser les constructeurs les plus « pures » possible 😁. Note importante: Nous injectons la méthode (getTodos) qui sera utilisée pour récupérer la liste car le Presenter ne veut pas savoir d’où ça vient (Dependency inversion): Cela pourrait être une méthode qui appelle une API REST ou GraphQL, un mock, etc… « Not our business ».

Code dispo ici: https://github.com/Nikoms/clean-todo/pull/6/commits/41ca449d64ebbb52427ab0424318b9e8202d0218

Et pour les tests

Hé ben pour les tests c’est beaucoup plus simple! En effet, techniquement, vous ne devriez plus avoir besoin de tests se basant sur votre component (ou si peu). Pour info, dans ce commit, vous avez une comparaison des tests écrits en utilisant le component et les tests écrits en utilisant directement le Presenter. Résultat des courses:

  • Les tests sont bien plus rapides: 300ms contre 6ms maintenant
  • Les tests sont robustes car ne se n’utilisent pas la vue (jsx, dom, …)
  • Le code métier n’es pas mélangé à la vue
  • On utilise du javascript pure. C’est tout une partie de code qui ne devra pas changer à chaque mise à jour d’une dépendance (je suppose que vous aussi, vous en avez 2-3…)

Vous pouvez toujours avoir un ou deux petits tests qui couvre le component, mais ils devraient survoler l’ensemble… Pour le coup, je testerais l’intégration manuellement, car écrire les tests équivaudrait à savoir si on a bien copier/coller la variable au bon endroit (tel un placeholder). Des exceptions sont toujours possibles, mais pour moi, le ratio entre le temps passer à écrire/maintenir le test et le gain apporté, n’est pas assez interessant. Je pense que les tests E2E couvriraient déjà suffisamment le code.

C’est tout?

Oui c’est tout 😊 Bien sûr, j’ai encore effectué 2-3 refac sur le code comme:

Si vous voulez, la merge request est disponible sur Github. Elle contient tous les commits atomiques avec, comme d’habitude, chaque refac effectué est commenté, suivi d’un commit « Remove comment for the next refactoring » pour vous simplifier la lecture du refac suivant 🤓

Encore des questions?

Postez les en commentaires… Sinon j’anticipe déjà quelques-unes :

Tu nous fais des HOC avec des classes plutôt que des fonctions… Tu serais pas un peu has-been? Va faire de l’angular! Ils aiment les classes là bas 🔥

Monsieur M’palèclass

Je dois être has-been oui 🙂. Mais je fais partie de ceux qui pensent qu’on peut utiliser les 2 dans un projets. Par contre Angular, Vue ou React, pour moi c’est le même combat! Après, vous pouvez aller lire l’article sur intitulé Double Your React Coding Speed With This Simple Trick. Il commence par faire un hook où il exporte des fonctions/variables. J’imagine qu’il y a moyen d’écrire tout ce que j’ai décrit ici en mode « composition ». Si ça se trouve, je le ferai pour le challenge. Mais entre temps, je trouve le HOC plus simple pour les tests qu’un hook qu’il faut mocker. En effet, je n’ai qu’à instancier un Presenter, éventuellement appeler quelques méthodes pour me mettre dans une situation particulière (ex: presenter.openModal()), et ensuite le passer à ma vue avec son viewModel. « Et voilà », comme disent les anglais..

Ok, mais ça n’ira pas dans mon projet, j’utilise redux et pleins de hooks, c’est impossible à intégrer dans ton bazar…

Monsieur Nicroipa

Si si! On va en faire un sujet très prochainement (je suis en plein dedans au boulot…). En fait, il faut voir le truc comme un simple HOC qui ajoute 2 props à ton component… La prochaine fois que tu rajoutes un useState, essaye cette technique pour voir.

Et donc tu n’utilises pas la puissance de tout le framework? Les hooks comme useMemo, useEffect ou des trucs plus custom comme useFetch ou useAsync, etc…

Monsieur daypendanse

Effectivement, je ne les utilise que quand il faut et certainement pas dans le Presenter. Mon but est d’avoir un code qui tient dans le temps, pas d’utiliser toutes les technos et les libs en vogue. J’ai tellement eu de gros projets Legacy (des projets avec 15/20 ans de devs) et c’est tellement la galère de maintenir des dépendances que merci mais non merci. Chaque version major est un vrai challenge 🤓! De plus, je me méfie des frameworks/libs qui prennent des décisions pour nous (React qui passe des classes aux fonctions par exemple) sur les paradigmes, nos structures de dossier ou nos nommages de fichier. Ce n’est pas pour ça que je ne les utilise pas bien sûr, mais leur impact sur notre code doit être limité. Intégrer des hooks qui font des calls API dans la vue, je trouve que c’est se tirer une balle dans le pied et franchement pas plus « rapide » à programmer.

On fait ça pour tous les components alors?

Monsieur Bon’Kestillon

Non. Pour le moment, je me concentre sur les gros component qui représente une page. Les « sous components » ont dest props hyper spécifiques. Je ne ferais pas de MVP pour un champ texte par exemple.

La suite?

Si vous voulez me demander d’ajouter une fonctionnalité dans le code ou me lancer un challenge, vous pouvez ouvrir une issue. Au programme:

  • Pouvoir éditer un todo (Mhhh, les formulaires, c’est jamais simple…)
  • Enregistrer ma liste quelque part. On aura probablement un backend simulé qui enregistrera tout en localstorage.

Merci d’avoir eu le courage de tout lire 😘

Quicky React: N’abusez pas des « useEffect »

Nouvelle série d’articles

Nouvelle série d’articles baptisés « Quicky ». Ils portent ce nom car à priori, ils devraient être court 🙂 J’utiliserai aussi mon repository github où vous pourrez voir un code complet avec des commits pour chaque étape de refactoring… Des commentaires dans le code seront rajoutés pour bien montrer l’avant et l’après à chaque commit

TL;DR :

  • Evitez les useEffect en cascade (un useEffect qui va modifier un state qui va faire en sorte d’appeler un autre useEffect, etc…)
  • useMemo peut faire le job à la place
  • Bonus: Appeler plusieurs setState dans une méthode asynchrone n’est pas une bonne idée 🙂

Trop de useEffect

Voici un exemple de code qui me dit que parfois, les useEffect, c’est vraiment puant… Un useEffect qui « appelle » un autre

useEffect. C’est à devenir fou! Par exemple, cet exemple, où une liste « async » qui, une fois chargée change certains compteurs via un deuxième useEffect. Pour les besoin de la démo, le code a été simplifié et « bien sûr » que personne ne code comme cela, n’est-ce pas?

Code complet dispo (avec commentaires et explications des soucis) sur : https://github.com/Nikoms/clean-todo/commit/5be102ae9e2eed1a4affd9586d71c8b972c0df4d

Le problème avec cela, c’est qu’on a beaucoup trop de « render ». Sans aucune interaction, on en a déjà 3:

  • Avant le fetchTodosFromApi
  • Après le fetchTodosFromApi
  • Après avoir mis à jour les compteurs via refreshCounters

Pas de state -> pas de useEffect -> pas de re-render.

Et pourquoi pas se passer des state pour les deux compteurs?

Code complet:  https://github.com/Nikoms/clean-todo/commit/1a36045110ef98db5f2d8ab2ff43af0a5808c397

C’est bien, mais là, on va recalculer les compteurs à chaque render. C’est pas trop grave pour certaines parties de code, mais dans notre cas, partons dans l’idée que c’est un « gros process ».

useMemo si besoin…

Si les performances s’en font ressentir, on peut très bien utiliser useMemo:

Code complet: https://github.com/Nikoms/clean-todo/commit/09b942f07122323e7229b8bbf32064d7e851be12

 Ca fait le même job que le useEffect au début, sauf que le calcul ne nécessite pas un re-render.

Bonus point: Async useEffect

Un truc intéressant à savoir pour les useEffect qui exécute des setState dans une méthode async:

Code complet: https://github.com/Nikoms/clean-todo/commit/3a952053adcb4b9e12aa8140c6550daa5cf2dd24

Bien sûr que personne ne va écrire ce genre de code (la promise n’a aucun interêt), mais pour le fun: Savez vous combien de render il va y avoir? Au moment où mes todos changent, l’exécution du useEffect se fera à la vitesse de la lumière (car il n’y a rien à faire, si ce n’est lancer une Promise qui sera finie plus tard…).

Ensuite, il y aura autant de re-render qu’il y a de setState. Dans ce cas-ci, il y aura donc 2 render…

Dans un useEffect « classique » (non-async) par contre, il n’y aurait eu qu’un seul re-render, car React ne se re-render qu’à la fin du useEffect… Bon à savoir 🙂

Debug d’un process node en prod

TL;DR Executer « kill -usr1 pid », « node inspect -p pid » et le debug mode (en ligne de commande) est à vous.

Récemment, j’ai eu besoin de débugger une app Node.js en production (un setTimeout qui ne fonctionne plus après 25 jours… Original). Seul problème: Comment faire des breakpoints et donc entrer en « mode debug » sur un process qui tourne déjà… Facile!

Commençons avec une app très simple qui affiche « Hello {name} » toutes les 2 secondes.

Pour le lancer en production, nous utilisons la commande « node index.js« .

Time to debug!

Première chose à faire, trouver le process

Ensuite, il faut le passer en mode debug. Pour ce faire, il faut le « killer » avec l’option -usr1 et le pid.

Si tout va bien, vous devriez voir ce message dans vos logs. Comme si vous aviez fait un console.log, donc au même endroit que vos « Hello {name} ».

Vu que mon port 9229 était fermé dans docker, j’ai préféré tout faire en ligne de commande. C’est un peu plus fastidieux, mais ça fonctionne bien!

La prochaine étape est d’écouter/inspecter le process en indiquant le même pid.

Vous devriez avoir un nouveau message dans vos logs:

Nous pouvons, dès à présent, commencer à rentrer dans le vif du sujet. On peut par exemple connaitre la liste des fichiers utilisés:

Ajouter des break points. Pour cela, il suffit d’indiquer le fichier et la ligne.

Attention, car vos fichiers seront légèrement différents que le fichier source que vous connaissez. Voici par exemple le fichier index.js tel que le node l’interprète:

Celui-ci est d’autant plus différent si vous utilisez « ts-node » par exemple. Au début, il faut donc un peu tâtonner pour arriver à la bonne ligne.

Après avoir mis le break point , votre app devrait rapidement arriver à la ligne demandée. Celle-ci se bloque, affiche un petit « > » sur la ligne « en cours », qui attend vos instructions. Les étoiles (*) correspondent à vos autres break points:

A présent, vous avez le choix:

« bt »: Afficher la backtrace:

« c »: Continuer l’exécution (jusqu’au prochain break point si vous en avez d’autres):

« n »: Aller à la prochaine instructions (La prochaine ligne):

« list(n) »: Afficher plus de lignes avant et après la ligne en cours:

« s »: « Entrer » dans la fonction où l’on se trouve (« o » pour en sortir). Vous allez même pouvoir entrer dans du code de node lui-même!

« exec »: Executer/Interpréter du code node « on the fly », dans le context actuel. Pratique pour connaître l’état des variables.

Il existe cependant quelques limitations. Par exemple « require » qui ne peut pas être appelé:

Et voilà, plus d’excuse maintenant! Si vous avez un bug en production, vous avez les outils pour trouver votre bug si vos logs sont incomplets ou si, comme moi, vous avez un problème avec une version de node en particulier 🙂

Le TOKEN API: Simple, JWT et macaron

TL;DR Le macaron, c’est un JWT sous stéroïde…

Si vous créez une application, vous aurez probablement (en tout cas j’espère pour vous) des clients qui vont la consommer.

Si c’est une API, vous allez devoir fournir à vos utilisateurs un token qu’ils devront rajouter dans le header (Authorization: bearer MON_TOKEN). Grâce à cela vous allez pouvoir les identifier mais surtout savoir ce qu’ils ont le droit faire (ou pas).

Mais qu’est-ce qu’on met dans ces tokens? Comment les génère-t-on? Quid de la vérification des droits? Que fais-je? Pourquoi-je?

1. La préhistoire

Dans un cas basique, un token est une bête chaine de caractère plus ou moins longue, liée à un « user id » dans une base de données. Que l’on soit en mode « monolithe » ou « micro service », nous devrons probablement appeler un service d’authentification (user gateway) centralisé pour vérifier la validité du token.

Une fois l’utilisateur authentifié, le backend prend le relais et peut, le cas échéant, refaire des requêtes au « user gateway » pour savoir si l’utilisateur a les droits pour effectuer telle ou telle action. Pour  limiter l’accès au « user gateway », nous allons probablement l’optimiser pour qu’il nous renvoi l’identité de la personne et toute une série de flags correspondant à tous ses droits.

Autant vous le dire tout de suite, notre « user gateway » est un SPOF (single point of failure): S’il crash, c’est terminé! Aucun de nos micro-services ne répondra ou  notre monolithe sera down.

Ça fait également beaucoup des requêtes sur un réseau et qui dit réseau dit: Lent et non-fiable. En mettant du cache, on s’expose à d’autres problème car c’est bien connu, le cache est la chose la plus complexe en informatique…

2. Le JWT? C’est « sooo 2013 »

Ensuite, il y a le Json Web Token (JWT, pour les intimes). Avec lui, ce qui est cool c’est qu’il permet d’embarquer un JSON (appelé payload) intégré dans le token. Ce n’est donc plus une chaine de caractères aléatoires.

Mais pourquoi aurait-on besoin d’un json dans un token?
Je vous donne un exemple: Nous n’aurons plus besoin d’une table de correspondance token<->userId car le userId sera livré dans le payload.

Mais si le userId est dans le JSON, on peut mettre ?
Oui

Mais c’est pas du tout « secure » ton brol?
Mais siii, allez, il y a quand même une sécurité: Tout le monde ne peut pas créer un token!

Comment crée-t-on un token JWT?

La recette est simple! Pour faire un token, nous avons besoin de:

  • Un payload: le JSON.
  • Une clé secrète. Elle peut être symétrique (un secret qui peut être partagé) ou asymétrique (clé publique/privée).
  • Un algorithme de signature. Au choix: HS256, HS512, RS256, RS512, ES256, ES512, etc…

Comme nous pouvons le voir, le format d’un JWT est formée de 3 parties, séparées par un point:

  • La première partie « header », est le « base 64 » d’un json qui indique l’algorithme utilisé
  • La deuxième partie « payload », est le « base 64 » du json que nous voulons envoyer (le userId, etc…)
  • La troisième partie « verify signature », est le « base 64 » de la signature de l’algorithme utilisé.

Comment vérifie-t-on un token JWT ?

Une fois le token envoyé au backend, il faut bien sûr pouvoir le vérifier. Pour ce faire, rien de plus simple, les librairies fournisse la méthode « verify » qui nécessite 3 arguments:

  • Le token
  • Le secret avec lequel le token a été signé
  • L’algorithme qui a permis de signé le token

Grâce à cette méthode, nous pouvons être sûr que le payload est valide car personne ne peut produire un JWT valide à moins de connaître la clé secrète.

Pour récupérer le payload sous format json rien de plus simple:

Le payload n’étant pas crypté, on peut le récupérer avant même d’avoir vérifié la signature du token. Cela peut être pratique dans certains cas. N’y mettez donc jamais de données sensibles (mot de passe, api key, etc…).

Résumé et pistes de réflexion à propos du JWT…

  • Grâce à JWT, on a l’occasion de passer plus d’informations valides grâce au payload.
  • Le payload peut être lu sans connaitre le secret
  • Il faut connaitre un secret pour signer ou vérifier un token… Ce qui implique parfois un secret partagé. A ne pas oublier toutefois: Au moins le secret est partagé, au mieux on se porte.
  • On ne peut pas modifier un token. Pour ce faire, il faut en créer un nouveau sur base d’un autre:
    • Extraire le payload
    • Rajouter/modifier/supprimer des données
    • Créer un autre token avec le même secret et le même algorithme… Il faut donc connaitre le secret.
  • Si le secret est compromis, il faudra en re-générer un autre, ce qui invalidera tous les tokens.
  • Idée: Pourquoi ne pas mettre la « version » du secret pour permettre la migration d’un secret à un autre? Une sorte de dépréciation.
  • Idée: Vu que le secret peut être asymétrique, Il est possible de créer une clé privée/publique par utilisateur. On génère un token avec la clé privée, et on enregistre la clé publique dans la base de données. Cette clé n’est pas critique et peut être volée sans rien compromettre. Ce qui donnerait ceci:
    • Récupération du token dans le header (ou un cookie, une session, etc…)
    • Extraction du champs userId dans le payload
    • Récupération de la clé publique liée à cet utilisateur
    • Vérification du token avec la clé publique
    • Si c’est bon, le token est confirmé et on peut faire confiance aux infos du payload

3. Le macaron, le serial(ized) killer ?

Le macaron est un type de token inventé par des mecs de Google en  2014. Si voulez le papier complet, vous pouvez le télécharger ici: https://ai.google/research/pubs/pub41892.

L’idée assez différente: On va créer un token (toujours signé avec une clé secrète), qui va contenir une liste de conditions (appelés caveats) qui vont permettre de savoir si celui-ci est valide. Un peu comme une liste de « if » super stricte: Le premier qui répond false rend le token invalide. Un caveat est toujours une chaine de caractères, du genre: »time < 2019-01-01″. Dans ce cas-ci, le token est valide jusqu’au 31 décembre.

Comment crée-t-on un macaron?

La recette est simple. Pour faire un macaron, nous avons besoin de:

  • Une location: L’adresse « http » de la ressource que nous voulons accéder. Cette adresse est purement indicative et n’impacte pas la validité du macaron. Il n’y a pas de format spécifique, on peut donc vraiment y mettre ce qu’on veut.
  • Une liste de caveats: conditions sous forme de chaine de caractères. Il n’y a pas de format spécifique.
  • Une clé secrète pour signé le tout
  • Un identifier (ou identifier key): C’est un « indice » sur la clé secrète utilisée pour signer le macaron. Par exemple, vous pouvez mettre « clé numéro 2 » et le backend saura qu’il faudra vérifier le token avec la clé secrète numéro 2. Comme pour la location, cette valeur est purement indicative et n’impacte pas la validité du token. Il n’y pas de format spécifique.

Qu’est ce qui se cache derrière ce gros pavé de base64:

Si on analyse ligne par ligne:

  • location: La location utilisée pour instancier le macaron.
  • identifier: Le fameux « indice » qui indique quel secret on utilise
  • « cid ip = 127.0.0.1 »:  « cid » veut dire « caveat identifier ». C’est une condition qui doit être correcte pour que le macaron soit valide. Si nous avons 2 caveats, il y aura deux lignes commençant par « cid ».
  • « signature »: La signature de l’encryption en base 64.

Comment vérifie-t-on un macaron ?

C’est bien joli tout ça, on a un ou plusieurs caveats qui fonctionnent comme des conditions, mais comment vérifier les conditions? Voilà le code qui vérifie notre macaron précédent:

En fait, ce n’est pas sorcier, la méthode satisfyExact doit recevoir la même chaine de caractères que la méthode addCaveat utilisée pour créer le macaron. Simple, basique! Par défaut, les librairies donne deux méthodes pour vérifier une condition:

  • satisfyExact: Elle ne fonctionne que pour la condition « machin est strictement égal à cette valeur ». Ex: ip = 127.0.0.1.
  • satisfyGeneral: Cette méthode, bien plus puissante, reçoit une fonction/callback en paramètre. Celle-ci sera appelée pour vérifier chaque caveat. Le caveat est d’ailleurs son seul argument. La fonction doit renvoyer « true » si le caveat est correct et « false » dans tous les autres cas.

Pour illustrer l’utilisation de la méthode satisfyGeneral, imaginons ce petit caveat tout simple:

Comme on peut le voir, ce caveat est un peu plus complexe à cause du « < » qui n’est pas implémenté par les librairies. On aurait pu l’écrire autrement (« time is lower than 2019-01-01 » par exemple), mais peu importe, car de toute façon, c’est nous qui allons devoir faire une fonction qui « parse » et vérifie ce caveat. Voici le code de notre « vérifieur »:

Maintenant que notre nouveau « vérifieur de caveat » est prêt, il suffit de  l’ajouter à la liste des « vérifieurs connus » via la méthode satisfyGeneral :

À présent, nous sommes prêts à recevoir un caveat de type « time < … ».

Passe, passe l’macaron!

La deuxième idée du macaron est assez cool: Une fois le macaron en notre possession, il est possible, sans connaitre le secret, de rajouter d’autres caveat. On va donc rendre ce macaron de plus en plus strict car de nouvelles conditions devront être remplies. Il n’est pas possible de modifier ou supprimer un caveat.

Imaginez un peu:

  • Un proxy peur rajouter des caveats sans connaitre le secret partagé.
  • Un utilisateur peut donner son macaron a quelqu’un d’autre en rajoutant un caveat du style « readonly ».
  • Un tier peut rajouter une notion d’expiration sur une ressource.
  • etc…

Moi j’aimerais bien donner des données aussi…

Jusqu’ici, on a vu qu’un macaron, ce n’est qu’une succession de conditions/caveats mais parfois, on aimerait bien pouvoir donner des données comme un JWT. Il y a deux possibilités:

  • Soit on utilise l’ identifier comme gros payload en format json. Vu qu’il n’y a pas de format, ce n’est pas contre indiqué.
  • Après une petite discussion avec Quentin Adam de Clever Cloud, il m’a gentillement montré un bout de code qui permet de faire passer des données via les caveats. L’idée est pas mal du tout: Vu que le format d’un caveat (et son « vérifieur ») peut être custom, nous allons créer un caveat qui dit « enregistre cette valeur dans cette variable lorsque tu vas me vérifier ». Le « vérifieur » va extraire la donnée et renvoyer true.

Voici un exemple:

Et le « vérifieur »:

Et voilà comment on peut s’en sortir pour extraire des données d’un macaron.

Résumé et pistes de réflexion à propos du macaron…

  • Le macaron, comme pour JWT, permet de s’assurer de l’authenticité du token
  • On peut rajouter des caveats sans même connaitre le secret: pratique pour les proxy ou pour partager un macaron avec moins de droits. Cela fait de lui un sérieux outsider car les secrets sont moins éparpillés.
  • Le macaron, comme pour JWT, permet aussi d’extraire des données. Vu que la récupération est custom: The sky is the limit! Peut-être une source d’inspiration?
  • Idée: Pour l’identifier (à la création du macaron), j’ai déjà vu des gens utiliser ce genre du format: « userId:secretType:nonce ». Ce qui permet de:
    • Connaitre facilement le userId.
    • Savoir quel type de secret on a utilisé: une clé publique? un secret partagé? Cela peut aussi changer en fonction des droits de l’utilisateur.
    • Le « nonce » est une valeur incrémentée à chaque requête. Cela permet de vérifier qu’on ne réutilise pas deux fois le même macaron… Pourquoi pas ?

Nous n’en avons pas fini avec les macarons. On reviendra, dans un autre article, sur une autre fonctionnalité plus obscure, mais super puissante: les « third party caveat ». SPOILER: En gros, un autre système vous donne un macaron que vous mixez avec le votre et paf, ça fait (des chocapics) un mix des deux. Vous vous retrouver, dès lors, avec deux autorisations en une.

Selenium webdriver: Le click droit

Voici un mini article pour vous montrer comment faire un click droit dans Selenium webdriver. Contrairement au click « standard », cette action n’est pas prévue de base. Si vous avez affaire à une interface web avec de faux menus contextuels, il faut bien trouver un moyen pour passer outre les limitations du driver!

Heureusement, le javascript permet de simuler le click droit et double-heureusement, Webdriver nous permet d’exécuter du javascript.

Voici une méthode pour « cliquer droit » sur un élément via un sélecteur CSS. Dans l’exemple, jQuery est utilisé pour sélectionner l’élément, mais libre à vous d’utiliser du « pur javascript » s’il n’est pas disponible sur votre site.

Il ne vous reste plus qu’à étendre la nouvelle classe MySelenium2Extended plutôt que PHPUnit_Extensions_Selenium2TestCase pour avoir accès à la méthode.

Voici un exemple de code à l’usage:

Bons tests!

Symfony2: I18N dans les assets

Lorsque vous utilisez une librairie javascript, comme un date picker par exemple, il est bien souvent nécessaire de charger un fichier supplémentaire qui contient les traductions. Fort heureusement, ces fichiers sont nommés avec la « locale » (fr, nl, en, etc…), il est donc assez simple de les charger.

Le problème, dans ce cas-ci, c’est que 2 fichiers sont chargés séparément, alors qu’on favorise plutôt la concaténation.

On pourrait essayer de combiner les fichiers, mais ce n’est pas si simple. Ce code, par exemple, donne l’erreur suivante: Unexpected token « operator » of value « ~ ».

Il existe, cependant, un moyen d’utiliser des variables grâce à ces deux classes (je vous ai surligné les lignes importantes):

Comme vous pouvez le constater, Assetic gère deux variables par défaut: « locale » et « env ». Voyons comment on peut les utiliser…

Configuration

Tout d’abord, il faut les spécifier dans le fichier config.yml, dans la partie twig:

Il est indispensable d’indiquer toutes les valeurs possibles que pourrait avoir la variable au risque de reçevoir une grosse exception! Petit conseil pour la « locale », utilisez un paramètre (%available_locales% par exemple) dans votre parameters.yml afin de ne pas avoir à gérer la liste de vos locales dans plusieurs fichiers différents.

On va ensuite indiquer à Assetic le même tableau de langues et empêcher l’utilisation des controllers pour la génération des assets. Ce deuxième point est malheureusement indispensable.

Utilisation

Le plus dur est fait, vous pouvez maintenant combiner vos javascript classiques et « dynamiques » comme ceci:

  • {locale} sera remplacer par la langue en cours
  • vars est utilisé pour indiquer à Assetic quelles variables il doit gérer dans sa liste d’assets (souvenez-vous, il peut aussi gérer « env »)

Génération des fichiers

On a presque fini! Vu que, pour certains fichiers, on utilise le chemin direct (« bundles/acme/foo » au lieu de  « @AcmeFooBundle »), il faut lancer l’installation de vos assets dans le dossier « /web »:

Il ne reste plus qu’à générer les fichiers « à la main ». Cette commande va parcourir tous vos twigs et générer toutes les combinaisons possibles. Dans notre cas, 3 fichiers seront créés et contiendront chacun le datepicker avec la traduction.

En dev, il est préférable d’utiliser un –watch si vous modifiez régulièrement un de ces fichiers.

Customisation

Si « locale » et « env » ne vous suffisent pas, vous pouvez écrire vous-même votre classe qui gérera d’autres variables. Pour cela rien de plus simple:

  • Créez une classe qui implémente l’interface « Assetic\ValueSupplierInterface ».
  • Changer la classe de la clé « assetic.value_supplier.class » par la votre

Et voilà le travail!

Le SEO: Travail de fin d’études

Suite à une année de cours du soir en marketing (ce qui explique le faible nombre d’articles), j’ai décidé de choisir le SEO (avec une préférence pour Google) comme thème pour mon travail de fin d’année.

Il ne fallait « malheureusement » pas dépasser 40 pages, il n’est donc pas aussi complet qu’espéré. Cependant, je pense qu’il reprend une grosse partie de ce qu’il faut savoir sur les pratiques à appliquer (ou non) pour un bon référencement.

Sans aucune prétention, je met donc mon travail de fin d’études sur le SEO à disposition pour ceux que ça intéresse.

Bonne lecture!

 

Solid – D : Dependency inversion

Voici venu le temps des rires, des chants et du dernier article concernant les principes « SOLID », j’ai nommé: la « dependency inversion» ou, pour les anglophobes, l’inversion de dépendance.

À ne pas confondre avec l’injection de dépendance, même si on n’en est « pas loin », nous verrons pourquoi. Pour comprendre le principe voyons d’abord ce qu’est réellement une dépendance.

Team spirit!

Prenons comme exemple deux équipes de programmeurs qui travaillent sur le même projet.

  • L’équipe A s’occupe de créer une classe SmtpMailer,  qui va envoyer un e-mail via SMTP. Une classe d’assez bas niveau donc.
  • L’équipe B s’occupe de créer une classe PromoSender qui va récupérer la dernière promotion et va l’envoyer par e-mail via SmtpMailer.

Pour faciliter la lecture, je vais simplifier le code au maximum.

La classe de la deuxième équipe utilise la classe « Mailer » et donc en dépend. En effet, si un jour on remplace la classe « SmtpMailer », il y a fort à parier que le code de la seconde équipe devra aussi changer.

Voici le code à l’utilisation:

Le lecteur averti que vous êtes, remarquera que nous avons utilisé le principe d’injection de dépendance pour « donner » SmtpMailer à PromoSender. Nous parlons bien d’injection et pas encore d’inversion.

En Uml, cela donnerait ceci:

UserLog uses User

Remarquez la direction de la flèche qui va vers SmtpMailer. La classe PromoSender ne peut pas vivre sans cette dernière car elle l’utilise (ou en dépend si vous préférez). Donc si vous voulez envoyer un e-mail avec une autre classe que SmtpMailer, vous êtes cuit! A moins que…

Première étape: l’abstraction

Tout d’abord, voyons ces 2 classes comme faisant partie de 2 packages différents. Je rajoute les couleurs au diagramme pour vous simplifier la vie.

PromoSender uses SmtpMailer

En vert: Equipe A
En orange: Equipe B

Pour faire une « inversion » (et accessoirement bien faire les choses), nous utilisons une interface. L’équipe B se charge simplement d’extraire les méthodes publiques dans une interface qu’ils nomment MailerInterface. Le Sender va alors utiliser l’interface MailerInterface plutôt que la classe concrète: C’est une bonne chose!

En vert: Equipe A
En orange: Equipe B

Notez une chose intéressante à propos de la dépendance par rapport à SmtpMailer: Nous l’avons inversé (Le mot est lâché) ! À présent une flèche part de (et non plus vers) SmtpMailer. Les deux classes se rejoignent sur MailerInterface.

Dans le code, vous vous doutez bien que c’est tout simple. L’équipe A crée l’interface, et l’implémente:

Et pour l’équipe B, il suffit d’indiquer l’interface plutôt que la classe concrète dans le typage de la méthode:

L’essentiel est de ne pas dépendre de l’implémentation, mais d’une abstraction. Grâce à cela, l’implémentation (la classe SmtpMailer) peut être remplacée si votre projet évolue. Libre à vous de créer des ImapMailerGmailMailer, SimulatorMailer etc… tant que ceux-ci implémentent l’interface MailerInterface. Au final, peu importe votre type d’envoi, le code ne bougera pas:

C’est réutilisable et en plus ca répond au principe de l’O – Open/closed!

Voilà LA grande étape de l’inversion de dépendance. Pour résumer grossièrement: il faut utiliser des interfaces. Cependant, il y a deux choses qui ne semblent pas nettes.

  • Tout d’abord, lors du chapitre I – Interface Segregation, nous avons vu qu’une méthode/classe ne devait recevoir que ce dont elle avait besoin, ni plus ni moins. Or, PromoSender n’utilise pas le quart de ce que propose MailerInterface.
  • Ensuite, pour bien faire, les 2 modules devraient pouvoir être utilisables séparément. Si c’est bien le cas pour le package du « Mailer », c’est nettement moins vrai pour PromoSender qui reste dépendant de l’interface d’un autre package.

Voici peut-être venu le moment idéal pour lire la définition exacte du principe d’inversion de dépendance. Que dit Bob Martin (aka Uncle Bob) à ce sujet?

Bob a dit…

High-level modules should not depend on low-level modules. Both should depend on abstractions.
Abstractions should not depend upon details. Details should depend upon abstractions.

En français, cela donne:

Les modules de haut niveau ne devraient pas dépendre des modules de bas niveau. Les 2 devraient dépendre d’abstraction.
Les abstractions ne devraient pas dépendre de détails (=de l’implémentation). Les détails (=implémentations) devraient dépendre d’abstractions.

En gros, Bob nous dit qu’il va falloir revoir notre copie. Et ce, pour deux raisons:

  • Le module de haut niveau (PromoSender) dépend directement d’un module de bas niveau (MailerInterface).
  • PromoSender n’a pas sa propre abstraction. Elle utilise une interface d’un autre module et est donc dépendante de l’ « implémentation » de MailerInterface. En effet, si les méthodes de cette dernière changent, PromoSender doit changer également. On a vu mieux au niveau du « couplage ».

Chacun chez soi et les hippopotames seront bien gardés

Comment fait-on pour régler ces problèmes? Commençons par le plus simple: Demandons à l’équipe B (aka l’équipe des losers sans interface), de bien vouloir créer une interface que va utiliser PromoSender dans son constructeur. Cela afin de ne plus être dépendant de MailerInterface. On en profitera pour faire des méthodes qui nous « arrangent ».

Et en UML, cela donne:

Les 2 modules sont maintenant indépendants, les interfaces sont bien définies (et différentes), les tests unitaires sont probablement encore plus simples (on ne mock que le nécessaire, comme on a dit lors du I-Interface Segregation)… Mais une chose saute aux yeux: ils sont tellement indépendants qu’ils ne savent plus communiquer ensemble! Nous avons, comme qui dirait, un problème 🙂

Il existe une solution pour faire communiquer 2 modules qui « ne parlent pas la même langue ». Cette solution, nous l’avons déjà vue lors d’un précédent article, c’est bien sûr, le pattern Adapter. Encore lui! Décidément, il est partout. On le voit  d’ailleurs de plus en plus. Surtout dans des projets comme Symfony, Laravel où tout (ou presque) est modulable, externalisable voire même réutilisable en standalone (twig, swiftmailer, oAuth, etc…).

Le but du jeu est de pouvoir utiliser un objet « MyAdapter » comme s’il était un PromoSenderTransportInterface, et qu’il envoie un mail via le MailerInterface choisi.

Ça peut paraitre compliqué, mais ça ne l’est pas. Voici son code où il fait vraiment office de passerelle entre les 2 modules en implémentant une interface et en utilisant l’autre.

A l’utilisation, voici ce que ça donne avec une comparaison avant/après:

Voilà à quoi cela va ressembler en UML. Vous remarquerez que les flèches partent de MyAdapter, les 2 modules ne se connaissent toujours pas:

Comme vous pouvez le voir, l’adaptateur ne fait partie d’aucun des 2 modules. Cependant, il faut bien le mettre quelque part, mais où?

  • Au mieux, il sera créé dans un package à part: celui de votre projet.
  • Au pire, s’il faut vraiment choisir un des deux modules, il pourra être implémenté dans le package où se trouve PromoSenderInterface. En effet, celui-ci est de plus haut niveau, ce qui lui permet de plus facilement « anticiper » ce qu’il va utiliser.
  • Par contre, MailerInterface est de très bas niveau, et ne peut pas anticiper tous les autres modules qui vont l’utiliser.

L’heure du bilan

Aujourd’hui, nous avons vu qu’il était important de bien séparer l’abstraction et l’implémentation.

Tout d’abord, parce que l’abstraction a beaucoup moins susceptible d’être modifiée que l’implémentation. Le code change beaucoup, que ce soit en « change » ou en « bug », mais les interfaces changent très peu.

Ensuite, parce cela vous donne une certaine flexibilité quant à l’objet que vous pourrez donner à vos méthodes. Si vous n’utilisez pas d’interface, vous basculerez vite dans l’héritage « insensé » du style ImapMailer qui étend SmtpMailer qui lui même étend GmailMailer. Sur ce point de vue là, c’est Liskov qui ne sera pas contente.

Séparer abstraction et implémentation, oui, mais pas seulement. En effet, nous avons vu que nous pouvions aussi séparer le code en 2 packages complètement indépendants (ce qui nous rappelle le premier fondement SOLID: S: Single Responsibility). Deux packages différents, rime avec ré-utilisabilité, testabilité, maintenabilité, et plein d’autres choses qui terminent pas « ité », mais aussi plus de cohésion, moins de couplage et donc probablement moins de bugs. On sait également qu’au moins il y a de code, au moins vous rencontrerez de problèmes.

Nous avons vu, encore une fois, la magie de l’adaptateur. Celui-ci n’a pas que des avantages, car il peut aussi complexifier les choses, mais il a le mérite de coller des morceaux entre eux. Certes, si une de nos interfaces venait à être modifiée, il faudra changer l’adaptateur ainsi que ses 2-3 tests unitaires, soit! Mais c’est tout de même mieux que de revoir tout le code de votre package, et tous les malheureux projets qui l’utilisent. Sans parler des tests unitaires qui ne passent plus car plus aucun de vos « mocks » ne sont corrects. Ça fait beaucoup de boulot, vous ne trouvez pas?

Même si cet article n’est pas là pour faire l’apologie de l’adaptateur, sachez qu’il est tout autour de vous, mais qu’il ne s’appelle pas forcément Adapter partout. Voici un simple exemple de comment font les gens pour utiliser Smarty à la place de Twig dans Symfony: via EngineInterface. Vous verrez que la manière de procéder est exactement la même: On crée une classe qui étend une interface (EngineInterface) qui va utiliser un objet d’un autre module (Smarty ), et le tour est joué! Bien évidemment, Smarty et Symfony continuent d’évoluer indépendamment.

SOLID: Fin

Voilà pour le dernier article de SOLID. J’espère que tout cela vous a plu.

Comme vous avez pu le constater, les principes se chevauchent un peu les uns les autres. Au final, en suivant ces quelques règles, il est difficile de dévier de la trajectoire de « bon développeur ».

Cependant, comme dit Ribery: « SOLID, c’est une boite à outils qu’elle est bien de connaitre et de se rappeler ». Il ne s’agit donc pas là d’une vérité absolue. Je ne pense pas qu’un projet puisse être SOLID à 100%. Il ne faut pas tomber dans l’extrême où il existe une interface pour chaque signature de méthode. De même que chaque classe ne doit pas forcément avoir son interface. A l’époque, j’étais tombé sur ce ticket à propos de PimpleFabien Potencier disait:

Using an interface does not really make sense to me as I don’t see what kind of other implementation you might have.

Ça se tient! Quand savoir s’il faut une interface ou pas? Sans doute une question de feeling et d’expérience. L’exemple de l’ O: Open/Closed l’a bien montré: Le code, ça peut aussi se refactorer. Qui ne l’a jamais fait? Cela fait aussi partie de notre quotidien, et c’est comme ça que l’on apprend. C’est beau…

SOLID – I: Interface Segregation

Pour comprendre l’ Interface Segregation. Commençons par la définition de « ségrégation » trouvée dans le Larousse :

« Action de mettre à part quelqu’un, un groupe »

Ou si l’on regarde la définition d’un point de vue technique:

 « Séparation en amas distincts d’un ensemble de corps différents préalablement mélangés. »

Vous adaptez ceci à la programmation et plus spécifiquement aux interfaces, et je vous le donne dans le mile, la ségrégation c’est donc splitter une Interface en 2 (ou plus) si celle-ci prévoit trop de choses. Comment pourrait-on faire trop de choses si l’on suit le premier principe de SOLID ? Suivez le guide!

Pour comprendre ce principe, commençons par un classe native de php: ArrayObject. Lorsque vous étendez cette classe, vous pouvez déjà utiliser votre instance de plein de manière différentes: faire un count, un foreach, le sérialiser et accéder à ses attributs comme un tableau. Jusque là, rien de choquant. Pourtant lorsqu’on y regarde de plus près, cette classe étend quatre interfaces:  IteratorAggregate, ArrayAccess, Serializable et Countable. Que diriez vous de regrouper celles-ci pour n’en faire plus qu’une: ArrayObjectInterface ? Bof… Quel est l’intérêt? Elle ferait sans doute « trop de choses »,  même si, au final, elle n’a qu’une responsabilité: Gérer un objet comme tableau.

Dans ce cas-ci, nous avons fait le « chemin inverse » d’une ségrégation d’interface. Votre job consistera donc à couper une interface en petits morceaux. Un peu comme si monsieur PHP était parti de l’ ArrayObjectInterface pour arriver à IteratorAggregate , ArrayAccess , Serializable et Countable. L’avantage est, bien entendu, de n’étendre que le strict nécessaire. La ségrégation la plus extrême est de ne mettre qu’une seule méthode dans une interface, comme c’est le cas pour Countable par exemple.

 

Mais pourquoi fait-on cela ?

Prenons une interface Contact qui représente une personne qui a plusieurs informations basiques: Nom, prénom, e-mail, téléphone et adresse.

Ajoutons à cela une classe PubSender qui s’occupera d’envoyer des publicités (ou spams) à ces pauvres gens. Une méthode pour l’envoi par sms, l’autre pour les e-mails.

Pour savoir si nous sommes dans le bon, posons nous une première question: Lorsque j’envoie un sms, ai-je vraiment besoin de toutes les méthodes de l’objet Contact ? Ai-je besoin que « mon contact » possède un e-mail, une adresse et un nom de famille?

Biensûr que non! Pour l’envoi d’un sms, vous n’avez besoin que d’un numéro de téléphone et d’un prénom. Le problème est le même pour l’envoi d’e-mail qui n’a besoin que d’un e-mail pour vivre.

Ces méthodes ont donc trop de responsabilités car elle reçoivent de trop gros objets. Il est peut-être temps de diviser notre interface Contact même si elle parait déjà si petite.

 

Ségrégons !

Dans ce cas-ci,  l’idée est de faire en sorte que lorsque l’on envoie un sms, on ne demande qu’un objet qui ne contient que les méthodes que l’on va utiliser. Qu’à cela tienne, nous allons créer une interface Smsable qui représente une personne contactable par sms:

Faisons de même pour les gens qui sont contactables par e-mail.

Mon utilisateur (ou contact) peut donc étendre ces 2 interfaces, et y ajouter d’autres méthodes nécessaires.

Refactorons dès lors notre classe PubSender.

Comme vous pouvez le constater, seule la signature de la méthode change. Nous avons à présent des méthodes qui reçoivent des objets taillés sur-mesure pour la fonctionnalité.

Une chose intéressante avec la ségrégation est que vous pouvez plus facilement réutiliser (décidément LE maitre mot des principes SOLID) les classes. En effet, si les arguments des méthodes sont des interfaces simples, il sera beaucoup plus facile de les intégrer dans un autre projet. C’est aussi moins couteux d’implémenter les 2 méthodes de Smsable plutôt que les 5 méthodes de Contact. Ceci est d’autant plus vrai si 3 des 5 méthodes doivent rester vides car vous ne gérez pas les noms, prénoms et adresses postales. Souvenez-vous de ce que disait Barbara Liskov!

 

Autres avantages

En plus d’avoir des méthodes mieux définies, la ségrégation d’interface apporte aussi un autre avantage : Des mock plus simple pour les tests unitaires.

Fini les grosses classes dont il ne faut mocker qu’une petite partie des méthodes. Mais d’ailleurs, quelle partie ? Quelles méthodes ? Qui n’a jamais été vérifier ce qui est réellement appelé pour savoir ce qui est utilisé (et donc ce qu’il faut mocker)? Cela se passe bien moins souvent lorsque les interfaces sont plus petites, car tout, ou presque, est utilisé.

Utiliser la ségrégation d’interface peut aussi aider à mieux diviser son code (et donc à atteindre/garder la responsabilité unique). En effet, une grosse interface « fourre-tout » est bien plus difficile à maintenir que plusieurs petites qui ont plusieurs avantages:

  • Moins de tests unitaires par classe. En plus d’être petites, on peut penser qu’il y aura aussi moins de méthodes « interconnectées » entre elles.
  • Moins de problèmes de régression car moins de méthodes donc moins de code impacté.

Tout cela donne envie n’est-ce pas?

 

Et si j’envoie un mail ET un sms ?

La partie simple était d’envoyer un e-mail ou un sms. Mais que faire si vous devez envoyer une publicité à votre User sur les 2 supports simultanément (donc via une seule méthode).

Une première solution serait d’abord de faire une nouvelle interface Contactable regroupant les deux interfaces et qui sera implémentée par User.

L’envoi des publicités se fera alors en utilisant cette nouvelle interface

Cette solution est viable. Son principal défaut est que vous vous trouvez avec une nouvelle interface qui ne fait rien d’autre que représenter un ensemble d’autres interfaces. Croisons les doigts pour qu’elle ne grossisse pas trop, sinon vous devrez probablement la ségréguer à nouveau.

Une deuxième solution serait que votre classe User implémente les interfaces de base (Smsable et Emailable) sans passer par une interface intermédiaire. Rien d’extraordinaire pour l’instant.

En écrivant la méthode « send », vous vous dites logiquement que vous aurez besoin de 2 objets, car il y a deux supports: un objet Smsable et un Emailable.

Vous l’avez certainement deviné, comme User étend ces 2 interfaces, on va devoir le passer deux fois à la méthode.

Horreur et stupéfaction! Avant de vous précipiter sur la première solution, mettons les choses au clair.

MultiPubSender veut envoyer une publicité à deux supports différents. Elle n’a clairement pas besoin de savoir si ceux-ci appartiennent à la même personne. Nous n’avons donc aucune raison de changer sa signature, n’est-ce pas? Pourquoi devrait-elle s’adapter à votre objet User?

Oui, le monde est cruel car la même instance de User peut être vu de deux manières différentes (Smsable et Emailable). Mais c’est le problème de User (et le vôtre au passage) pas celui de MultiPubSender! L’inconvénient de cette solution est donc évident: On passe plusieurs fois le même objet à une méthode. Comme on dit chez nous: « ça l’fait pas! ». Par contre, vous laissez une porte ouverte pour le jour où les personnes de contact seront différentes (Open/Closed).

 

Conclusion

La ségrégation d’interface est un chouette principe, si on ne tombe pas dans l’extrême. Si vous poussez le bouchon un peu trop loin, vous n’aurez que des interfaces à une méthode. C’est avec la pratique que vous allez apprendre à bien les utiliser. N’hésitez pas à tester vos connaissances, à vous tromper, refactorer (<> refacturer), re-essayer, etc. Débattez avec vos collègues sur les interfaces à créer ou modifier.

Au début, cela peut sembler compliqué voire inutile pour ceux qui n’utilisent jamais d’interface, mais comme je l’ai déjà dit, elle pourra aussi vous aider à garder un code qui suit les principes de responsabilité unique. Comme quoi, tous les principes SOLID sont liés.

Quant au dernier exemple, un peu tordu mais bien réel, il n’est pas si fréquent que cela. Je suis sûr que chacun trouvera le compromis qui lui convient le mieux : Que ce soit avec une des solutions évoquées ou avec du code supplémentaire comme des adaptateurs, convertisseurs, etc.

Des perles de code pour terminer l’année !

Hello les méchants!

Petite pause de début/fin d’année pour tous. On reprendra les articles sur SOLID début janvier. On parlera également de designs pattern, TDD et aussi d’intégration continue.

Mais en attendant les « vacances », voici 3 « petites » perles (+1 gratuite) à déguster avec sagesse. Au menu:

  • De l’eval comme on les aime en amuse bouche
  • Un commentaire qui fait froid dans le dos, comme entrée
  • Un condition typique des juniors (désolé) comme plat
  • Et pour le dessert, une manière bien spéciale de s’assurer qu’un booléen est un booléen… Enfin je crois!

Trêve de bavardages inutiles et faisons place aux gurus!

 

1. Eval false

On commence léger avec un petit amuse-bouche en javascript. Je ne comprend toujours pas ce que vient faire l’eval ici:

 

2. Le commentaire de la mort

Âmes sensibles s’abstenir… Je rappelle que tout ici est authentique, c’est difficile à croire 🙂

Le développeur ne connait pas les cas qui ne sont pas gérer. Ça nous fais une bonne jambe! Sans test unitaire, cela rend la tache encore plus difficile. Bref, pour ces cas là: vive le TDD

 

3. La condition du noob

Je pense bien faire, je « déclare » ma variable, l’intention y est…

On aurait peut-être pu faire plus simple:

 

 4. Bang bang, you shoot me down

Celle-ci aussi vaut son pesant de cacahuète.

Je ne sais pas pourquoi ce « double bang » au début. Peut-être pour simplifier le fait de « vite changer pour voir si ça fonctionne pas dans l’autre cas »… J’espère au moins qu’il n’a pas fait ça pour s’assurer qu’on ai bien un booléen… Je doute, je ne sais pas, je ne sais plus, je suis perdu.

 

Bonne année!

J’espère que tout cela ne vous a pas donné une indigestion. Je vous souhaite à tous une bonne et méchante année 2014.

A très bientôt!