DIARY

[헤드퍼스트 디자인패턴] 3. 데코레이터 패턴

혀내 2022. 7. 28. 17:44
반응형

데코레이터 패턴 (Decorator Pattern)

 객체에 새로운 행동을 동적으로 더할수 있는 패턴으로 상속보다 훨씬 유연하게 기능을 확장할 수 있다는 장점이 있습니다.

 

 

https://namu.wiki/w/%EB%A7%88%ED%8A%B8%EB%A3%8C%EC%8B%9C%EC%B9%B4

 

 

마트료시카 인형 안에는 인형이.. 인형 안에 또 인형..이 있듯이,

데코레이터 패턴을 사용하면 클래스 안에 클래스.. 클래스 안에 또 클래스가 감싸져 있습니다.

 

 

구조

그래서 패턴을 구성하는 각 요소는 그 자체로 직접 쓰일 수도 있고, 또는 데코레이터에 감싸져 쓰일 수도 있습니다.

 

특징

  • 한 객체를 여러 개의 데코레이터로 감쌀 수 있습니다. (마트료시카 인형처럼요!)
  • 런타임에서도 객체에 필요한 데코레이터를 동적으로 마음껏 적용할 수 있습니다.
  • ConcreteComponent에 새로운 행동을 동적으로 추가할 수 있습니다.
  • 각 데코레이터 안에는 자신이 장식할 Component 객체를 담는 인스턴스 변수가 존재합니다.
  • 데코레이터의 슈퍼클래스는 자신이 장식하고 있는 Component 객체의 슈퍼클래스와 같습니다.
  • 데코레이터는 자신이 장식하고 있는 객체에게 어떤 행동을 위임할 수도, 또는 새로운 메소드를 추가할 수도 있습니다.

 

 

ex) java.io 라이브러리

java.io 라이브러리에서는 데코레이터 패턴을 사용해 Stream 객체에 새로운 행동을 추가할 수 있습니다. ZipInputStream이 BufferedInputStream을, 그리고 BufferedInputStream이 FileInputStream을 감싸고 있는 것 처럼 말이죠. java로 알고리즘 문제를 풀어본 사람들은 BufferedReader, InputStreamReader를 통해 마트료시카 클래스를 경험했을 겁니다.

 

데코레이터 패턴은 OCP 원칙에 충실하면서 유연하게 디자인할 수 있는 패턴이지만 잡다한 클래스가 많아진다는 단점도 함께 존재합니다.

 

 


 

OCP 원칙

 OCP란 Open-Closed Principle의 약자로 클래스는 확장에 열려있어야 하지만 동시에 변경에는 닫혀 있어야 한다는 디자인 원칙입니다. 원칙 자체가 모순적으로 들릴 수 있지만 객체지향 기법을 사용하면 OCP를 준수하는 코드를 작성할 수 있습니다.

 

 

 


책에서 제시하는 문제 상황

 스타벅스의 다양한 음료를 모두 포괄하는 주문 시스템을 개선해보자. 현재 주문 시스템은 판매되는 모든 음료들이 Beverage 추상 클래스를 상속해 구현된다. 우유, 두유, 모카, 심지어 휘핑크림을 얹을 때마다 가격이 달라지기 때문에 Beverage의 서브 클래스가 몇 백개까지 달한다.

 

 

스타벅스의 기존 Beverage 추상 클래스

데코레이터 패턴의 Component 상위 클래스에 해당합니다.

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

 

 우유, 두유, 모카 등 첨가물마다 인스턴스 변수를 만든다고 가정한다면 새로운  첨가물이 생길 때마다 변수를 생성해야 하기 때문에 클래스를 계속 수정해야 한다. 그렇다면 데코레이터 패턴을 사용해 확장에는 열려있으나 변경에는 닫혀 있는 코드를 직접 작성해보자!

 


 

해결 방법

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

데코레이터 패턴의 Decorator 상위 클래스에 해당합니다.

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

 

실제 음료 클래스 구현

데코레이터 패턴의 ConcreteComponent 구현 클래스에 해당합니다.

public class Espresso extends Beverage {
    public Espresso() {
        description = "에스프레소";
    }
   	
    public double cost() {
    	return 1.99;
    }
}

 

첨가물 클래스 구현

데코레이터 패턴의 ConcreteDecorator 구현 클래스에 해당합니다.

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

 

코드 사용 방법

Beverage beverage = new Espresso();    // 에스프레소 한 잔
beverage = new Mocha(beverage);        // 모카 추가
beverage = new Whip(beverage);         // 휘핑 추가

System.out.println(beverage.getDescription()); // 에스프레소, 모카, 휘핑

 

 

 

참조:

 

헤드 퍼스트 디자인 패턴(개정판)

유지관리가 편리한 객체지향 소프트웨어 만들기! “『헤드 퍼스트 디자인 패턴(개정판)』 한 권이면 충분합니다!”

m.hanbit.co.kr

 

반응형