Wrap like an Egyptian
Le wrapping consiste à envelopper (to wrap en anglais) un code dans un autre code.
Le design pattern « Façade », illustré dans l’exemple ci-dessous, est un exemple de Wrapper.
Voici une petite illustration IRL du wrapping (tirée de Façade)
Quand nous passons une commande chez un distributeur, plusieurs actions s’enchainent :
- fabrication
- emballage
- prise en compte des taxes
- livraison…
La complexité de cet enchaînement nous est invisible puisqu’elle ne se réduit qu’à l’action de “commander” (Wrapper)

Il y a un an, dans notre application, nous avions voulu remplacer la bibliothèque de gestion de date js, moment dépréciée alors. Or, les appels aux fonctions de moment étaient éparpillés dans notre application. Faire la migration au cas par cas, dans les différents fichiers de notre base de code aurait été pénible.
Pour s’assurer d’une harmonie dans le comportement des fonctions de date et pour faciliter le remplacement de la bibliothèque externe, nous avons opté pour l’utilisation d’un fichier dans lequel seront répertoriés des Wrappers qui appellent les fonctionnalités de moment, utilisés dans notre code. Cet exemple illustrera la suite de l’article.
Les avantages à wrapper
Centraliser les appels
Pour formater une date avec moment, on peut utiliser le code suivant :
moment(date).format(format)
Maintenant, encapsulons cette fonctionnalité dans une fonction que nous créons :
// time-wrapper.js
const formatDate = (date, format) => {
return moment(date).format(format)
}
De cette façon, nous avons créé une fonction qui utilise une fonction de moment. On peut donc répéter ce processus et compiler en un seul fichier time-wrapper.js
les fonctions de moment que nous souhaitons utiliser dans notre application.
Effectivement, lorsqu’on wrappe une bibliothèque externe, le reste du code ne doit pas directement appeler cette bibliothèque. Sinon, nous nuirons à la centralisation des appels. Le code doit absolument passer par notre Wrapper.
Ainsi, un premier avantage du Wrapper est l’inventaire des fonctionnalités, d’une source tierce, que nous utilisons.
Lisibilité et dé-complexité
Nous avons vu plus haut que nous pouvons réduire des appels de fonction successifs, à une méthode. Notre Wrapper nous a donc permis de gagner en lisibilité.
Ensuite, la prise en main du Wrapper est plus simple car il « cache » la complexité des fonctions qu’il utilise. En effet, la fonctionnalité étant implémentée qu’une seule fois à son niveau, celui-ci la découple de sa complexité.
De plus nous avons la main sur les déclarations des fonctions du Wrapper. (Logique, nous les créons)
On peut donc avoir un objet en paramètre de nos fonctions. Cela est pratique:
- lorsqu’un ou plusieurs paramètres peuvent être non-définis
- documenter : lorsqu’on appelle la fonction, ses arguments seront explicites
De cette manière, on peut réecrire la fonction formatDate
qu’on a vue plus haut comme suit:
// time-wrapper.js
const formatDate = ({ date, format }) => {
return moment(date).format(format)
}
/* au lieu de
const formatDate = (date, format) => {
return moment(date).format(format)
}
*/
Qu’on appellera alors en explicitant chaque attribut de l’objet en paramètre:
formatDate({date: 1698942175420, format: "LLL"})
/* au lieu de
const formatDate(1698942175420, "LLL")
*/
Et si date
est non défini, nous n’avons plus besoin de le préciser.
formatDate({format: "LLL"}) // date est undefined
/* au lieu de
const formatDate(undefined, "LLL")
*/
La centralisation des fonctionnalités qu’on utilise dans un Wrapper contribue aussi à faciliter leur compréhension. Les moyens suivants permettent la documentation des fonctionnalités wrappées:
- les tests
- les commentaires
- un fichier de documentation créé dans le même dossier que le Wrapper
- le nom des fonctions
- la déclaration des fonctions et leur typage
Étendre une fonctionnalité
Parfois, les fonctionnalités proposées par une bibliothèque ne sont pas suffisantes pour un besoin particulier.
Avec un Wrapper, nous pourrions ajouter des fonctionnalités en enrichissant ce que nous propose la bibliothèque. Prenons l’exemple suivant :
const logDate = (a) => {
console.log(`Date input is a=${a}`)
const result = moment(a)
console.log(`Result Date : ${result}`)
return result
}
La fonction logDate
wrappe la fonction moment en lui ajoutant de la journalisation.
Par ailleurs, un autre exemple commun de fonctionnalité additionnelle que nous pourrions implémenter est une gestion d’erreur.
En effet lorsque cette dernière n’est pas proposée par l’existant, nous pouvons la créer dans un Wrapper.
const createDate = (date) => {
try {
const res = moment(date)
if (!moment(res).isValid()) {
throw new Error(`Mmmh il y a un souci de Date : ${date} semble être un mauvais input`)
}
return res
} catch (error) {
if (error instanceof Error) throw error
}}
Ainsi, notre createDate
gère un cas, comme étant une erreur, alors que moment ne le considère pas comme tel.
Modularité
L’utilisation d’un Wrapper nous donne la main pour étendre des fonctionnalités qui sont proposées par une entité.
Pour faire évoluer ces fonctionnalités, nul besoin de les traiter partout dans une application, mais uniquement au sein du Wrapper.
Reprenons l’exemple de la fonction formatDate
. Si nous décidions de changer de bibliothèque de gestion de dates et utiliser day.js au lieu de moment, nous n’aurions qu’à modifier le fichier time-wrapper.js.
// time-wrapper.js
const formatDate = ({ date, format }) => {
return dayjs(date).format(format)
}
Et voilà. Ou presque…
Certes, nous avons modifié la fonction formatDate
mais nous n’avons aucune certitude sur son comportement.
Ce qui nous amène au point suivant, le Wrapper doit être testé dans l’optique de voir son code évoluer.
L’idée n’est pas de tester l’intégralité d’une source tierce, mais plutôt de tester le Wrapper et ses fonctions qui sont utilisées dans notre application.
Si les tests sont au vert lorsque le code évolue, nous serons plus sereins.
Kairos ou le temps de l’opportunisme
Si on se demande quel est le bon moment pour wrapper, voici quelques raisons de mettre en place un Wrapper :
- Migration : Abstraire l’utilisation du composant externe simplifie sa migration.
- Faire l’inventaire des services tiers utilisés dans notre application.
- Faciliter la documentation d’une fonctionnalité
- Si on souhaite standardiser l’output d’une fonctionnalité dans nos applications. (Exemples : avoir les mêmes constructions de messages d’erreur, les mêmes formats de date, les mêmes url de base pour effectuer des requêtes etc..)
- Lorsqu’on sent/sait qu’une dépendance risque d’évoluer et qu’il faudra la remplacer
Conclusion
Nous avons vu ensemble ce qu’est un Wrapper et quels intérêts peut présenter sa mise en place. Effectivement, il facilite la clarté des fonctionnalités qu’il enveloppe ainsi que l’évolution de celles-ci.
Pour ma part, le gros atout d’un Wrapper est la facilitation de la maintenance de fonctionnalités : si une source externe du projet est amenée à changer, la wrapper est une étape qui facilite assurément ces évolutions.
Attention toutefois à son utilisation car cela a un coût. C’est donc après analyse qu’il faudra mettre en balance la création ou non d’un Wrapper. Appliquer le principe YAGNI (“you ain’t gonna need it” ou “vous n’en aurez pas besoin”) nous évitera de produire du code inutile.
Par exemple : si une fonctionnalité ne changera pas ou peu, ou si elle est peu utilisée dans le code, on ne devrait pas investir dans la mise en place d’un Wrapper.
Dans cet article, nous avons discuté de Wrapper de fonction, néanmoins, il ne se limite pas seulement aux fonctions : on peut par exemple wrapper des classes, des types ou encore des variables…
Références
Cette entrée a été publiée dans programmation avec comme mot(s)-clef(s) Programmation, js, javascript, design patterns
Les articles suivant pourraient également vous intéresser :
- Comment utiliser les fixtures pytest en autouse tout en maîtrisant ses effets de bord ? par Amaury Boin
- Améliorer l'architecture d'une application Vue grâce à la composition API par Pierre Assemat
- La charte du développement backend chez Deepki - Partie 4 : opérabilité par Olivier Roux
- Les fixtures de données pytest : la fausse bonne idée ? par Julia Cavicchi
- Introduction a l'éco-conception numérique par Jean-Baptiste Bergy
- La charte du développement backend chez Deepki - Partie 3, les tests automatisés par Olivier Roux
- Le duel de programmation : Python et Elixir face à face par Yahya Lazaar
- La charte du développement backend chez Deepki - Partie 2, la lisibilité par Olivier Roux
- La charte du développement backend chez Deepki - Partie 1, les grands principes par Olivier Roux
- Pourquoi et comment gérer la compatibilité ascendante ? par Olivier Roux
- Quelques techniques pour rendre un code lisible et agréable (et éliminer la charge cognitive) par Xavier Barbosa
- Débuter avec Ansible par Younes Bentalia
- TypedDict vs DataClass en Python par Pierre Assemat
- Quelques design patterns commentés en python par Vincent Valat
- Au fait, c'est quoi, un algorithme ? par Valentin Leclere
- Les design patterns en Vue3 par Juan Lozano
- This is DOP* par Xavier Barbosa
- The Clean Coder par Robert C. Martin - Être professionnel dans sa pratique du code par Quentin Mondot
- Comment manipuler facilement l'objet datetime dans MongoDB au sein d'un projet international en Python? par Muhui YU
- Introduction au Test-Driven Developement par Alex Peyrard
- Typage en python : comment utiliser le type hinting pour debugger du code par Juan Lozano
- Comment nous gérons nos configurations par Xavier Barbosa
- Domain Driven Design - Définir et modéliser un domaine métier par Younes Bentalia
- L'ETL chez Deepki par Marc Spiteri
- Comment manipuler un DataFrame Pandas avec le MultiIndex ? par Muhui YU
- The Clean Coder par Robert C. Martin - C'est quoi être professionnel quand on est développeur ? par Quentin Mondot
- Comment faire du Parsing de PDF sans utiliser les expressions régulières ? par Bo Jiang
- Dask vs Pandas : Fight ! par Juan Lozano
- Comment faire cohabiter D3 et Vue ? par Matthieu Caule
- Les outils de gestion de packages python par David Couderc
Postez votre commentaire :
Votre commentaire a bien été envoyé ! Il sera affiché une fois que nous l'aurons validé.
Vous devez activer le javascript ou avoir un navigateur récent pour pouvoir commenter sur ce site.