Terminologie - part 2

Dans un précédent article, j'évoquais un certain nombre de terminologie bon à connaître. Voici sa suite !

Dans la façon de concevoir

YAGNI

Signifiant "You Aren’t Gonna Need It", c'est une extension du KISS où en plus nous ne codons que ce qui est nécessaire.

Autrement dit, l'objectif est de ne pas avoir d'extra code qui n'est pas utilisée (et donc réduire la complexité du code) et du coup éviter d'avoir un ensemble de code mort.

Car ces derniers peuvent poser soucis. En effet, d'un point de vue refactoring, nous allons nous poser le sens de ce code mort, et savoir / investiguer s'il est vraiment mort et quel était son objectif. Bref, perdre du temps en vain.

TDD

Ici, le "Test Driven Development" est une philosophie de développement orienté test. Le principe repose sur les 6 points suivants:

  1. Réfléchir à la fonctionnalité
  2. Ecrire les tests de la fonctionnalité
  3. Lancer le test qui doit échoué
  4. Implémenter le code de la fonctionnalité
  5. Lancer le test qui doit réussir
  6. Répéter / raffiner

Le principe étant que nous réfléchissons plus à ce que nous avons besoin avant de réfléchir à comment l'implémenter (donc nous prenons plus de recules). Mais de plus, nous évitons du code mort car nous ne codons que ce que nous avons besoin (le TDD peut être vu comme une solution pour le YAGNI). Et cela nous force aussi à produire un code très fonctionnel

En pratique même, nous faisons les étapes suivantes

Nous regardons si la méthode existe

describe('and the method "createCommitLabel" ', function () {
 it('should exist', function () {
  expect(GitUtils.createCommitLabel).to.exist;
 });
});

Nous déclarons la méthode et regardons si le test passe. Ensuite nous raffinons en ajoutant un critère fonctionnel:


describe('and the method "createCommitLabel" ', function () { it('should exist', function () { expect(GitUtils.createCommitLabel).to.exist; }); it('should return a default value if no label set', function () { expect(GitUtils.createCommitLabel('1.2.3')).equals('Release version: 1.2.3'); }); });

Nous faisons alors du refactoring de la méthode jusqu'à que les tests déclarés passent, et ainsi de suite:

describe('and the method "createCommitLabel" ', function () { it('should exist', function () { expect(GitUtils.createCommitLabel).to.exist; }); it('should return a default value if no label set', function () { expect(GitUtils.createCommitLabel('1.2.3')).equals('Release version: 1.2.3'); }); it('should inject the package version if a label is set', function () { expect(GitUtils.createCommitLabel('1.2.3', 'Release: %s')).equals('Release: 1.2.3'); }); });


SOC

Le "Separation of Concerns" rejoint le "Single Responsibility Principle" du SOLID où nos fonctions, classes ont une seule responsabilité / rôle / comportement.

Autrement dit, un service par exemple ne doit gérer que des utilisateurs, et non pas les commandes et les factures (qui auront chacun leur propre service). Ce qui en plus nous amène un regroupement fonctionnel et métier.

C'est le mantra du monde unix: "do one thing well"


Dans la programmation fonctionnelle

Idempotem

Issue du monde mathématique, ce dernier indique qu'une fonction f(x) doit toujours retourner le même résultat si nous la rappelons plusieurs fois avec les mêmes paramètres.

Exemple d'une fonction non idempotem: Math.random();
Exemple d'une fonction idempotem: Math.cos(2 * Math.PI)

Agnostique

Ici, nous indiquons que notre fonction n'est pas sujette à un contexte ou à un état de notre application.

Regardons le code suivant:

class NotAgnostic {
 static aNumber = 1;
 
 static sum(aValue) {
  return NotAgnostic.aNumber + aValue;
 }
 
 static willChangeTheContext() {
  NotAgnostic.aNumber = 5;
 }
}


Nous voyons que la méthode "sum" dépend de la variable de class "aNumber" qui peut être changé à n'importe quel moment. Ce qui peut amener à des effets de bord non désiré.

Immutable

Le principe ici est de dire que la fonction ne modifie jamais les paramètres d'entrées. Par exemple une fonction qui doit ajouter une information dans un objet "User" devra cloner cet objet d'abord puis faire la modification et retourner l'objet cloné.

Transduction

Pour expliquer la transduction, prenant un cas concret. Soit un tableau de 1000 éléments. Nous voulons:
  • Récupérer les noms​
  • Ne garder que ceux commençant par ‘B’​
  • Prendre le troisième nom

Implémentons une approche naïve:

let onlyNames = oneThousandElement.map(function (row) {
       return row.name;
}); // 1000 iterations

let onlyNamesWithB = onlyNames.filter (function (name) {
       return name.toLowerCase().startsWith('b');
}); // 1000 iterations

let getThirdNameWithB = onlyNamesWithB[2]; // 1 iteration


Pour obtenir notre information, nous avons fait 2001 itérations !

Le principe de la transduction est de décrire un flux de filtrage et de transformations, qui seront exécutés pour chaque élément (si nécessaire) afin de restreindre au maximum les itérations. Ce qui donnerait en Lodash:

let getThirdNameWithB = _(oneThousandElement)
      .map(row => row.name)
      .filter(name => name.toLowerCase().startsWith('b'))
      .get(2)
      .value(); // Only needed iterations and will be execute only here

Commentaires

Posts les plus consultés de ce blog

ISO: liens & outils utiles

NodeJs et SSL: une petite analyse

Créer sa commande slack en quelques minutes