-
[OOP] 디자인 패턴 - Decorator Pattern(데코레이터 패턴)프로그래밍 2024. 7. 17. 21:52
## 데코레이터 패턴(Decorator Pattern).
객체에 추가적인 요건을 동적으로 첨가한다. 데코레이터는 서브클래스를 만드는 것을 통해서 기능을 유연하게 확장할 수 있는 방법을 제공한다.
## 커피 클래스를 구현해보자 - (1)
우리가 수 많은 커피를 클래스로 구현한다고 했을때,
일반적으로 아래와 같은 구조를 생각하기 쉽다.
나 또한 가장 먼저 이런 생각이 떠올랐었고,
회사에 다니는 젊은 개발자들에게 물어봤을때도 위와 같은 구조를 우선적으로 많이 말씀하셨다.
하지만 조금만 더 고민해보면 이런 구조는 문제가 있는걸 알 수 있다.
커피는 너~무 많다.
수십, 수백가지 되는 커피들이 있고,
커피콩은 어떤것을 쓸 것이고
두유를 첨가할 지 우유를 첨가할 지,
샷은 얼마나 추가할 것인지,
휘핑크림 넣고 빼고,
이런것들을 고려해야 하며,
해당 커피들을 모두 상속으로 처리하면 클래스가 너무 많아지고 관리하기 힘들어질 것이다.
## 커피 클래스를 구현해보자 - (2)
그렇다면 생각을 조금 바꿔서, 샷, 두유 등의 속성을 변수로 가져와 아래와 같은 구조로 클래스를 구현하면 어떨까?
위와 같은 형태가 된다 해도 여전히 문제가 있다.
### 문제점 1 - 첨가물의 종류가 추가되거나 변경되면 서브클래스에 영향이 갈 수 있음
"여름 한정 신메뉴가 추가되었습니다. 당분간 레몬시럽을 첨가한 커피를 팔거에요."
첨가물의 종류가 추가되거나 변경된다면? Beverage 클래스에 변수를 수정할 것이고,
그것을 상속받고 있는 서브클래스들에게 영향이 갈 수 있다.
### 문제점 2 - 필요없는 변수를 가지고 있는 서브클래스들이 있음.
특정 첨가물이 필요없는 서브클래스가 있을 수 있다.
예를들어 IceTea 클래스는 Beverage이지만, milk , whip , mocha 등이 필요없다.
## 커피 클래스를 변경해보자 - 데코레이터 패턴
- Beverage 클래스를 상속받은 CondimentDecorator 클래스가 생겼다.
상속을 쓰지만, 형식을 맞추기 위한 것일 뿐, 상속을 통해 행동을 물려받는 목적이 아니다. - 데코레이터를 통해 행동이 추가되는 것이다.
class Expresso: Beverage { var description: String = "에스프레소 커피" func cost() -> Float { return 2000 } } class DarkRoast: Beverage { var description: String = "다크로스팅 커피" func cost() -> Float { return 3000 } }
protocol CondimentDecorator: Beverage { var beverage: Beverage {get set} } class Soy: CondimentDecorator { var beverage: Beverage var description: String init(beverage: Beverage) { self.description = beverage.description + ", 두유" self.beverage = beverage } func cost() -> Float { return beverage.cost() + 200 } } class Whip: CondimentDecorator { var beverage: Beverage var description: String init(beverage: Beverage) { self.description = beverage.description + ", 휘핑" self.beverage = beverage } func cost() -> Float { return beverage.cost() + 120 } }
위와 같이 첨가물(CondimentDecorator)와 그 서브클래스들(Soy, Whip, Mocha..) 등을 만들면 준비는 끝났다.
이제 커피를 만들때에도,
var drink: Beverage = DarkRoast() // 다크 로스팅된 커피 생성 drink = Soy(beverage drink) // 두유 추가 drink = Whip(beverage drink) // 휘핑 크림 추가
이런 식으로 다크 로스트 원두 커피에, 두유와 휘핑크림을 추가하는(decorate 하는) 형태로서
동적으로 클래스를 변경하는게 가능하다.
## 정리
데코레이터 패턴(Decorator Pattern)은 서브클래스를 만드는 것을 통해서 기능을 유연하게 확장할 수 있는 방법을 제공한다.
- 여기서는 CondimentDecorator가 Beverage를 상속받음으로서 형식을 맞춰 기능을 유연하게 확장할 수 있는 방법을 제공했다.
- 메소드 체이닝과 같이 생각해보면 좋을 것 같다. 만약 커피 만들기를 메소드체이닝으로 하면 어땠을까?
## Ref
- Head First Design Patterns
'프로그래밍' 카테고리의 다른 글
[OOP] 디자인 패턴 - Strategy Pattern(전략 패턴) (0) 2024.07.16 [OOP] Law of Demeter: 최소 지식 원칙 (0) 2024.07.11 - Beverage 클래스를 상속받은 CondimentDecorator 클래스가 생겼다.