Recherche…


Introduction

Le motif de décorateur permet à un utilisateur d’ajouter de nouvelles fonctionnalités à un objet existant sans modifier sa structure. Ce type de motif de conception est inclus dans le motif structurel, car ce motif agit comme une enveloppe pour la classe existante.

Ce modèle crée une classe de décorateur qui enveloppe la classe d'origine et fournit des fonctionnalités supplémentaires en gardant intacte la signature des méthodes de classe.

Paramètres

Paramètre La description
Boisson ce peut être du thé ou du café

VendingMachineDecorator

Définition du décorateur selon Wikiepdia:

Le motif Decorator peut être utilisé pour étendre (décorer) de manière statique ou, dans certains cas, au moment de l'exécution, les fonctionnalités d'un objet donné, indépendamment des autres instances de la même classe, à condition que le travail de conception soit effectué.

Le décorateur attribue des responsabilités supplémentaires à un objet de manière dynamique. Les décorateurs offrent une alternative flexible au sous-classement pour étendre les fonctionnalités.

Le motif de décorateur contient quatre composants.

entrer la description de l'image ici

  1. Interface de composant: définit une interface pour exécuter des opérations particulières
  2. ConcreteComponent: implémente les opérations définies dans l'interface de composant
  3. Decorator (Abstract): c'est une classe abstraite qui étend l'interface du composant. Il contient une interface de composant. En l'absence de cette classe, vous avez besoin de plusieurs sous-classes de ConcreteDecorator pour différentes combinaisons. La composition du composant réduit les sous-classes non nécessaires.
  4. ConcreteDecorator: Il contient l'implémentation de Abstract Decorator.

Revenons à l'exemple de code,

  1. La boisson est un composant. Il définit une méthode abstraite: decorateBeverage
  2. Le thé et le café sont des implémentations concrètes de boissons .
  3. BeverageDecorator est une classe abstraite contenant des boissons
  4. SugarDecorator et LemonDecorator sont des décorateurs concrets de BeverageDecorator.

EDIT: Changé l'exemple pour refléter le scénario réel de calcul du prix des boissons en ajoutant un ou plusieurs arômes tels que le sucre, le citron, etc. (les arômes sont des décorateurs)

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 de la boisson dans Vending Machine après avoir 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é, il en 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 de décorateur, vous devriez avoir différentes sous-classes pour différentes combinaisons.

Les combinaisons seront comme ceci:

SugarLemonTea
SugarTea
LemonTea

SugarLemonCapaccuino
SugarCapaccuino
LemonCapaccuino

etc.

En utilisant le même Decorator pour les deux boissons, le nombre de sous-classes a été réduit. C'est possible en raison de la composition plutôt que du concept d' inheritance utilisé dans ce modèle.

Comparaison avec d'autres modèles de conception (à partir d'article de fabrication )

  1. L'adaptateur fournit une interface différente à son sujet. Proxy fournit la même interface. Le décorateur fournit une interface améliorée.

  2. L'adaptateur modifie l'interface d'un objet, Decorator améliore les responsabilités d'un objet.

  3. Composite et Decorator ont des diagrammes de structure similaires, reflétant le fait que les deux utilisent une composition récursive pour organiser un nombre illimité d’objets.

  4. Decorator est conçu pour vous permettre d'ajouter des responsabilités à des objets sans sous-classement. L' accent de Composite n'est pas sur l'embellissement mais sur la représentation

  5. Le décorateur et le mandataire ont des objectifs différents mais des structures similaires

  6. Le décorateur vous permet de changer la peau d'un objet. La stratégie vous permet de changer les tripes.

Principaux cas d'utilisation:

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

Caching Decorator

Cet exemple montre comment ajouter des fonctionnalités de mise en cache à DbProductRepository aide du motif Decorator. Cette approche respecte les principes SOLID car elle vous permet d’ajouter de la mise en cache sans violer le principe de responsabilité unique ou le principe ouvert / fermé .

public interface IProductRepository
{
    Product GetProduct(int id);
}

public class DbProductRepository : IProductRepository
{
    public Product GetProduct(int id)
    {
        //return Product retrieved from DB
    }
}

public class ProductRepositoryCachingDecorator : IProductRepository
{
    private readonly IProductRepository _decoratedRepository;
    private readonly ICache _cache;
    private const int ExpirationInHours = 1;

    public ProductRepositoryCachingDecorator(IProductRepository decoratedRepository, ICache cache)
    {
        _decoratedRepository = decoratedRepository;
        _cache = cache;
    }

    public Product GetProduct(int id)
    {
        var cacheKey = GetKey(id);
        var product = _cache.Get<Product>(cacheKey);
        if (product == null)
        {
            product = _decoratedRepository.GetProduct(id);
            _cache.Set(cacheKey, product, DateTimeOffset.Now.AddHours(ExpirationInHours));
        }
        
        return product;
    }

    private string GetKey(int id) => "Product:" + id.ToString();
}

public interface ICache
{
    T Get<T>(string key);
    void Set(string key, object value, DateTimeOffset expirationTime)
}

Usage:

var productRepository = new ProductRepositoryCachingDecorator(new DbProductRepository(), new Cache());
var product = productRepository.GetProduct(1);

Le résultat de l'appel de GetProduct sera le suivant: récupérer le produit à partir du cache (responsabilité du décorateur), si le produit n'était pas dans le cache, procéder à l'appel à DbProductRepository et extraire le produit de la base de données. Une fois que ce produit peut être ajouté au cache, les appels suivants n'atteindront pas le DB.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow