본문 바로가기

IT Book Summary/디자인패턴

데코레이터 패턴

객체 꾸미기

상속을 남용하는 전형적인 예를 살펴보고 객체 작성이라는 형식으로 실행중에 클래스를 꾸미는 (데코레이션) 방법

데코레이터 패턴을 알면 원래 클래스의 코드는 바꾸지 않고도 다른사람이 만든 객체에 새로운 임무를 부여할 수 있다.

 

스타버즈 커피샵. 다양한 음료들을 모두 포괄하는 주문 시스템.

 

Beverage 클래스는 추상클래스이며 각 서브클래스에서 description 인스턴스 변수와 cost() 를 구현한다.

그러나 다양한 옵션이 추가될경우 가격이 변동되기 때문에 너무 많은 클래스를 만들어야 한다.

 

그래서 음료에 추가되는 여러 옵션 인스턴트 변수를 추가하자.

 

추가 요소에 해당하는 Boolean 변수 추가.

cost() 를 구현해 기본음료값에 추가비용을 합친 총 가격을 리턴한다.

 

하지만 추가요소 가격이 바뀔때마다 기존코드를 수정해야 한다.

첨가물의 종류가 많아지면 메소드를 추가해야하며, 수퍼클래스의 cost()메소드도 고쳐야 한다.

 

-> 서브클래스를 만드는 방식으로 행동을 상속받으면 그 행동이 컴파일시 결정됨.

    하지만 구성을 통해 객체의 행동을 확장하면 실행중에 동적으로 행동을 설정할 수 있다.

    객체를 동적으로 구성하면, 기존코드를 두고 새로운 기능을 추가할 수 있다.

 


OCP (Open-Closed Principle)

클래스는 확장에 대해 열려있으나, 코드 변경에 대해 닫혀있어야 한다.


데코레이터 패턴

 

특정 음료에서 시작해 첨가물로 그 음료를 장식할 것이다.

1. DarkRoast 객체에서 시작

 

2. 모카를 주문했으니 Mocha 객체를 만들고 그 객체로 DarkRoast를 감싼다.

 

3. 휘핑크림도 같이 주문해서 Whip 데코레이터를 만들고 그 객체로 Mocha를 감싼다.

 

4. 가격계산.

  바깥쪽에 있는 Whip의 cost() 호출하고 구해진 가격에 휘핑크림의 가격을 더한다음 결과를 리턴.

 

  • 한 객체를 여러개의 데코레이터로 감쌀 수 있다.
  • 데코레이터는 감싼 객체와 같은 수퍼클래스를 가지고있지기 때문에 바뀌어도 됨.
  • 데코레이터는 자신이 장식하고있는 객체에게 어떤 행동을 위임하는 것 이외에 원하는 추가 작업 수행 가능.
  • 싱행중에 필요한 데코레이터를 마음대로 적용 가능. 

데코레이터 패턴의 정의

- 객체에 추가적인 요건을 동적으로 첨가. 

- 데코레이터는 세브클래스를 만드는 것을 통해서 기능을 유연하게 확장할 수 있는 방법을 제공

 

ConcreteComponent - 행동을 동적으로 추가

Decorator - component 객체가 들어있다. 구성요소에 대한 레퍼런스가 들어있는 인스턴스 변수를 가짐.

ConcreteDecorator - 그 객체가 장식하고 있는것(Component 객체)을 위한 인스턴스 변수가 있음.

 

스타버즈 음료에 적용해보자.

커피 종류마다 구성요소를 나타내는 구상클래스를 가진다.

Beverage 추상클래스의 서브클래스를 만드는게 올바른 형식을 맞추기 위함. 상속을 이용해 형식을 맞춤.

객체 구성(인스턴스 변수로 다른 객체를 저장)을 이용해 다양하게 추가요소를 구성할 수 있는 유연성을 가짐.

 


Beverage 클래스

 

public abstract class Beverage {
 
  String description ="Unknown Beverage";
 
  public String getDescription() {
    return description;
  }
 
  public abstract double cost();
}

 

첨가물 condiment를 나타내는 추상클래스 (데코레이터 클래스) 구현

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

 

음료 코드 구현

- 생성자에서 description 변수값 설정.

- 음료 가격 설정.

- 다른 종류의 커피도 동일하게 설정.

public class Espresso extends Beverage {
 
  public Espresso() {
    description="Espresso";
  }
 
  @Override
  public double cost() {
    return 1.99;
  }
 
}

 

첨가물용 코드

- 생성자에 감싸고자하는 음료객체를 전달하는 방식

- description, cost에 추가한 옵션 정보 추가 설정.

public class Mocha extends CondimentDecorator {
 
  Beverage beverage;
  public Mocha(Beverage beverage) {
    this.beverage=beverage;
  }
 
  @Override
  public String getDescription() {
    return beverage.getDescription()+", Mocha";
  }
 
  @Override
  public double cost() {
    return beverage.cost()+.20;
  }
 
}

 

커피 주문 코드

public class StarbuzzCoffee {
 
  public static void main(String[] args) {
    // 아무것도 넣지않은 에스프레소 
    Beverage beverage = new Espresso();
    System.out.println(beverage.getDescription() + " = $" + beverage.cost());
 
    // 모카하나 휩 두개 추가한 하우스 블랜드 
    Beverage beverage2 = new HouseBlend();
    beverage2 = new Mocha(beverage2);
    beverage2 = new Whip(beverage2);
    beverage2 = new Whip(beverage2);
    System.out.println(beverage2.getDescription() + " = $" + beverage2.cost());
  }
}