HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
📖
공부한 책
/헤드 퍼스트 디자인 패턴/
3. 데코레이터 패턴

3. 데코레이터 패턴

💡
객체지향 원칙 - 클래스는 확장에는 열려 있어야 하고 변경에는 닫혀 있어야 한다(OCP)

상황

notion image
  • 사업을 시작할 무렵에 만들어진 주문 시스템 클래스는 위와 같이 구성되어 있음
  • 고객은 커피를 주문할 때 우유나 두유, 모카(초콜릿)을 추가하고 그 위에 휘핑크림을 얹기도 함
  • 각각 추가할 때마다 커피 가격이 올라가야 하기에 주문 시스템을 구현할 때 이런 점을 모두 고려해야 함 → 처음에는 모든 조합별로 클래스를 다 만듦 → 클래스 폭발

데코레이터 패턴

  • 다른 방식으로 위의 상황을 해결하는 방법 → 특정 음료에서 시작해서 첨가물로 그 음료를 장식(Decorate) 해보기
      1. DarkRoast 객체를 가져온다
      1. Mocha 객체로 장식한다.
      1. Whip 객체로 장식한다
      1. cost() 메서드를 호출한다.
notion image
  • CondimentDecorator라는 추상클래스를 중간에 하나 더 도입하여, Milk, Mocha, Soy, Whip이 Beverage의 타입을 갖게 만드는 동시에, getDescription() 메서드를 추상으로 만들게 됨
  • Beverage와 분리되는 타입으로 묶여 있기에 구분이 용이하다는 생각도 듦
public abstract class Beverage { protected String description; public String getDescription() { return this.description; } public abstract double cost() ; } public abstract class CondimentDecorator extends Beverage { private Beverage beverage; public abstract String getDescription(); public abstract double cost(); } public class Espresso extends Beverage { public Espresso(String description) { this.description = description; } public double cost() { return 1.99; } } 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; } }

단점

  • 위의 코드를 그대로 사용하면 구상 구성 요소(HouseBlend, DarkRost ,,)로 특별 할인 같은 작업을 처리할 때 문제가 생길 수 있음
    • 구상 구성 요소로 어떤 작업을 처리하는 코드에 데코레이터 패턴을 적용하면 코드가 제대로 동작하지 않음. Wrapping이 되기 때문에 그 내부에 뭐가 있는지 알수가 없기에
    • 추상 구성 요소(Beverage)로 돌아가는 코드에 데코레이터 패턴을 적용해야만 제대로 된 결과를 얻을 수 있음
  • 데코레이터 패턴을 쓰면 관리해야 할 객체가 늘어나니, 코딩할 때 실수할 가능성이 높음(모카, 두유, 휘핑크림을 추가해야 하는데 휘핑 크림을 빼먹는다던가) ⇒ 팩토리나 빌더 패턴으로 데코레이터 사용하면 괜찮음
  • 자바 I/O (java.io) 를 보면 그 단점을 알 수 있는데, 데코레이터 패턴을 사용해서 디자인을 하다 보면 잡다한 클래스가 너무 많아져서 해당 API를 사용하는 개발자는 상당히 괴로움
 
 
그러나 데코레이터가 어떤 식으로 작동하는지 이해하면 다른 사람이 데코레이터 패턴을 활용해서 만든 API를 끌어 쓰더라도 클래스를 데코레이터로 감싸서 원하는 행동을 구현할 수 있다..