image de chargement
Retour au glossaire

Decorator (Décorateur)

Pattern structurel permettant d'ajouter dynamiquement des fonctionnalités à un objet sans modifier sa structure de base.

Mis à jour le 9 janvier 2026

Le pattern Decorator est un design pattern structurel qui permet d'étendre les fonctionnalités d'objets individuels de manière flexible et réutilisable, sans affecter les autres instances de la même classe. Contrairement à l'héritage qui ajoute des comportements à toute une classe, le décorateur enveloppe l'objet cible dans une série de couches fonctionnelles, chacune ajoutant un comportement spécifique. Ce pattern respecte le principe ouvert/fermé en permettant l'extension sans modification du code existant.

Fondements du Pattern

  • Composition sur héritage : utilise l'encapsulation d'objets plutôt que l'extension de classes pour ajouter des comportements
  • Transparence : les décorateurs implémentent la même interface que l'objet décoré, permettant leur utilisation interchangeable
  • Chaînage : plusieurs décorateurs peuvent s'empiler pour combiner leurs fonctionnalités de manière modulaire
  • Responsabilité unique : chaque décorateur a une responsabilité claire et indépendante

Avantages Techniques et Métier

  • Flexibilité maximale : ajout ou retrait de fonctionnalités à l'exécution sans recompilation
  • Évite l'explosion combinatoire : remplace les dizaines de sous-classes par des décorateurs composables
  • Testabilité accrue : chaque décorateur peut être testé isolément et en composition
  • Réutilisabilité : les décorateurs peuvent être appliqués à différents objets compatibles
  • Maintenance simplifiée : modifications localisées sans impact sur le code existant

Exemple Concret en TypeScript

notification-decorator.ts
// 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 abstrait
abstract class NotifierDecorator implements Notifier {
  constructor(protected wrapped: Notifier) {}
  
  send(message: string): void {
    this.wrapped.send(message);
  }
}

// Décorateurs concrets
class SlackDecorator extends NotifierDecorator {
  send(message: string): void {
    super.send(message);
    console.log(`Slack: ${message}`);
  }
}

class SMSDecorator extends NotifierDecorator {
  send(message: string): void {
    super.send(message);
    console.log(`SMS: ${message}`);
  }
}

class LogDecorator extends NotifierDecorator {
  send(message: string): void {
    console.log(`[LOG] Sending at ${new Date().toISOString()}`);
    super.send(message);
  }
}

// Utilisation avec composition dynamique
let notifier: Notifier = new EmailNotifier();
notifier = new SlackDecorator(notifier);
notifier = new SMSDecorator(notifier);
notifier = new LogDecorator(notifier);

notifier.send("System alert!");
// Output:
// [LOG] Sending at 2024-01-15T10:30:00.000Z
// Email: System alert!
// Slack: System alert!
// SMS: System alert!

Mise en Œuvre Pratique

  1. Définir l'interface commune que partageront le composant de base et les décorateurs
  2. Implémenter le composant concret de base avec le comportement fondamental
  3. Créer une classe décorateur abstraite qui implémente l'interface et contient une référence au composant enveloppé
  4. Développer des décorateurs concrets qui étendent le décorateur abstrait et ajoutent leurs comportements spécifiques
  5. Composer les décorateurs par empilement successif pour obtenir la fonctionnalité désirée
  6. Assurer que chaque décorateur appelle la méthode du composant enveloppé (généralement via super.method())

Conseil d'Architecture

En TypeScript/JavaScript moderne, les décorateurs de classe (@decorator) offrent une syntaxe élégante pour des cas spécifiques (métadonnées, logging), mais le pattern Decorator classique reste préférable pour la composition dynamique de comportements à l'exécution. Combinez les deux approches selon le contexte : décorateurs syntaxiques pour les aspects transverses statiques, pattern Decorator pour la flexibilité runtime.

Cas d'Usage Courants

  • Streams et I/O : BufferedReader, GZipInputStream en Java encapsulent des flux basiques
  • Middleware HTTP : Express.js utilise ce pattern pour le chaînage de middlewares
  • UI Components : ajout de bordures, scrollbars, ombres à des composants graphiques
  • Caching et logging : enrichissement transparent de services avec mise en cache ou traçabilité
  • Feature flags : activation/désactivation conditionnelle de fonctionnalités

Outils et Frameworks Associés

  • NestJS : utilisation extensive des décorateurs pour injection de dépendances et métadonnées
  • TypeORM / TypeGraphQL : décorateurs pour mapping objet-relationnel et schémas GraphQL
  • Redux : pattern observable avec enhancers qui décorent le store
  • Java Streams API : décorateurs fonctionnels pour transformation de flux de données
  • Python functools.wraps : décoration de fonctions pour aspect-oriented programming

Attention aux Pièges

L'empilement excessif de décorateurs peut créer des chaînes d'appels complexes difficiles à déboguer. Documentez clairement l'ordre d'application des décorateurs et leurs dépendances. Surveillez également les impacts performance : chaque couche ajoute un niveau d'indirection. Privilégiez les décorateurs légers et envisagez des alternatives (Proxy pattern, AOP) pour les cas lourds.

Valeur Métier

Le pattern Decorator réduit considérablement le time-to-market pour les nouvelles fonctionnalités en permettant leur ajout sans refactorisation majeure. Les équipes produit peuvent activer progressivement des capacités (A/B testing, déploiements canary) via composition de décorateurs. Cette modularité favorise la réutilisation inter-projets, diminuant les coûts de développement récurrents. Pour les systèmes critiques, la capacité à composer dynamiquement des comportements (monitoring, circuit breakers, retry logic) améliore la résilience opérationnelle tout en maintenant une base de code épurée et maintenable.

L'argentestdéjàsurlatable.

En 1 heure, découvrez exactement combien vous perdez et comment le récupérer.