Design Patterns
Les design patterns sont des solutions éprouvées pour organiser et structurer votre code. Ce chapitre couvre les patterns fondamentaux utilisés dans les applications Node.js modernes.
Déclaration de fonctions
Fonction classique
Les fonctions classiques utilisent le mot-clé function et ont leur propre contexte this.
function calculateSum(a, b) {
return a + b
}
console.log(calculateSum(5, 3)) // 8
Caractéristiques :
- Possède son propre
this - Peut être utilisée comme constructeur (
new) - Hoisting : peut être appelée avant sa déclaration
Fonction fléchée (Arrow Function)
Introduite en ES6, la syntaxe fléchée est plus concise et n'a pas son propre contexte this.
Comparaison syntaxe et comportement :
// Arrow function (équivalent, plus court)
const calculateSum = (a, b) => a + b
console.log('Résultat:', calculateSum(5, 3)) // 8
Caractéristiques des arrow functions :
- Pas de
thispropre (hérite du contexte parent) - Ne peut pas être utilisée comme constructeur (
new) - Pas de hoisting : doit être déclarée avant utilisation
- Syntaxe concise et moderne
- Idéale pour les callbacks et fonctions courtes
Quand utiliser quoi ?
| Cas | Utiliser |
|---|---|
| Callback simple (map, filter...) | Arrow function |
| Méthode d'objet (besoin de this) | Fonction classique |
| Fonction standalone | Les deux (préférer arrow) |
| Constructeur (avec new) | Fonction classique |
Classes vs Fichiers de fonctions
Approche fonctionnelle
Organisation du code avec des fonctions exportées.
math.js :
// math.js
export const add = (a, b) => {
return a + b
}
export const subtract = (a, b) => {
return a - b
}
export const multiply = (a, b) => {
return a * b
}
export const divide = (a, b) => {
if (b === 0) {
throw new Error('Division par zéro impossible')
}
return a / b
}
Utilisation :
import { add, multiply, divide } from './math.js'
// Utilisation
console.log('Addition:', add(10, 5)) // 15
console.log('Multiplication:', multiply(10, 5)) // 50
console.log('Division:', divide(10, 5)) // 2
Avantages :
- Simple et direct
- Facile à tester (chaque fonction est isolée)
- Pas de complexité liée aux classes
- Difficile de partager un état (ex: historique des calculs)
Approche orientée objet (Classes)
Organisation du code avec des classes pour gérer un état partagé.
Calculator.js :
export class Calculator {
constructor() {}
add(a, b) {
return a + b
}
subtract(a, b) {
return a - b
}
multiply(a, b) {
return a * b
}
divide(a, b) {
if (b === 0) {
throw new Error('Division par zéro impossible')
}
return a / b
}
}
Utilisation :
import { Calculator } from './calculator.js'
// Instanciation
const calc = new Calculator()
// Utilisation
console.log(calc.add(10, 5)) // 15
console.log(calc.multiply(3, 4)) // 12
console.log(calc.add(7, 3)) // 10
Avantages :
- Encapsulation (état partagé via
this) - Méthodes qui accèdent au même état
- Idéal quand on doit mémoriser des données (historique, configuration, etc.)
Quelle approche choisir ?
| Critère | Fonctions | Classes |
|---|---|---|
| Simplicité | Plus simple | Plus complexe |
| État partagé (historique, etc.) | Difficile | Facile (this) |
| Testabilité | Excellent | Bon |
| Organisation du code | Plusieurs fichiers | Tout dans une classe |
| Recommandation pour juniors | Commencer par fonctions | Utiliser si besoin de mémoire |
Promesses
Les promesses permettent de gérer les opérations asynchrones. Il existe deux façons de les consommer.
.then() / .catch()
// Créer une promesse qui se résout après un délai
const delay = () => {
return new Promise((resolve, reject) => {
// Simuler une opération asynchrone
const success = true
if (success) {
resolve('Opération réussie !')
} else {
reject("Erreur lors de l'opération")
}
})
}
// :icon{name="i-heroicons-x-circle" class="text-red-500"} ANCIEN STYLE : .then() / .catch()
console.log("Début de l'opération...")
delay()
.then((result) => {
console.log('Résultat:', result)
return 'Suite...'
})
.then((result2) => {
console.log(result2)
})
.catch((error) => {
console.error('Erreur:', error)
})
Problèmes avec .then() / .catch() :
- "Callback hell" : code difficile à lire avec beaucoup de
.then()imbriqués - Gestion d'erreurs complexe (besoin de
.catch()à chaque niveau) - Difficile de combiner avec des boucles ou conditions
async / await
// :icon{name="i-heroicons-check-circle" class="text-green-500"} STYLE MODERNE : async/await
const run = async () => {
try {
console.log("Début de l'opération...")
const result = await delay()
console.log('Résultat:', result)
// Facile d'enchaîner plusieurs opérations
const result2 = 'Suite...'
console.log(result2)
} catch (error) {
console.error('Erreur:', error)
}
}
run()
Avantages de async/await :
- Code plus lisible (comme du code synchrone)
- Gestion d'erreurs simple avec
try/catch - Facile à combiner avec boucles et conditions
Exemple pratique : Requête API
// Fonction qui retourne une promesse
const fetchUser = async (userId) => {
const response = await fetch(
`https://jsonplaceholder.typicode.com/users/${userId}`,
)
return response.json()
}
const user = await fetchUser(1)
console.log('Utilisateur:', user)
Méthodes utiles :
| Méthode | Comportement |
|---|---|
Promise.all() | Attend que toutes les promesses réussissent |
Promise.allSettled() | Attend toutes les promesses (succès ou échec) |
Promise.race() | Retourne dès que la première promesse se termine |
Promise.any() | Retourne dès que la première promesse réussit |
Gestion d'erreurs
// Fonction qui retourne une promesse
const fetchUser = async (userId) => {
const response = await fetch(
`https://jsonplaceholder.typicode.com/users/${userId}`,
)
// Vérifier si la requête a réussi
if (!response.ok) {
throw new Error(`Erreur HTTP: ${response.status}`) // Lance une erreur si le statut n'est pas OK
}
return response.json()
}
// Utilisation avec gestion d'erreur
try {
const user = await fetchUser(1)
console.log(':icon{name="i-heroicons-check-circle" class="text-green-500"} Utilisateur:', user)
} catch (error) {
console.error(":icon{name="i-heroicons-x-circle" class="text-red-500"} Impossible de récupérer l'utilisateur:", error.message)
}
Le mot-clé throw :
throwpermet de lancer une erreur manuellement- Quand vous utilisez
throw, le code s'arrête immédiatement et passe au bloccatch - Utile pour créer des erreurs personnalisées avec des messages clairs
- Dans l'exemple ci-dessus, on lance une erreur si le statut HTTP n'est pas OK (404, 500, etc.)
Points clés du try/catch :
- Capture toutes les erreurs asynchrones (réseau, parsing JSON, erreurs lancées avec
throw) - Code lisible et facile à maintenir
- Fonctionne avec
awaituniquement (pas avec.then()/.catch())