본문 바로가기
컴퓨터과학/디자인패턴

[디자인패턴] 데코레이터 패턴 (Decorator Pattern)

by 윤호 2021. 10. 14.

목적

객체에 추가적인 책임을 동적으로 부여함

상속을 사용하지 않고 새로운 기능을 추가

데코레이터는 상속을 사용하지 않아도 유연하고 융통성 있는 기능 확장을 가능하게 함

서브클래스를 만들지 않고 기능을 유연하게 확장하는 방법 제공

요소

문제: 조금씩 다른 다양한 종류 -> 늘어날수록 확장 어려움

해결: 상속을 남용하지 않고 연관으로 필요한 기능 추가

결과: 확장성

정의

  • 여기서 상속은 기능을 물려받기 위해서가 아니라 형식을 맞추기 위해서임
  • Component: 각 구성요소는 직접 쓰일 수도 있고 데코레이터로 감싸져서 쓰일 수도 있음, 추상클래스 또는 인터페이스로 구현
  • ConcreteComponent: 새로운 행동을 동적으로 추가
  • Decorator: 자신이 장식할 구성요소(Component)와 같은 인터페이스 또는 추상 메소드 역할, Component의 기능 및 상태를 확장할 수 있음
  • ConcreteDecorator: 해당 객체가 장식하고 있는 것(Decorator가 감싸고 있는 Component 객체)을 위한 인스턴스 변수가 있음

 

데코레이터 패턴을 이용한 카페 음료

음료에 여러가지 옵션(휘핑, 샷 등등)을 추가하는 경우를 생각해보자

음료는 Component이고, 옵션들은 Decorator다.

Decorator들이 Component와 같이 Beverage를 상속받는 이유는 형식을 맞추기 위해서이다.

-> Component를 Decorator로 감싸도 근본이 훼손되지 않기 위해

Component인 DarkRoast을 Decorator들로 감쌈.

이렇게 감싸진 객체는 Beverage라는 근본은 잃지 않는다.

이 객체의 가격을 구할 경우 재귀호출을 통해 Component부터 Decorator까지의 가격들을 모두 더한 값을 반환하게 된다.

// Main 함수

public class Coffee {
    public static void main(String[] args[]) {

        Beverage b = new DarkRoast();
        b = new Mocha(b); // 모카 추가
        b = new Mocha(b); // 모카 한 개 더 추가
        b = new Whip(b); // 휘핑 추가

        System.out.println(b.getDescription()
          +" $" + b.cost());
    }
}

 

// Component

public abstract class Beverage {
  String description = "제목 없음";
  
  public String getDescription() {
    return description;
  }

  public abstract double cost(); 

}


// Decorator

public abstract class CondimentDecorator extends Beverage {
  public abstract String getDescription(); 
}

 

 // Concrete Component
 
 public class Espresso extends Beverage {
  public Espresso() {
    description = "다크로스트"; 
  }

  public double cost() {
    return 1.99;
  }
}

 

// Concrete Decorator (CondimentDecorator는 Beverage를 상속받음)

public class Mocha extends CondimentDecorator {
  Beverage beverage;
  
  public Mocha(Beverage beverage) {
    this.beverage = beverage;
  }

  public String getDescription() {
    return beverage.getDescription() + ", 모카"; 
  }

  public double cost() {
    return beverage.cost() + .20;
  }
}

댓글