Sök…


Introduktion

Dekoratörsmönster tillåter en användare att lägga till ny funktionalitet till ett befintligt objekt utan att ändra dess struktur. Denna typ av designmönster kommer under strukturella mönster eftersom detta mönster fungerar som ett omslag till befintlig klass.

Det här mönstret skapar en dekoratörsklass som förpackar den ursprungliga klassen och ger ytterligare funktionalitet som håller klassmetodens signatur intakt.

parametrar

Parameter Beskrivning
Dryck det kan vara te eller kaffe

VendingMachineDecorator

Definition av dekoratör enligt Wikiepdia:

Dekoratormönstret kan användas för att förlänga (dekorera) funktionaliteten för ett visst objekt statiskt, eller i vissa fall vid körning, oberoende av andra instanser i samma klass, förutsatt att vissa grundarbeten görs vid designtid.

Dekoratören lägger ytterligare ett ansvar till ett objekt dynamiskt. Dekoratörer erbjuder ett flexibelt alternativ till underklassning för att utöka funktionaliteten.

Dekoratormönster innehåller fyra komponenter.

ange bildbeskrivning här

  1. Komponentgränssnitt: Det definierar ett gränssnitt för att utföra vissa operationer
  2. ConcreteComponent: Den implementerar de operationer som definieras i komponentgränssnittet
  3. Dekoratör (abstrakt): det är en abstrakt klass som utvidgar komponentgränssnittet. Det innehåller komponentgränssnitt. I avsaknad av denna klass behöver du många underklasser av ConcreteDecorators för olika kombinationer. Kompositionens sammansättning minskar onödiga underklasser.
  4. ConcreteDecorator: Det har implementeringen av Abstract Decorator.

Kommer tillbaka till exempelkod,

  1. Dryck är en komponent. Den definierar en abstrakt metod: dekorera Beverage
  2. Te och kaffe är konkreta implementeringar av dryck .
  3. BeverageDecorator är en abstrakt klass som innehåller dryck
  4. SugarDecorator och LemonDecorator är betongdekoratörer för BeverageDecorator.

EDIT: Ändrade exemplet för att återspegla den verkliga scenariot för att beräkna priset på dryck genom att lägga till en eller flera smaker som socker, citron etc (smaker är dekoratörer)

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

produktion:

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

I det här exemplet beräknas dryckekostnaderna i varuautomaten efter att många smaker har lagts till drycken.

I exemplet ovan:

Kostnad för te = 10, citron = 3 och socker = 5. Om du gör socker + citron + te, kostar det 18.

Kostnad för kaffe = 15, citron = 3 och socker = 5. Om du gör socker + citron + kaffe kostar det 23

Genom att använda samma dekoratör för båda dryckerna (te och kaffe) har antalet underklasser minskats. I avsaknad av dekoratörsmönster bör du ha olika underklasser för olika kombinationer.

Kombinationerna kommer att vara så här:

SugarLemonTea
SugarTea
LemonTea

SugarLemonCapaccuino
SugarCapaccuino
LemonCapaccuino

etc.

Genom att använda samma Decorator för båda dryckerna har antalet underklasser minskats. Det är möjligt på grund av composition snarare än inheritance används i detta mönster.

Jämförelse med andra designmönster (Från artikel om framställning )

  1. Adapter ger ett annat gränssnitt till ämnet. Proxy ger samma gränssnitt. Decorator ger ett förbättrat gränssnitt.

  2. Adapter ändrar ett objekts gränssnitt, Decorator förbättrar ett objekts ansvar.

  3. Composite och Decorator har liknande strukturdiagram som återspeglar det faktum att båda förlitar sig på rekursiv komposition för att organisera ett öppet antal objekt

  4. Dekoratören är utformad så att du kan lägga till ansvar för föremål utan underklassificering. Composite fokuserar inte på utsmyckning utan på representation

  5. Decorator och Proxy har olika syften men liknande strukturer

  6. Dekoratör låter dig ändra huden på ett objekt. Strategi låter dig ändra tarmarna.

Fall av nyckelanvändning:

  1. Lägg till ytterligare funktioner / ansvar dynamiskt
  2. Ta bort funktionaliteter / ansvar dynamiskt
  3. Undvik för mycket underklassificering för att lägga till ytterligare ansvar.

Caching Decorator

Detta exempel visar hur du lägger till DbProductRepository till DbProductRepository hjälp av Decorator-mönster. Denna strategi följer SOLID-principer eftersom det gör att du kan lägga till cache utan att bryta mot principen om ett enda ansvar eller öppen / stängd princip .

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

Användande:

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

Resultatet av att anropa GetProduct blir: hämta produkt från cache (dekoratöransvar), om produkten inte var i cachen, fortsätt med anrop till DbProductRepository och hämta produkt från DB. Efter den här produkten kan läggas till cachen så att efterföljande samtal inte träffar DB.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow