Accueil > Le design pattern Decorator (Décorateur)
Boris Dhambahadour

Le design pattern Decorator (Décorateur)

Le design pattern Decorator (Décorateur)

Qu’est-ce que le Design Pattern ?

Théorisé dans les années 1970 par l’Architecte Christopher Alexander, le concept de Design Pattern s’applique dans tous les processus de conception de forme. Il se matérialise donc dans différents domaines comme celui de l’anthropologie, le design ou encore l’informatique.

Cette théorie l’a amené à modéliser 253 patterns, dont 23 d’entre eux vont être repris et étendus au domaine de la conception logicielle par le « Gang of Four ».

Aujourd’hui utilisé par les Designers et Développeurs, les modèles de Design Pattern les aident à améliorer l’interface des sites ou applications en les rendant plus intuitives.

Chaque patron décrit un problème qui se manifeste constamment dans notre environnement et donc décrit le cœur de la solution à ce problème, d’une façon telle que l’on puisse réutiliser cette solution des millions de fois sans jamais le faire deux fois de la même manière.

Christopher Alexander, 1977

 

Quand utiliser le Design Pattern Decorator ?

Après avoir publié un article sur le Design Pattern Adapter, on a décidé d’en publier un nouveau sur le Design Pattern Decorator.

Très souvent, lorsque l’on souhaite ajouter des fonctionnalités à une classe, on pense héritage – et c’est un bon réflexe !

Mais, très souvent également, cette solution n’est pas réellement adaptée : on peut souhaiter ajouter plusieurs fonctionnalités à la même classe de manière dynamique (runtime) et non pas à l’écriture du programme (ajout de fonctionnalité statique). En choisissant les nouvelles fonctionnalités pour chaque instance et non pour la classe.

C’est dans ce cas qu’il est pertinent de faire appel au design pattern Decorator (pattern du GoF de type structure).

 

Présentation du Design Pattern Decorator

 

Le pattern Decorator est un patron de conception de structure qui fait partie des design pattern du GoF (Gang of Four).

L’avantage du Design Pattern Decorator : l’ajout simplifié des nouvelles fonctionnalités qu’on souhaite intégrer à une classe, et cela de façon dynamique sans impacter les classes qui l’utilisent ou en héritent.

Utiliser le Design Pattern Decorator c’est aussi la solution pour éviter de complexifier son modèle et donc respecter le principe SOLID « open close principle ». Pour allez plus loin sur ce principe, découvrez notre série d’articles sur SOLIDITE.

Pour mieux comprendre comment il fonctionne, j’ai créé un programme simple pour vous aider à le mettre en place.

Implémentation du Design Pattern Decorator

Présentation Design Pattern Decorator

 

 

Afin de comprendre son fonctionnement, prenons l’exemple d’un vendeur de HotDog. Le décorateur représente donc tout ce qu’on souhaite ajouter en supplément sur nos différents HotDogs.

 

Voici les différents suppléments :

  • HotDogsComponent : définit la classe abstraite du composant de base c’est-à-dire notre matière première pour le HotDog.
  • HotDogMeuh, HotDogBase, HotDogBzz : héritent de HotDogsComponent et sont des composants concrets autrement dit nos différents HotDog proposés à la vente.
  • Decorator : est une classe abstraite qui hérite de HotDogsComponent, elle permet de décorer le composant de base, qu’on assimile à l’ajout d’un supplément à nos HotDog.
  • MoutardeCidreMielDecorator, MoutardeGingembreDecorator : héritent de Décorator, ils permettent d’agrémenter notre élément de base sans modifier notre classe de base. En l’occurrence, le supplément ici sera de la sauce avec un coût supplémentaire s’ajoutant au prix du HotDog choisi.

 

Design Pattern Decorator : découverte du code

Le composant de base

Ci-après, notre classe abstraite qui représente le composant de base : un HotDog avec un nom et un prix. Dans notre exemple, le composant de base est « HotDogsComponent ».

public abstract class HotDogsComponent

{
    protected string h_nom;
    public virtual string GetName()
    {
       return h_nom;
    }

    protected double h_prix;
    public virtual double GetPrice()
    {
       return h_prix;
    }
}

 

Les classes : héritage du composant de base

Nous avons 3 classes concrètes qui héritent du composant de base, auxquelles on affecte un prix et un nom.

Voici comment ajouter HotDogBase, HotDogMeuh, HotDogBzz dans votre code :

class HotDogBase : HotDogsComponent
{
   public HotDogBase()
   {
       h_nom = "Hot Dog Base";
       h_prix = 5.0;
   }
} 

class HotDogBzz : HotDogsComponent
{
    public HotDogBzz()
    {
       h_nom = "Hot Dog Bzz";
       h_prix =7.5;
    }
}

class HotDogMeuh : HotDogsComponent
{
   public HotDogMeuh()
   {
       h_nom = Hot Dog Meuh";
       h_prix = 6.5;
   }
}

 

Decorator

Notre Decorator hérite de notre composant de base auquel on fixe des règles, à savoir l’ajout d’un nom de sauce à la suite du nom du hotdog et le supplément ajouté au prix du hotdog initial :

public abstract class Decorator : HotDogsComponent
{
   HotDogsComponent h_BaseComponent = null;

   protected string h_Nom = "Decorateur indéfini";
   protected double h_prix = 0.0;

   protected Decorator(HotDogsComponent hotdog)
   {
      h_BaseComponent = hotdog;
   }

#region membres

   public override string GetName()
   {
      return string.Format("{0}, {1}", h_BaseComponent.GetName(), h_Nom);
   }

   public override double GetPrice()
   {
      return h_prix + h_BaseComponent.GetPrice();
   }
#endregion
}

 

 Les classes hériant du Decorator

Nous avons deux classes concrètes héritant de Decorator, auxquelles on affecte un prix et un nom.

Dans notre exemple, les deux classes sont les suivantes :

  • MoutardeCidreMiedDecorator
  • MoutardeGingembreDecorator

Voici comment les intégrer :

class MoutardeCidreMielDecorator : Decorator
{
   public MoutardeCidreMielDecorator(HotDogsComponent hotdogmoutarde)
: base(hotdogmoutarde)
   {
      this.h_Nom = "Moutarde Cidre Miel";
      this.h_prix = 5.0;
   }
}

class MoutardeGingembreDecorator : Decorator
{
    public MoutardeGingembreDecorator(HotDogsComponent hotDog)
: base(hotDog)
   {
      this.h_Nom = "Moutarde Gingembre";
      this.h_prix = 4.0;
   }
}

 

Program.cs : l’application console

Enfin, nous créons une application console qui nous affiche les différents choix de HotDog avec ou sans supplément :

class Program
{
   static void Main(string[] args)
{
   HotDogBase hBase = new HotDogBase();
   PrintHotDogDetails(hBase);

   HotDogMeuh hotDogMeuh = new HotDogMeuh();
   PrintHotDogDetails(hotDogMeuh);

   HotDogBzz hotDogBzz = new HotDogBzz();
   PrintHotDogDetails(hotDogBzz);

   MoutardeGingembreDecorator hotDogMoutardeGingembre = new MoutardeGingembreDecorator(hotDogMeuh);
   PrintHotDogDetails(hotDogMoutardeGingembre);

   MoutardeCidreMielDecorator hotCidreMielDecorator = new MoutardeCidreMielDecorator(hotDogBzz);
   PrintHotDogDetails(hotCidreMielDecorator);

}
   private static void PrintHotDogDetails(HotDogsComponent hotdog)
   {
      Console.WriteLine();
      Console.WriteLine("Sandwich: {0}, Prix: {1}", hotdog.GetName(), hotdog.GetPrice());
   }
}

 

Résultat

HotDog

 

Design Pattern Decorator : gage de flexibilité

Ce pattern est très intéressant à utiliser lors de la conception de classes qui risquent d’évoluer fortement par ajout et modification dynamique de fonctionnalités. C’est un patron de conception très souple.

De cette manière, avec un arbre d’héritage peu complexe, on a la possibilité d’avoir des compositions dynamiques très complexes tout en respectant le principe « open close ».

Vous l’aurez compris, un des avantages de ce pattern est l’aspect dynamique et optionnel des fonctionnalités que l’on ajoute. Si on le met en place suffisamment tôt lorsque l’on développe, la solution reste simple et élégante.

Dans notre cas, l’intérêt premier est de pouvoir étendre les fonctionnalités offertes par la classe HotDogComponent sans la modifier et ainsi éviter de modifier le reste du système en respectant le principe « Open Close ».

Cependant, il faut bien réfléchir aux points sensibles de l’application dès la phase d’analyse pour éviter une conception trop complexe.

Nos autres articles
Commentaires

Quel est l’Intérêt de ce design comparé a une approche plus classique qui consisterait a dire qu’un hot dog se compose d’un hot dog de base + une liste d’ingrédients supplémentaires ?

Je trouve ça ultra compliqué et peu pratique a utiliser. Par exemple, ajouter ou supprimer des ingrédients de manière interactive devient plus dur, changer le hot dog de base aussi….

Bref, a mon avis c’est un pattern qu’il faut utiliser avec précautions.

Bonjour,
En effet, quand on peut ce le permettre, la solution que vous proposez est plus logique.
Cependant, qu’en est-il du cas ou ne pouvons pas modifier la classe de base(=> hotdog component)? Dans ce cas-là, le décorateur est une solution élégante

Laisser un commentaire

Restez au courant des dernières actualités !
Le meilleur de l’actualité sur le Cloud, le DevOps, l’IT directement dans votre boîte mail.