Chain of Responsibility : du code propre pour de la logique métier bordélique
Je vais vous parler de cette fonction qui hante mes nuits.
Tout système, à un moment donné, se retrouve avec une fonction que personne ne veut toucher. Au début, c'est petit : une simple validation, un `if` par-ci, un autre par-là. Puis les exigences s'empilent, les parties prenantes demandent « juste une petite exception », et bim — vous voilà face à un monstre de 500 lignes qui a l'air d'avoir été écrit par un comité (parce que c'est le cas).
C'est là que le pattern Chain of Responsibility débarque — pas avec de la magie, mais avec de la discipline. Je vous montre comment ce grand classique des design patterns peut démêler votre logique métier la plus tordue.
C'est quoi, Chain of Responsibility ?
À la base, Chain of Responsibility, c'est passer une requête le long d'une chaîne de handlers jusqu'à ce qu'un d'entre eux la prenne en charge. Pensez à une file d'escalade du service client — chaque personne essaie de résoudre votre problème, et si elle peut pas, elle passe au suivant.
Dans le code, au lieu d'avoir une méga-méthode qui gère tous les cas possibles, vous créez une série de handlers petits et focalisés. Chaque handler traite la requête ou la passe au suivant dans la chaîne.
Scénarios réels où CoR brille
Traitement de commandes e-commerce
Imaginez : vous construisez une plateforme e-commerce, et votre fonction `processOrder()` doit gérer codes promo, points fidélité, promos saisonnières, comptes entreprises, statut VIP, partenariats spéciaux... Votre fonction explose à des centaines de lignes avec des conditionnels imbriqués.
Avec Chain of Responsibility, vous créez des processeurs individuels :
```javascript
class DiscountCodeHandler {
handle(order) {
// Logique de réduction
return next ? next.handle(order) : order;
}
}
class LoyaltyPointsHandler {
handle(order) {
// Application points fidélité
return next ? next.handle(order) : order;
}
}
```
Chaque handler fait une seule chose. Test et maintenance deviennent un jeu d'enfant.
Pipeline de modération de contenu
Les réseaux sociaux font face à des workflows de modération complexes. Un post doit être vérifié selon les règles communautaires, scanné pour discours de haine, signalé pour contenu politique, contrôlé pour violation de copyright, évalué pour désinfo — tout ça avant publication.
Au lieu de tout fourrer dans une fonction de modération, vous construisez un pipeline :
1. **Handler Détection Spam** – Filtre le spam évident
2. **Handler Discours de Haine** – Signale le langage abusif
3. **Handler Copyright** – Vérifie contre du matériel protégé
4. **Handler Contenu Politique** – Route les posts politiques sensibles
5. **Handler Révision Finale** – Relecture par modérateur humain
Chaque étape gère le contenu ou le passe à la suivante.
Flux d'autorisation de paiement
Le traitement de paiement implique plusieurs couches de validation et d'approbation. Plutôt qu'un processeur monolithique, imaginez cette chaîne :
1. **Handler Détection Fraude** – Bloque les transactions suspectes
2. **Handler Plafond Crédit** – Vérifie les fonds suffisants
3. **Handler Vérification Banque** – Confirme la validité du compte
4. **Handler Conversion Devise** – Gère les paiements internationaux
5. **Handler Passage Passerelle** – Traite la transaction effective
Cette approche rend l'ajout de nouvelles étapes de vérification trivial, sans toucher au code existant.
Implémenter votre première chaîne
Voici une implémentation pratique en JavaScript :
```javascript
// Classe de base handler
class BaseHandler {
constructor() {
this.nextHandler = null;
}
setNext(handler) {
this.nextHandler = handler;
return handler;
}
handle(request) {
if (this.nextHandler) {
return this.nextHandler.handle(request);
}
return null; // Aucun handler n'a traité la requête
}
}
// Handlers concrets
class AuthenticationHandler extends BaseHandler {
handle(request) {
if (!request.user) {
return { error: 'Authentification requise' };
}
console.log('Authentification OK');
return super.handle(request);
}
}
class AuthorizationHandler extends BaseHandler {
handle(request) {
if (request.user.role !== 'admin') {
return { error: 'Permissions insuffisantes' };
}
console.log('Autorisation OK');
return super.handle(request);
}
}
class ValidationHandler extends BaseHandler {
handle(request) {
if (!request.data || Object.keys(request.data).length === 0) {
return { error: 'Données invalides' };
}
console.log('Validation OK');
return super.handle(request);
}
}
// Utilisation
const authHandler = new AuthenticationHandler();
const authzHandler = new AuthorizationHandler();
const validationHandler = new ValidationHandler();
authHandler.setNext(authzHandler).setNext(validationHandler);
const result = authHandler.handle({
user: { role: 'admin' },
data: { amount: 100 }
});
```
Les vrais bénéfices
**Maintenabilité** : Chaque handler fait une chose, et il la fait bien. Quand les règles métier changent, vous ne touchez qu'au handler concerné.
**Testabilité** : Les tests unitaires deviennent simples — vous testez chaque handler isolément au lieu de mocker des scénarios complexes.
**Flexibilité** : Besoin de réorganiser les étapes ? Changez la config de la chaîne. Routage conditionnel ? Implémentez la logique dans vos handlers.
**Débogage** : Quand ça casse, vous savez exactement quel handler a échoué parce que chacun logue sa décision.
Pièges à éviter
Ne tombez pas dans le piège des handlers trop génériques ou trop spécifiques. Visez l'équilibre : chaque handler représente une règle métier significative. Et résistez à l'envie de rendre les handlers conscients de leur position dans la chaîne — qu'ils se concentrent sur leur responsabilité et fassent confiance à la chaîne.
Rappelez-vous : Chain of Responsibility ne vise pas à remplacer toute votre logique conditionnelle. Il s'agit d'organiser des workflows complexes où plusieurs étapes de traitement distinctes doivent s'enchaîner.
Outils et frameworks qui embrassent CoR
Beaucoup de frameworks modernes implémentent déjà des variantes de ce pattern :
- **Middleware Express.js** ([expressjs.com](https://expressjs.com)) utilise une approche type chaîne
- **Pipeline middleware ASP.NET Core** suit les mêmes principes
- **Filtres Servlet Java** implémentent le traitement en chaîne
Même si votre framework n'utilise pas explicitement CoR, comprendre ce pattern aide à concevoir de meilleures architectures de middleware et d'intercepteurs.
Quand NE PAS utiliser Chain of Responsibility
CoR n'est pas une balle en argent. Évitez-le quand :
- Les étapes de traitement sont fortement couplées et s'exécutent toujours ensemble
- La performance est critique et il faut minimiser le surcoût d'appels de méthodes
- L'ordre de traitement n'a pas d'importance ou est figé
- Vous avez moins de trois étapes de traitement distinctes
L'introduire dans votre codebase
Prêt à amener Chain of Responsibility dans votre projet ? Commencez petit :
1. Identifiez une fonction complexe que tout le monde évite
2. Décomposez-la en étapes logiques de traitement
3. Créez des classes handler séparées pour chaque étape
4. Reliez-les en chaîne
5. Testez à fond avant d'étendre à d'autres zones
L'investissement se rentabilise vite en temps de débogage réduit et revues de code plus propres.
FAQ
**Q : Comment gérer les erreurs dans la chaîne ?**
R : Chaque handler doit soit traiter la requête avec succès, soit lever une exception appropriée. Envisagez un handler d'erreur en bout de chaîne pour attraper les requêtes non traitées.
**Q : Les handlers peuvent-ils modifier la requête en passant ?**
R : Absolument ! C'est même une pratique courante. Chaque handler peut enrichir, valider ou transformer les données pour les handlers suivants.
**Q : Et le traitement asynchrone ?**
R : Les implémentations modernes supportent souvent async/await. Assurez-vous juste que chaque handler attend correctement le suivant dans la chaîne.
**Q : Quelle différence avec le pattern Strategy ?**
R : Strategy choisit un algorithme parmi plusieurs, tandis que Chain of Responsibility essaie plusieurs handlers en séquence jusqu'à ce qu'un prenne la requête en charge.
La beauté de Chain of Responsibility réside dans sa simplicité. Il ne résout pas tous les problèmes d'architecture, mais bien appliqué, il transforme du code spaghetti ingérable en logique organisée, lisible — et votre futur vous remerciera.
Technologie
Comments (0)
No comments yet. Be the first to comment!
Leave a Comment