Suche…


Einführung

Mit dem Dekorationsmuster kann ein Benutzer einem vorhandenen Objekt neue Funktionen hinzufügen, ohne seine Struktur zu ändern. Diese Art von Designmuster fällt unter Strukturmuster, da dieses Muster als Wrapper für eine vorhandene Klasse fungiert.

Dieses Muster erstellt eine Dekoratorklasse, die die ursprüngliche Klasse umschließt und zusätzliche Funktionalität bereitstellt, wobei die Signatur der Klassenmethoden erhalten bleibt.

Parameter

Parameter Beschreibung
Getränk Es kann Tee oder Kaffee sein

VendingMachineDecorator

Definition von Decorator nach Wikiepdia:

Das Decorator-Muster kann verwendet werden, um die Funktionalität eines bestimmten Objekts statisch oder in einigen Fällen zur Laufzeit unabhängig von anderen Instanzen derselben Klasse zu erweitern (zu dekorieren), sofern zur Entwurfszeit einige Vorarbeiten ausgeführt werden.

Der Dekorateur fügt einem Objekt dynamisch zusätzliche Verantwortlichkeiten hinzu. Dekorateure bieten eine flexible Alternative zu Unterklassen zur Erweiterung der Funktionalität.

Dekorateur-Muster enthält vier Komponenten.

Geben Sie hier die Bildbeschreibung ein

  1. Komponentenschnittstelle: Definiert eine Schnittstelle, um bestimmte Vorgänge auszuführen
  2. ConcreteComponent: Implementiert die in der Komponentenschnittstelle definierten Vorgänge
  3. Decorator (Abstract): Dies ist eine abstrakte Klasse, die die Komponentenschnittstelle erweitert. Es enthält eine Komponentenschnittstelle. In Abwesenheit dieser Klasse benötigen Sie viele Unterklassen von ConcreteDecorators für verschiedene Kombinationen. Die Zusammensetzung der Komponente reduziert unnötige Unterklassen.
  4. ConcreteDecorator: Es beinhaltet die Implementierung von Abstract Decorator.

Zurück zum Beispielcode,

  1. Getränk ist Bestandteil. Es definiert eine abstrakte Methode: decorateBeverage
  2. Tee und Kaffee sind konkrete Umsetzungen von Beverage .
  3. BeverageDecorator ist eine abstrakte Klasse, die Beverage enthält
  4. SugarDecorator und LemonDecorator sind konkrete Dekorateure für BeverageDecorator.

BEARBEITEN: Das Beispiel wurde geändert, um das reale Szenario der Berechnung des Getränkepreises durch Hinzufügen einer oder mehrerer Geschmacksrichtungen wie Zucker, Zitrone usw. zu berücksichtigen (Geschmacksrichtungen sind Dekorateure).

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

Ausgabe:

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 diesem Beispiel werden die Getränkekosten im Automaten berechnet, nachdem dem Getränk viele Geschmacksrichtungen hinzugefügt wurden.

In obigem Beispiel:

Teekosten = 10, Zitrone = 3 und Zucker = 5. Wenn Sie Zucker + Zitrone + Tee herstellen, kostet dies 18.

Kosten für Kaffee = 15, Zitrone = 3 und Zucker = 5. Wenn Sie Zucker + Zitrone + Kaffee herstellen, kostet dies 23

Durch die Verwendung des gleichen Decorators für beide Getränke (Tee und Kaffee) wurde die Anzahl der Unterklassen reduziert. In Abwesenheit des Decorator-Musters sollten Sie unterschiedliche Unterklassen für unterschiedliche Kombinationen haben.

Die Kombinationen werden so sein:

SugarLemonTea
SugarTea
LemonTea

SugarLemonCapaccuino
SugarCapaccuino
LemonCapaccuino

usw.

Durch die Verwendung des gleichen Decorator für beide Getränke wurde die Anzahl der Unterklassen reduziert. Dies ist aufgrund der composition und nicht des in diesem Muster verwendeten inheritance möglich.

Vergleich mit anderen Designmustern (aus dem Quelltextartikel )

  1. Der Adapter bietet eine andere Schnittstelle zum Betreff. Proxy bietet dieselbe Schnittstelle. Decorator bietet eine erweiterte Benutzeroberfläche.

  2. Der Adapter ändert die Oberfläche eines Objekts, Decorator verbessert die Verantwortlichkeiten eines Objekts.

  3. Composite und Decorator weisen ähnliche Strukturdiagramme auf. Dies spiegelt die Tatsache wider , dass beide auf rekursive Komposition angewiesen sind, um eine unbegrenzte Anzahl von Objekten zu organisieren

  4. Decorator ermöglicht das Hinzufügen von Verantwortlichkeiten zu Objekten ohne Unterklassen. Composite konzentriert sich nicht auf Verschönerung, sondern auf Repräsentation

  5. Decorator und Proxy haben unterschiedliche Zwecke, aber ähnliche Strukturen

  6. Mit Decorator können Sie die Skin eines Objekts ändern. Strategie lässt Sie den Mut ändern.

Wichtige Anwendungsfälle:

  1. Fügen Sie dynamisch weitere Funktionen / Verantwortlichkeiten hinzu
  2. Entfernen Sie Funktionalitäten / Verantwortlichkeiten dynamisch
  3. Vermeiden Sie zu viel Unterklassifizierung, um zusätzliche Verantwortlichkeiten hinzuzufügen.

Zwischenspeicher-Dekorateur

In diesem Beispiel wird DbProductRepository , wie DbProductRepository mithilfe des Decorator-Musters DbProductRepository wird. Dieser Ansatz folgt den SOLID-Grundsätzen, da Sie das Zwischenspeichern hinzufügen können, ohne dass das Prinzip der Einzelverantwortung oder das Open / Closed-Prinzip verletzt wird.

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

Verwendungszweck:

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

Das Ergebnis des Aufrufs von GetProduct ist: Produkt aus dem Cache abrufen (Verantwortlichkeit des Dekorators), wenn sich das Produkt nicht im Cache befindet, fahren Sie mit dem Aufruf von DbProductRepository und DbProductRepository Produkt aus der DbProductRepository ab. Nachdem dieses Produkt zum Cache hinzugefügt werden kann, trifft nachfolgende Aufrufe nicht die DB.



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow