Zoeken…


Invoering

Met het decoratorpatroon kan een gebruiker nieuwe functionaliteit toevoegen aan een bestaand object zonder de structuur te wijzigen. Dit type ontwerppatroon valt onder een structureel patroon omdat dit patroon als een verpakking voor de bestaande klasse fungeert.

Dit patroon creëert een decoratorklasse die de oorspronkelijke klasse omhult en biedt extra functionaliteit om de handtekening van de klassenmethode intact te houden.

parameters

Parameter Beschrijving
Drank het kan thee of koffie zijn

VendingMachineDecorator

Definitie van Decorator volgens Wikiepdia:

Het Decorator-patroon kan worden gebruikt om de functionaliteit van een bepaald object statisch uit te breiden (decoreren), of in sommige gevallen tijdens runtime, onafhankelijk van andere instanties van dezelfde klasse, op voorwaarde dat er tijdens de ontwerpfase enig grondwerk wordt gedaan.

Decorateur koppelt dynamisch extra verantwoordelijkheden aan een object. Decorateurs bieden een flexibel alternatief voor subklasse voor uitbreiding van de functionaliteit.

Decorateurpatroon bevat vier componenten.

voer hier de afbeeldingsbeschrijving in

  1. Componentinterface: het definieert een interface voor het uitvoeren van bepaalde bewerkingen
  2. ConcreteComponent: het implementeert de bewerkingen die zijn gedefinieerd in de componentinterface
  3. Decorator (Abstract): het is een abstracte klasse die de componentinterface uitbreidt. Het bevat een componentinterface. Bij afwezigheid van deze klasse heeft u veel subklassen van ConcreteDecorators nodig voor verschillende combinaties. Samenstelling van component vermindert onnodige subklassen.
  4. ConcreteDecorator: het bevat de implementatie van Abstract Decorator.

Terugkomend op voorbeeldcode,

  1. Drank is component. Het definieert een abstracte methode: decorateBeverage
  2. Thee en koffie zijn concrete implementaties van Beverage .
  3. BeverageDecorator is een abstracte klasse die Beverage bevat
  4. SugarDecorator en LemonDecorator zijn betonnen decorateurs voor BeverageDecorator.

BEWERKEN: Het voorbeeld is gewijzigd om het echte scenario van de berekening van de drankprijs weer te geven door een of meer smaken toe te voegen, zoals suiker, citroen enz. (Smaken zijn decorateurs)

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();
    }
}

output:

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

In dit voorbeeld worden de drankkosten in de automaat berekend nadat veel smaken aan de drank zijn toegevoegd.

In bovenstaand voorbeeld:

Kostprijs thee = 10, citroen = 3 en suiker = 5. Als je suiker + citroen + thee maakt, kost dat 18.

Kosten van koffie = 15, citroen = 3 en suiker = 5. Als u suiker + citroen + koffie maakt, kost dit 23

Door dezelfde Decorator voor beide dranken (thee en koffie) te gebruiken, is het aantal subklassen verminderd. Bij afwezigheid van Decorator-patroon, zou u verschillende subklassen voor verschillende combinaties moeten hebben.

De combinaties zijn als volgt:

SugarLemonTea
SugarTea
LemonTea

SugarLemonCapaccuino
SugarCapaccuino
LemonCapaccuino

enz.

Door dezelfde Decorator voor beide dranken te gebruiken, is het aantal subklassen verminderd. Het is mogelijk vanwege de composition plaats van het inheritance dat in dit patroon wordt gebruikt.

Vergelijking met andere ontwerppatronen (van sourcemaking- artikel)

  1. Adapter biedt een andere interface dan het onderwerp. Proxy biedt dezelfde interface. Decorator biedt een verbeterde interface.

  2. Adapter verandert de interface van een object, Decorator verbetert de verantwoordelijkheden van een object.

  3. Composiet en Decorateur hebben vergelijkbare structuurdiagrammen, wat het feit weerspiegelt dat beide afhankelijk zijn van recursieve compositie om een open aantal objecten te organiseren

  4. Decorator is ontworpen om u verantwoordelijkheden aan objecten toe te voegen zonder subklasse. Composiet richt zich niet op verfraaiing maar op representatie

  5. Decorateur en Proxy hebben verschillende doeleinden maar vergelijkbare structuren

  6. Met Decorator kunt u de skin van een object wijzigen. Strategie laat je het lef veranderen.

Belangrijkste gebruiksgevallen:

  1. Voeg dynamisch extra functies / verantwoordelijkheden toe
  2. Verwijder functies / verantwoordelijkheden dynamisch
  3. Vermijd te veel onderverdeling om extra verantwoordelijkheden toe te voegen.

Caching Decorator

Dit voorbeeld laat zien hoe u DbProductRepository aan DbProductRepository kunt toevoegen met behulp van het Decorator-patroon. Deze aanpak houdt vast aan SOLID-principes, omdat u caching kunt toevoegen zonder het Single verantwoordelijkheidsprincipe of Open / closed-principe te schenden.

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)
}

Gebruik:

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

Het resultaat van het aanroepen van GetProduct is: haal het product uit de cache (verantwoordelijkheid van de decorateur), als het product niet in de cache stond, ga dan verder met aanroep naar DbProductRepository en haal het product uit de DB. Nadat dit product aan de cache kan worden toegevoegd, zullen daaropvolgende oproepen DB niet raken.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow