IT story

데코레이터 패턴을 언제 사용합니까?

hot-time 2020. 12. 28. 22:00
반응형

데코레이터 패턴을 언제 사용합니까?


저는 제 디자인 패턴을 살펴보고 있는데 제가 코딩에서 아직 심각하게 사용하지 않은 패턴은 데코레이터 패턴입니다.

나는 패턴을 이해하지만, 내가 알고 싶은 것은 데코레이터 패턴이 최고 / 최적 / 우아한 솔루션이라는 현실 세계의 좋은 구체적인 예입니다. 데코레이터 패턴이 정말 편리한 특정 상황.

감사.


데코레이터 패턴은 스트림과 함께 많이 사용됩니다. 스트림으로 스트림을 래핑하여 기능을 추가 할 수 있습니다. .Net 프레임 워크에서 이것을 보았습니다. 내가 아는 한 이것이 다른 곳에서 발생합니다. 내가 가장 좋아하는 것은 추가 압축을 위해 FileStream 주위에 GZipStream을 사용하는 것입니다.


데코레이터 패턴은 객체 클래스가 아닌 특정 객체에 추가 기능을 추가하는 데 사용됩니다. 객체를 서브 클래 싱하여 전체 객체 클래스에 기능을 추가하는 것은 쉽지만 이러한 방식으로 단일 객체를 확장하는 것은 불가능합니다. 데코레이터 패턴을 사용하면 단일 개체에 기능을 추가하고 다른 개체는 수정하지 않고 그대로 둘 수 있습니다.

Java에서 데코레이터 패턴의 전형적인 예는 Java I / O Streams 구현입니다.

FileReader       frdr = new FileReader(filename);
LineNumberReader lrdr = new LineNumberReader(frdr);

앞의 코드 lrdr는 파일에서 읽고 행 번호를 추적 하는 판독기를 만듭니다 . 1 행은 파일 판독기 ( frdr)를 만들고 2 행은 행 번호 추적을 추가합니다.

실제로 Java I / O 클래스에 대한 Java 소스 코드를 살펴 보는 것이 좋습니다.


최근에 다음 CommandProcessor 인터페이스를 사용하는 웹 서비스에서 데코레이터 패턴을 사용했습니다.

public Command receive(Request request);
public Response execute(Command command);
public void respond(Response response);

기본적으로 CommandProcessor는 Request를 수신하고 적절한 Command를 생성하고 Command를 실행하고 적절한 Response를 생성하고 Response를 보냅니다. 타이밍을 추가하고 기록하고 싶을 때 기존 CommandProcessor를 구성 요소로 사용하는 TimerDecorator를 만들었습니다. TimerDecorator는 CommandProcessor 인터페이스를 구현하지만 타이밍을 추가 한 다음 실제 CommandProcessor 인 대상을 호출합니다. 이 같은:

public class TimerDecorator implements CommandProcessor {
   private CommandProcessor target;
   private Timer timer;

   public TimerDecorator(CommandProcessor processor) {
      this.target = processor;
      this.timer = new Timer();
   }

   public Command receive(Request request) {
      this.timer.start();
      return this.target.receive(request);
   }

   public Response execute(Command command) {
      return this.target.execute(command);
   }

   public void respond(Response response) {
      this.target.response(response);
      this.timer.stop();
      // log timer
   }

}

따라서 실제 CommandProcessor는 TimerDecorator 내부에 래핑되어 있고 TimerDecorator를 대상 CommandProcessor처럼 처리 할 수 ​​있지만 이제 타이밍 로직이 추가되었습니다.


데코레이터 패턴은 개체의 기존 기능에 영향을주지 않고 런타임에 개체의 기능을 동적으로 변경합니다.

주요 사용 사례 :

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

단점 :

  1. 개방 폐쇄 원칙의 남용 (확장을 위해 개방, 수정을 위해 폐쇄). 코드가 변경 될 가능성이 가장 적은 곳에서는이 기능을 사용하지 마십시오.
  2. 소규모 클래스가 너무 많아 유지 관리 오버 헤드 가 추가 됩니다 .

실제 사례 : 여러 가지 맛을 포함 할 수있는 음료 가격을 계산합니다.

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

This example computes cost of beverage in Vending Machine after adding many flavours to the beverage.

In above example:

Cost of Tea = 10, Lemon = 3 and Sugar = 5. If you make Sugar + Lemon + Tea, it costs 18.

Cost of Coffee =15, Lemon = 3 and Sugar = 5. If you make Sugar + Lemon + Coffee, it costs 23

By using same Decorator for both beverages ( Tea and Coffee ), the number of sub-classes have been reduced. In absence of Decorator pattern, you should have different sub classes for different combinations.

The combinations will be like this:

SugarLemonTea
SugarTea
LemonTea

SugarLemonCapaccuino
SugarCapaccuino
LemonCapaccuino

etc.

By using same Decorator for both beverages, the number of sub-classes have been reduced. It's possible due to composition rather than inheritance concept used in this pattern.

Related SE question:

Decorator Pattern for IO

Useful links:

design-patterns-decorator by dzone

decorator by sourcemaking

oodesign article


The decorator is simple yet extremely powerful. It is key in achieving separation of concerns and is an essential tool for the Open Closed Principle. Take a common example of placing an order for a product:

IOrderGateway
{
    void PlaceOrder(Order order);
{

Main implementation: AmazonAffiliateOrderGateway

Possible decorators could be:

  • IncrementPerformanceCounterOrderGateway
  • QueueOrderForLaterOnTimeoutOrderGateway
  • EmailOnExceptionOrderGateway
  • InterceptTestOrderAndLogOrderGateway

A more detailed example from here illustrates a decorator that saves contacts for orders that were created using a gift card while completing the order:

class OrderWithGiftCardGateway extends OrderGatewayDecorator
{
    ...

    public function createOrder(CreateOrderRequest $order)
    {
        if ($this->containsGiftCard($order))
        {
            $this->addContactToFolder($order);
        }

        return parent::createOrder($order);
    }
}

Zend Framework uses the decorator for form elements

Some more info: http://framework.zend.com/manual/en/zend.form.decorators.html


  1. to add responsibilities to individual objects dynamically and transparently.
  2. for responsibilities that can be withdrawn.
  3. when extension by subclassing is impractical. Sometimes a large number of independent extensions are possible and would produce an explosion of subclasses to support every combination.

Decorator pattern is used by C# language itself. It is used to to decorate the Stream I/O class of C#. The decorated versions are BufferedStream, FileStrem, MemoryStrem, NetworkStream and CryptoStream classed.

These subclasses inherit from the Stream class and also contain an instance of the Stream class.

Read more here

ReferenceURL : https://stackoverflow.com/questions/1549743/when-to-use-the-decorator-pattern

반응형