web-dev-qa-db-fra.com

Quand utiliser le motif de décorateur?

Je passe en revue mes modèles de conception, et un modèle que je n'ai pas encore utilisé sérieusement dans mon codage est le modèle de décorateur. 

Je comprends le motif, mais ce que j'aimerais bien savoir, ce sont quelques bons exemples concrets de fois dans le monde réel où le motif de décorateur est la solution optimale/optimale/élégante. Des situations spécifiques où la nécessité du motif de décorateur est vraiment pratique.

Merci.

54
Alex Baranosky

Le motif décoratif est beaucoup utilisé avec les flux: vous pouvez envelopper un flux avec un flux pour bénéficier de fonctionnalités supplémentaires. J'ai vu cela avec le framework .Net - autant que je sache, cela se produit ailleurs. Mon préféré est d'utiliser GZipStream autour d'un FileStream pour une compression accrue.

36
Charlie Salts

Le motif Decorator est utilisé pour ajouter des fonctionnalités supplémentaires à un objet particulier, par opposition à une classe d'objets. Il est facile d'ajouter des fonctionnalités à une classe entière d'objets en sous-classant un objet, mais il est impossible d'étendre un seul objet de cette façon. Avec le motif de décorateur, vous pouvez ajouter des fonctionnalités à un seul objet et laisser les autres sans modification.

En Java, un exemple classique de modèle de décorateur est l'implémentation Java I/O Streams.

FileReader       frdr = new FileReader(filename);
LineNumberReader lrdr = new LineNumberReader(frdr);

Le code précédent crée un lecteur - lrdr - qui lit un fichier et trace les numéros de ligne. La ligne 1 crée un lecteur de fichiers (frdr) et la ligne 2 ajoute le suivi du numéro de ligne. 

En fait, je vous encourage vivement à consulter le code source Java pour les classes d'E/S Java.

66
Pascal Thivent

J'ai récemment utilisé le modèle de décorateur dans un service Web utilisant l'interface CommandProcessor suivante:

public Command receive(Request request);
public Response execute(Command command);
public void respond(Response response);

Fondamentalement, le CommandProcessor reçoit une demande et crée la commande appropriée, l'exécute, crée la réponse appropriée et envoie la réponse. Lorsque j'ai voulu ajouter du temps et le consigner, j'ai créé un TimerDecorator qui utilisait un CommandProcessor existant en tant que composant. TimerDecorator implémente l'interface CommandProcessor, mais ajoute simplement du temps et appelle ensuite sa cible, qui est le véritable CommandProcessor. Quelque chose comme ça:

public class TimerDecorator implements CommandProcessor {
   private CommandProcessor target;
   private Timer timer;

   public TimerDecorator(CommandProcessor processor) {
      this.target = processor;
      this.timer = new Timer();
   }

   public Command receive(Request request) {
      this.timer.start();
      return this.target.receive(request);
   }

   public Response execute(Command command) {
      return this.target.execute(command);
   }

   public void respond(Response response) {
      this.target.response(response);
      this.timer.stop();
      // log timer
   }

}

Ainsi, le véritable CommandProcessor est intégré à TimerDecorator et je peux traiter TimerDecorator exactement comme le CommandProcessor cible, mais la logique de minutage a maintenant été ajoutée.

24
Kaleb Brasee

Le décorateur est simple mais extrêmement puissant. C'est un élément clé pour séparer les préoccupations et constitue un outil essentiel pour le principe de fermeture. Prenons un exemple courant de commande d'un produit:

IOrderGateway
{
    void PlaceOrder(Order order);
{

Implémentation principale: AmazonAffiliateOrderGateway

Les décorateurs possibles pourraient être:

  • IncrementPerformanceCounterOrderGateway
  • QueueOrderForLaterOnTimeoutOrderGateway
  • EmailOnExceptionOrderGateway
  • InterceptTestOrderAndLogOrderGateway

Jetez un coup d'oeil ici pour un exemple plus détaillé.

7
Mike Valenty

Decorator pattern modifie de manière dynamique la fonctionnalité d'un objet au moment de l'exécution sans affecter la fonctionnalité existante de ces objets.

Cas d'utilisation clés: _

  1. Ajouter des fonctionnalités/responsabilités supplémentaires dynamiquement
  2. Supprimer les fonctionnalités/responsabilités de manière dynamique
  3. Évitez trop de sous-classes pour ajouter des responsabilités supplémentaires.

Désavantages:

  1. Surutilisation de Open Principe fermé (ouvert pour extension et fermé pour modification). Utilisez cette fonctionnalité avec parcimonie là où le code est le moins susceptible d’être modifié.
  2. Trop de petites classes et ajouteront frais généraux de maintenance

Un exemple concret: Calcule le prix d'une boisson, qui peut contenir plusieurs saveurs. 

abstract class Beverage {
    protected String name;
    protected int price;
    public Beverage(){

    }
    public  Beverage(String name){
        this.name = name;
    }
    public void setName(String name){
        this.name = name;
    }
    public String getName(){
        return name;
    }
    protected void setPrice(int price){
        this.price = price;
    }
    protected int getPrice(){
        return price;
    }
    protected abstract void decorateBeverage();

}
class Tea extends Beverage{
    public Tea(String name){
        super(name);
        setPrice(10);
    }
    public void decorateBeverage(){
        System.out.println("Cost of:"+ name +":"+ price);
        // You can add some more functionality
    }
}
class Coffee extends Beverage{
    public Coffee(String name){
        super(name);
        setPrice(15);
    }
    public void decorateBeverage(){
        System.out.println("Cost of:"+ name +":"+ price);
        // You can add some more functionality
    }   
}
abstract class BeverageDecorator extends Beverage {
    protected Beverage beverage;
    public BeverageDecorator(Beverage beverage){    
        this.beverage = beverage;   
        setName(beverage.getName()+"+"+getDecoratedName());
        setPrice(beverage.getPrice()+getIncrementPrice());
    }
    public void decorateBeverage(){
        beverage.decorateBeverage();
        System.out.println("Cost of:"+getName()+":"+getPrice());
    }   
    public abstract int getIncrementPrice();
    public abstract String getDecoratedName();
}
class SugarDecorator extends BeverageDecorator{
    public SugarDecorator(Beverage beverage){
        super(beverage);
    }
    public void decorateBeverage(){
        super.decorateBeverage();
        decorateSugar();        
    }
    public void decorateSugar(){
        System.out.println("Added Sugar to:"+beverage.getName());
    }
    public int getIncrementPrice(){
        return 5;
    }
    public String getDecoratedName(){
        return "Sugar";
    }
}
class LemonDecorator extends BeverageDecorator{
    public LemonDecorator(Beverage beverage){
        super(beverage);
    }
    public void decorateBeverage(){
        super.decorateBeverage();
        decorateLemon();    
    }
    public void decorateLemon(){
        System.out.println("Added Lemon to:"+beverage.getName());       
    }
    public int getIncrementPrice(){
        return 3;
    }
    public String getDecoratedName(){
        return "Lemon";
    }
}

public class VendingMachineDecorator {  
    public static void main(String args[]){
        Beverage beverage = new SugarDecorator(new LemonDecorator(new Tea("Assam Tea")));
        beverage.decorateBeverage();
        beverage = new SugarDecorator(new LemonDecorator(new Coffee("Cappuccino")));
        beverage.decorateBeverage();
    }
}

sortie:

Cost of:Assam Tea:10
Cost of:Assam Tea+Lemon:13
Added Lemon to:Assam Tea
Cost of:Assam Tea+Lemon+Sugar:18
Added Sugar to:Assam Tea+Lemon
Cost of:Cappuccino:15
Cost of:Cappuccino+Lemon:18
Added Lemon to:Cappuccino
Cost of:Cappuccino+Lemon+Sugar:23
Added Sugar to:Cappuccino+Lemon

Cet exemple calcule le coût d'une boisson dans un distributeur automatique après l'ajout de nombreuses saveurs à la boisson.

Dans l'exemple ci-dessus:

Coût du thé = 10, citron = 3 et sucre = 5. Si vous faites du sucre + citron + thé, cela coûte 18.

Coût du café = 15, citron = 3 et sucre = 5. Si vous faites du sucre + citron + café, cela coûte 23

En utilisant le même décorateur pour les deux boissons (thé et café), le nombre de sous-classes a été réduit. En l'absence de motif Décorateur, vous devez avoir différentes sous-classes pour différentes combinaisons. 

Les combinaisons seront comme ceci:

SugarLemonTea
SugarTea
LemonTea

SugarLemonCapaccuino
SugarCapaccuino
LemonCapaccuino

etc. 

En utilisant la même Décorateur pour les deux boissons, le nombre de sous-classes a été réduit. C'est possible grâce au concept composition_ _ plutôt qu'à héritage utilisé dans ce modèle.

Question SE connexe: 

Motif de décorateur pour IO

Liens utiles:

design-patterns-decorator par dzone

décorateur par sourcemaking

oodesign article

7
Ravindra babu

Zend Framework utilise le décorateur pour les éléments de formulaire

Quelques informations supplémentaires: http://framework.zend.com/manual/en/zend.form.decorators.html

3
Mike B
  1. pour ajouter des responsabilités à des objets individuels de manière dynamique et transparente.
  2. pour les responsabilités qui peuvent être retirées.
  3. quand l'extension par sous-classe n'est pas pratique. Parfois, un grand nombre d'extensions indépendantes sont possibles et produiraient une explosion de sous-classes pour supporter chaque combinaison.
2
Karim Haddad

Le motif Decorator est utilisé par le langage C # lui-même. Il est utilisé pour décorer la classe Stream I/O de C #. Les versions décorées sont classées BufferedStream, FileStrem, MemoryStrem, NetworkStream et CryptoStream.

Ces sous-classes héritent de la classe Stream et contiennent également une instance de la classe Stream.

En savoir plus ici

0
Ayushmati