Modèles de conception avancés en JavaScript : de la théorie à la pratique
\n\nLes modèles de conception sont des solutions éprouvées aux problèmes typiques du développement logiciel. Pour un développeur JavaScript travaillant avec Node.js, la compréhension des modèles avancés ouvre la voie à la création de systèmes évolutifs, flexibles et faciles à maintenir. Dans cet article, nous analyserons quatre modèles clés : Observer, Proxy, Chaîne de responsabilité et Stratégie. Chacun sera illustré par un exemple pratique en Node.js.
\n\n1. Modèle Observer (Observateur) : architecture événementielle en Node.js
\n\nObserver est l’un des modèles comportementaux fondamentaux. Il définit une relation "un à plusieurs" entre les objets : lorsqu’un objet (le sujet) change d’état, tous les objets dépendants (les observateurs) sont automatiquement notifiés et mis à jour. En Node.js, ce modèle est à la base du module EventEmitter.
Exemple : système de notifications pour une boutique en ligne
\nImaginons que nous ayons un serveur qui gère les commandes. Lorsque le statut d’une commande change, plusieurs sous-systèmes doivent être notifiés : la journalisation, l’envoi d’e-mails et la mise à jour des analyses.
\n\nconst EventEmitter = require('events');\n\n// Sujet (Subject)\nclass OrderManager extends EventEmitter {\n constructor() {\n super();\n this.orders = [];\n }\n\n createOrder(orderId, items) {\n const order = { orderId, items, status: 'created', createdAt: new Date() };\n this.orders.push(order);\n this.emit('order:created', order);\n return order;\n }\n\n updateStatus(orderId, newStatus) {\n const order = this.orders.find(o => o.orderId === orderId);\n if (order) {\n order.status = newStatus;\n this.emit('order:statusChanged', order);\n }\n }\n}\n\n// Observateurs (Observers)\nfunction logObserver(order) {\n console.log(`[LOG] Commande ${order.orderId} : statut changé en ${order.status} à ${new Date().toISOString()}`);\n}\n\nfunction emailObserver(order) {\n console.log(`[EMAIL] Envoi d’une notification pour la commande ${order.orderId} avec le statut ${order.status}`);\n}\n\nfunction analyticsObserver(order) {\n console.log(`[ANALYTICS] Enregistrement de l’événement : commande ${order.orderId} -> ${order.status}`);\n}\n\n// Utilisation\nconst manager = new OrderManager();\nmanager.on('order:created', logObserver);\nmanager.on('order:statusChanged', logObserver);\nmanager.on('order:statusChanged', emailObserver);\nmanager.on('order:statusChanged', analyticsObserver);\n\nmanager.createOrder('ORD-001', ['Livre', 'Stylo']);\nmanager.updateStatus('ORD-001', 'expédiée');\n\n\nCette approche permet d’ajouter ou de supprimer des observateurs sans modifier le code du sujet, ce qui rend le système flexible et facilement extensible.
\n\n2. Modèle Proxy (Mandataire) : contrôle d’accès et mise en cache
\n\nProxy fournit un objet de substitution qui gère l’accès à un autre objet. En JavaScript, ce modèle est implémenté via l’objet intégré Proxy, qui permet d’intercepter et de redéfinir des opérations fondamentales (accès aux propriétés, appels de fonctions, etc.).
Exemple : proxy de mise en cache pour les requêtes API
\nSupposons que nous ayons un service qui effectue des requêtes coûteuses vers une API externe. Nous souhaitons mettre en cache les résultats pour éviter des appels répétés.
\n\n// Service réel (Real Subject)\nclass ExternalApiService {\n async fetchUser(id) {\n console.log(`Récupération de l’utilisateur ${id} depuis l’API externe...`);\n // Simulation d’un délai réseau\n await new Promise(resolve => setTimeout(resolve, 1000));\n return { id, name: `Utilisateur ${id}`, email: `utilisateur${id}@example.com` };\n }\n}\n\n// Proxy (Proxy)\nclass CachingProxy {\n constructor(realService) {\n this.realService = realService;\n this.cache = new M