수색…


소개

데코레이터 패턴을 통해 사용자는 구조를 변경하지 않고 기존 기능에 새로운 기능을 추가 할 수 있습니다. 이 유형의 디자인 패턴은 기존 패턴에 대한 래퍼 역할을하는 구조 패턴 아래에 있습니다.

이 패턴은 원래 클래스를 래핑하고 클래스 메소드 서명을 그대로 유지하는 추가 기능을 제공하는 데코레이터 클래스를 작성합니다.

매개 변수

매개 변수 기술
마실 것 차나 커피 일 수있다.

VendingMachineDecorator

Wikiepdia에 따른 데코레이터의 정의 :

데코레이터 패턴은 디자인 타임에 일부 기초 작업이 수행되는 경우 특정 클래스의 다른 인스턴스와 독립적으로 특정 객체의 기능을 정적으로 또는 경우에 따라 런타임에 확장 (장식)하는 데 사용할 수 있습니다.

데코레이터는 객체에 추가 책임을 동적으로 부여합니다. 데코레이터는 기능 확장을 위해 하위 클래스 화에 대한 유연한 대안을 제공합니다.

장식 패턴에는 네 가지 구성 요소가 포함되어 있습니다.

여기에 이미지 설명을 입력하십시오.

  1. 구성 요소 인터페이스 : 특정 작업을 실행하기위한 인터페이스를 정의합니다.
  2. ConcreteComponent : 구성 요소 인터페이스에 정의 된 작업을 구현합니다.
  3. Decorator (Abstract) : 컴포넌트 인터페이스를 확장하는 추상 클래스입니다. 구성 요소 인터페이스가 포함되어 있습니다. 이 클래스가 없으면 다양한 조합에 대해 ConcreteDecorator의 여러 하위 클래스가 필요합니다. 구성 요소의 구성은 불필요한 하위 클래스를 줄입니다.
  4. ConcreteDecorator : Abstract Decorator의 구현을 유지합니다.

다시 예제 코드를 살펴보면,

  1. 음료 는 구성 요소입니다. 추상적 인 메소드를 정의합니다 : decorateBeverage
  2. 커피음료의 구체적인 구현입니다.
  3. BeverageDecorator음료 를 포함하는 추상 클래스입니다.
  4. SugarDecorator와 LemonDecorator는 BeverageDecorator의 구체적인 Decorator입니다.

수정 : 설탕, 레몬 등 (풍미가 장식품과 같은 하나 이상의 맛을 추가하여 음료의 가격을 컴퓨팅의 실제 시나리오를 반영하는 예를 변경)

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

산출:

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

이 예제는 음료에 많은 맛을 추가 한 후 Vending Machine의 음료 비용을 계산합니다.

위의 예에서 :

Tea = 10, Lemon = 3, Sugar = 5. Sugar + Lemon + Tea를 만들면 18이 소요됩니다.

커피 비용 = 15, 레몬 = 3, 설탕 = 5. 설탕 + 레몬 + 커피를 만드는 경우 23

두 음료 (Tea and Coffee)에 동일한 Decorator를 사용함으로써 하위 클래스의 수를 줄였습니다. Decorator 패턴이 없으면 다양한 조합에 대해 서로 다른 하위 클래스가 있어야합니다.

조합은 다음과 같습니다.

SugarLemonTea
SugarTea
LemonTea

SugarLemonCapaccuino
SugarCapaccuino
LemonCapaccuino

기타

두 음료 모두에 동일한 Decorator 를 사용함으로써 하위 클래스 수가 감소되었습니다. 이 패턴에서 사용 된 inheritance 개념보다는 composition 으로 인해 가능합니다.

다른 디자인 패턴과 비교 ( 소스 제작 기사에서)

  1. 어댑터 는 주제와 다른 인터페이스를 제공합니다. 프록시 는 동일한 인터페이스를 제공합니다. Decorator 는 향상된 인터페이스를 제공합니다.

  2. 어댑터 가 객체의 인터페이스를 변경하면 Decorator 는 객체의 책임을 강화합니다.

  3. CompositeDecorator 는 유사한 구조 다이어그램을 가지고 있습니다. 둘 다 재귀 컴포지션에 의존하여 개방 된 수의 객체를 구성한다는 사실을 반영합니다

  4. Decorator 는 서브 클래 싱없이 객체에 책임을 추가 할 수 있도록 설계되었습니다. 컴포지트의 초점은 장식에있는 것이 아니라 표현에 있습니다.

  5. 데코레이터프록시 는 다른 용도이지만 구조는 유사합니다.

  6. Decorator를 사용하면 객체의 스킨을 변경할 수 있습니다. 전략을 통해 용기를 바꿀 수 있습니다.

주요 사용 사례 :

  1. 추가 기능 / 책임을 동적으로 추가하십시오.
  2. 기능 / 책임을 동적으로 제거합니다.
  3. 책임을 추가하기에는 너무 많은 하위 분류를 피하십시오.

캐싱 데코레이터

이 예제는 Decorator 패턴을 사용하여 DbProductRepository 에 캐싱 기능을 추가하는 방법을 보여줍니다. 이 접근 방식은 단일 책임 원칙 또는 개방 / 폐쇄 원칙 을 위반하지 않고 캐싱을 추가 할 수 있으므로 SOLID 원칙을 준수합니다.

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

용법:

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

GetProduct 를 호출 한 결과는 다음과 같습니다. 캐시에서 제품을 검색 (데코레이터 책임). 제품이 캐시에 DbProductRepository 를 호출하고 DB에서 제품을 검색합니다. 이 제품을 캐시에 추가하면 후속 호출이 DB에 도달하지 않습니다.



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow