본문 바로가기
도서/프로그래밍

[헤드퍼스트 디자인패턴](3) 데코레이터 패턴(Dacorator Pattern)

by 신발사야지 2023. 1. 25.

상속맨 디자인에 눈을 뜨다

 

상속 남용 → 실행 중에 클래스를 꾸미는 방법을 배웁니다.

 

데코레이터 패턴을 배우면 기존 클래스 코드를 바꾸지 않고도 객체에 새로운 임무를 추가할 수 있습니다.

 

스타버즈라는 빠르게 성장한 커피 업체가 있습니다.

 

처음에는 Beverage 라는 추상 클래스를 만들고 모든 음료를 이 클래스의 서브 클래스로 구현하였습니다. 하지만 음료와 옵션의 종류가 늘자 DarkRoastWithWhip, DecafWithSteamedMilk 같은 수많은 클래스가 폭발적으로 생겨났습니다.

 

 

이 스타버즈는 어떠한 디자인 원칙을 어기지 않은걸까요? (힌트 2가지) (정답 확실 X)

  1. 애플리케이션에서 달라지는 부분을 찾아내고 달라지지 않는 부분과 분리한다.
  2. 구현보다는 인터페이스에 맞춰서 프로그래밍한다.
  3. 상속보다는 구성을 활용한다 → 상속에 너무 의존하고 있다.
  4. 상호작용하는 객체 사이에는 가능하면 느슨한 결합을 사용해야 한다. → 추상클래스와 서브클래스가 너무 강력한 결합을 사용하고 있다.

 

서브클래스를 만드는 방식으로 행동을 상속받으면 그 행동은 컴파일할 떄 완전히 결정됩니다. 게다가 모든 서브클래스에 똑같은 행동을 상속받아야 합니다. 하지만 구성으로 객체의 행동을 확장하면 실행 중에 동적으로 행동을 설정할 수 있습니다. 이 기술을 활용하면 객체에 여러 임무를 새로 추가할 수 있습니다. 심지어 슈퍼클래스를 디자인했던 사람이 전혀 생각지도 못했던 내용을 추가할 수도 있습니다. 클래스 코드를 전혀건드리지 않고도 말입니다.

 


OCP 살펴보기

 

OCP(Open-CLosed Principle)는 정말 중요한 디자인 원칙 중 하나입니다.

 

OCP: 클래스는 확장에는 열려 있어야 하지만 변경에는 닫혀있어야 한다

 

클래스를 변경하는 것이 아니라 확장하는 방식으로 수정하는 것이 오류 방지 및 유연성에 좋다.

 

 

코드에서 확장해야 할 부분을 선택할 때는 세심한 주의를 기울여야 합니다. 무조건 OCP를 적용한다면 괜히 쓸데없는 일을 하며 시간을 낭비할 수 있으며, 필요 이상으로 복잡하고 이해하기 힘든 코드를 만들게 되는 부작용이 발생할 수 있습니다. 

모든 부분에서 OCP를 준수해야 하는 것이 아니라 우리가 디자인한 것 중에서 가장 바뀔 가능성이 높은 부분을 중점적으로 살펴보고 OCP를 적용하는 방법이 가장 좋습니다.

 

 

데코레이터 패턴 살펴보기

 

특정 음료에 첨가물로 그 음료를 장식(decorate) 해보는 방식은 어떨까요?

주문 시스템에 데코레이터 패턴 적용하기

  1. DarkRoast 객체에서 시작합니다.
  2. 고객이 모카를 주문했으니까 Mocha 객체를 만들고 그 객체로 DarkRoast를 감쌉니다.
  3. 고객이 휘핑크림도 추가헀으니까 Whip 데코레이터를 만들어 Mocha를 감쌉니다.
  4. 이제 가격을 계산해 볼까요? 가격을 구할 떄는 가장 바깥쪽에 있는 데코레이터인 Whip의 cost()를 호출하면 됩니다. 그러면 Whip은 그 객체가 장식하고 있는 객체에게 가격 계산을 위임합니다. 가격이 구해지고 나면 계산된 가격에 휘핑크림의 가격을 더 한 다음 그 결과값을 리턴합니다.

데코레이터는 자신이 장식하고 있는 객체에게 어떤 행동을 위임하는 일 말고도 추가 작업을 수행할 수 있습니다.

 

데코레이터 패턴(Decorator Pattern)으로 객체에 추가 요소를 동적으로 더할 수 있습니다. 데코레이터 패턴을 사용하면 서브클래스를 만들 때보다 훨씬 유연하게 기능을 확장할 수 있습니다.

 

Q&A

Q1. 이 코드를 그대로 쓰면 구상 구성 요소로 특별 할인 같은 작업을 처리할 때 문제가 생기지 않을까 걱정입니다.

A1. 네 그렇습니다. 구상 구성 요소로 돌아가는 코드를 만들어야 한다면 데코레이터 패턴 사용을 다시 한번 생각해 봐야 합니다.

 

java.io 클래스와 데코레이터 패턴

 

 

 

데코레이터 패턴 단점

  • 잡다한 클래스가 너무 많아집니다.
  • 데코레이터 기반 API를 사용해야 하는 개발자는 상당히 괴롭다.

 

하지만 데코레이터가 어떤 식으로 작동하는지 이해하면 다른 사람이 데코레이터 패턴을 활용해서 만든 API를 끌어 쓰더라도 클래스를 데코레이터로 감싸서 원하는 행동을 구현할 수 있습니다.

 

+ 그냥 데코레이터 패턴만 쓰는건 불편 → 팩토리와 빌더 패턴이 도와줘야 한다