Decorator (Décorateur)
Pattern structurel permettant d'ajouter dynamiquement des fonctionnalités à un objet sans modifier sa structure originale.
Mis à jour le 9 janvier 2026
Le pattern Decorator est un modèle de conception structurel qui permet d'étendre les fonctionnalités d'objets de manière flexible et réutilisable. Contrairement à l'héritage qui ajoute des comportements de façon statique, le Decorator enveloppe un objet dans une série de couches fonctionnelles appliquées dynamiquement à l'exécution. Ce pattern respecte le principe ouvert/fermé en permettant l'extension sans modification du code existant.
Fondements du Pattern Decorator
- Composition plutôt qu'héritage : enveloppe un objet dans un ou plusieurs décorateurs partageant la même interface
- Responsabilité unique : chaque décorateur ajoute une fonctionnalité spécifique et bien définie
- Transparence : les décorateurs et l'objet décoré partagent la même interface, rendant l'empilement transparent pour le client
- Extension dynamique : les comportements peuvent être ajoutés ou retirés à l'exécution selon les besoins
Avantages du Pattern Decorator
- Flexibilité maximale : ajout et retrait de responsabilités à l'exécution sans impacter les autres objets
- Alternative à l'héritage multiple : évite l'explosion combinatoire de sous-classes pour chaque combinaison de fonctionnalités
- Respect des principes SOLID : notamment le principe de responsabilité unique et ouvert/fermé
- Réutilisabilité : les décorateurs peuvent être combinés de multiples façons pour créer différentes configurations
- Testabilité améliorée : chaque décorateur peut être testé indépendamment de la chaîne complète
Exemple Concret : Système de Notification
// Interface commune
interface Notifier {
send(message: string): void;
}
// Composant de base
class EmailNotifier implements Notifier {
send(message: string): void {
console.log(`Email: ${message}`);
}
}
// Décorateur de base abstrait
abstract class NotifierDecorator implements Notifier {
constructor(protected wrapped: Notifier) {}
send(message: string): void {
this.wrapped.send(message);
}
}
// Décorateurs concrets
class SMSDecorator extends NotifierDecorator {
send(message: string): void {
super.send(message);
console.log(`SMS: ${message}`);
}
}
class SlackDecorator extends NotifierDecorator {
send(message: string): void {
super.send(message);
console.log(`Slack: ${message}`);
}
}
class EncryptionDecorator extends NotifierDecorator {
send(message: string): void {
const encrypted = this.encrypt(message);
super.send(encrypted);
}
private encrypt(text: string): string {
return `🔒${text}`;
}
}
// Utilisation avec empilement de décorateurs
let notifier: Notifier = new EmailNotifier();
// Ajouter SMS
notifier = new SMSDecorator(notifier);
// Ajouter Slack
notifier = new SlackDecorator(notifier);
// Ajouter encryption à tout
notifier = new EncryptionDecorator(notifier);
notifier.send("Alerte système critique");
// Output:
// Email: 🔒Alerte système critique
// SMS: 🔒Alerte système critique
// Slack: 🔒Alerte système critiqueMise en Œuvre du Pattern Decorator
- Définir une interface commune que partageront le composant de base et tous les décorateurs
- Créer le composant concret de base qui implémente l'interface avec le comportement minimal
- Développer une classe décorateur abstraite qui implémente l'interface et contient une référence au composant décoré
- Implémenter des décorateurs concrets qui étendent le décorateur abstrait et ajoutent des fonctionnalités spécifiques
- Composer les objets en empilant les décorateurs selon les besoins fonctionnels à l'exécution
- Documenter l'ordre d'empilement si certaines combinaisons ont des dépendances ou impacts particuliers
Conseil Pro
Utilisez le pattern Decorator avec modération : un empilement excessif peut créer une complexité difficile à déboguer. Privilégiez une architecture où l'ordre des décorateurs n'a pas d'importance, ou documentez clairement les dépendances. Pour les cas très dynamiques, considérez un système de plugins ou une architecture middleware qui peut être plus explicite.
Cas d'Usage et Applications
- Frameworks web : middleware pour ajouter logging, authentification, compression (Express.js, NestJS)
- Flux de données : transformations successives de streams ou observables (RxJS operators)
- UI Components : ajout de fonctionnalités visuelles (bordures, scrollbars, tooltips) sans modifier le composant
- Caching et optimisation : ajout de couches de cache, compression, validation sur des services existants
- Sécurité : ajout de couches d'encryption, sanitization, rate-limiting de manière modulaire
Décorateurs dans les Langages Modernes
De nombreux langages modernes ont introduit la notion de décorateurs comme fonctionnalité native du langage. TypeScript, Python et Java utilisent une syntaxe avec @ pour appliquer des métadonnées ou modifier le comportement de classes, méthodes ou propriétés. Bien que conceptuellement liés au pattern Decorator, ces décorateurs de langage sont plus proches de métaprogrammation et d'Aspect-Oriented Programming.
// Décorateur TypeScript (métaprogrammation)
function Log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
console.log(`Appel de ${propertyKey} avec`, args);
const result = originalMethod.apply(this, args);
console.log(`Résultat:`, result);
return result;
};
return descriptor;
}
class Calculator {
@Log
add(a: number, b: number): number {
return a + b;
}
}
const calc = new Calculator();
calc.add(5, 3);
// Appel de add avec [5, 3]
// Résultat: 8Distinction Importante
Ne confondez pas le pattern Decorator (composition d'objets) avec les décorateurs de langage (@decorator). Le premier est un pattern de conception orienté objet, les seconds sont une fonctionnalité de métaprogrammation. Ils résolvent des problèmes différents, même si le nom est similaire.
Pattern Alternatifs et Comparaisons
- Proxy : similaire mais se concentre sur le contrôle d'accès plutôt que l'ajout de fonctionnalités
- Chain of Responsibility : transmet des requêtes le long d'une chaîne plutôt que d'enrichir un objet
- Composite : structure hiérarchique d'objets plutôt qu'empilement linéaire de comportements
- Strategy : change l'algorithme interne plutôt que d'ajouter des couches externes
Le pattern Decorator offre une solution élégante pour étendre les fonctionnalités sans créer une hiérarchie complexe de classes. En permettant la composition dynamique de comportements, il apporte flexibilité et maintenabilité aux architectures logicielles. Pour les équipes cherchant à construire des systèmes extensibles respectant les principes SOLID, le Decorator représente un outil architectural incontournable qui favorise la réutilisabilité du code et réduit la dette technique à long terme.
