데코레이터 패턴
Coddy GO 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 107개 중 93번째.
데코레이터 패턴(Decorator pattern)은 객체를 다른 객체로 감싸서 동적으로 새로운 동작을 추가할 수 있게 해줍니다. 어댑터(Adapter)가 인터페이스를 다른 인터페이스에 맞게 변경하는 반면, 데코레이터는 동일한 인터페이스를 유지하면서 원래 객체 주변에 래퍼를 겹겹이 쌓아 기능을 확장합니다.
Go에서는 기본 타입과 데코레이터가 모두 동일한 인터페이스를 구현하도록 하여 이를 구현합니다:
type Notifier interface {
Send(message string) string
}
type BasicNotifier struct{}
func (b BasicNotifier) Send(message string) string {
return "Sending: " + message
}데코레이터는 다른 Notifier를 감싸고, 해당 객체에 위임하기 전이나 후에 동작을 추가합니다:
type TimestampDecorator struct {
wrapped Notifier
}
func (t TimestampDecorator) Send(message string) string {
timestamped := "[2024-01-15] " + message
return t.wrapped.Send(timestamped)
}
type UppercaseDecorator struct {
wrapped Notifier
}
func (u UppercaseDecorator) Send(message string) string {
return strings.ToUpper(u.wrapped.Send(message))
}데코레이터(Decorator)의 강력함은 여러 래퍼(wrapper)를 쌓아 동작을 결합하는 데 있습니다:
notifier := BasicNotifier{}
withTimestamp := TimestampDecorator{wrapped: notifier}
withBoth := UppercaseDecorator{wrapped: withTimestamp}
fmt.Println(notifier.Send("Hello"))
// 전송 중: Hello
fmt.Println(withBoth.Send("Hello"))
// 전송 중: [2024-01-15] HELLO데코레이터 패턴은 객체의 코드를 수정하지 않고도 객체에 책임을 추가해야 할 때, 특히 다양한 기능의 조합이 필요할 때 이상적입니다. 이는 로깅, 캐싱, 인증 및 압축 레이어에 흔히 사용됩니다.
챌린지
쉬움데코레이터(Decorator) 패턴을 사용하여 커피숍 주문 시스템을 구축해 봅시다! 기본 커피 음료와 우유, 설탕, 휘핑크림과 같은 추가 옵션을 더하는 데코레이터들을 만들게 됩니다. 각 데코레이터는 이전 단계를 감싸며 설명과 가격을 모두 누적해 나갑니다. 이는 데코레이터가 어떻게 기능을 계층적으로 쌓아 올리는지 보여주는 완벽한 실무 예제입니다.
코드는 다음 세 개의 파일로 구성됩니다:
beverage.go: 핵심 인터페이스와 기본 커피 타입을 정의합니다.두 개의 메서드를 가진
Beverage인터페이스를 생성하세요:Description() string— 음료가 무엇인지 반환합니다.Cost() float64— 가격을 반환합니다.
기본 음료로
Coffee구조체를 구현하세요. 설명은Coffee여야 하며 가격은2.00이어야 합니다.decorators.go: 어떤 음료에도 추가 옵션을 더할 수 있는 데코레이터 타입들을 생성합니다.각 데코레이터는
Beverage를 감싸고 동일한 인터페이스를 구현합니다. 세 개의 데코레이터를 만드세요:MilkDecorator— 설명에, Milk를 추가하고 가격에0.50을 더합니다.SugarDecorator— 설명에, Sugar를 추가하고 가격에0.25를 더합니다.WhipDecorator— 설명에, Whip를 추가하고 가격에0.75를 더합니다.
각 데코레이터는 감싸고 있는 음료의 메서드를 호출하고 그 결과를 강화해야 합니다.
main.go: 데코레이터를 쌓아서 맞춤형 음료를 만듭니다.추가할 옵션의 개수를 읽습니다. 그런 다음 각 옵션에 대해 타입(
milk,sugar, 또는whip)을 읽고 현재 음료를 적절한 데코레이터로 감싸세요.모든 옵션을 적용한 후, 최종 음료의 설명과 가격을 별도의 줄에 출력하세요. 가격은 소수점 이하 두 자리까지 포함하여
$X.XX형식으로 출력합니다.
다음과 같은 입력이 제공됩니다:
- 첫 번째 줄: 추가할 옵션의 개수
- 다음 줄들: 한 줄에 하나씩의 옵션 타입 (
milk,sugar, 또는whip)
예를 들어, 다음과 같이 입력되면:
2
milk
sugar출력은 다음과 같아야 합니다:
Coffee, Milk, Sugar
$2.75그리고 다음과 같이 입력되면:
3
whip
milk
milk출력은 다음과 같아야 합니다:
Coffee, Whip, Milk, Milk
$3.75그리고 다음과 같이 입력되면:
0출력은 다음과 같아야 합니다:
Coffee
$2.00각 데코레이터가 이전 음료를 감싸면서 설명과 가격을 어떻게 쌓아가는지 확인해 보세요. 동일한 옵션을 여러 번 추가할 수 있으며(예: 더블 우유), 데코레이터의 순서가 설명의 순서를 결정합니다. 기본 커피는 추가 옵션에 대해 전혀 알지 못하며, 각 데코레이터는 단순히 자신이 감싸고 있는 것을 강화할 뿐입니다!
치트 시트
데코레이터 패턴(Decorator pattern)은 객체를 다른 객체로 감싸서 동적으로 새로운 동작을 추가합니다. 동일한 인터페이스를 유지하면서 레이어링을 통해 기능을 향상시킵니다.
기본 타입과 데코레이터 모두 동일한 인터페이스를 구현합니다:
type Notifier interface {
Send(message string) string
}
type BasicNotifier struct{}
func (b BasicNotifier) Send(message string) string {
return "Sending: " + message
}데코레이터는 다른 객체를 감싸고 위임 전후에 동작을 추가합니다:
type TimestampDecorator struct {
wrapped Notifier
}
func (t TimestampDecorator) Send(message string) string {
timestamped := "[2024-01-15] " + message
return t.wrapped.Send(timestamped)
}
type UppercaseDecorator struct {
wrapped Notifier
}
func (u UppercaseDecorator) Send(message string) string {
return strings.ToUpper(u.wrapped.Send(message))
}여러 데코레이터를 쌓아서 동작을 결합할 수 있습니다:
notifier := BasicNotifier{}
withTimestamp := TimestampDecorator{wrapped: notifier}
withBoth := UppercaseDecorator{wrapped: withTimestamp}
fmt.Println(notifier.Send("Hello"))
// Sending: Hello
fmt.Println(withBoth.Send("Hello"))
// SENDING: [2024-01-15] HELLO객체 코드를 수정하지 않고 책임을 추가하기 위해 데코레이터를 사용하며, 특히 다양한 기능 조합이 필요한 경우에 유용합니다. 일반적인 용도: 로깅, 캐싱, 인증, 압축.
직접 해보기
package main
import (
"fmt"
)
func main() {
// 추가 항목의 개수를 읽습니다
var count int
fmt.Scanln(&count)
// 기본 커피로 시작합니다
var drink Beverage = &Coffee{}
// 각 추가 항목을 읽고 적절한 데코레이터로 음료를 감쌉니다
for i := 0; i < count; i++ {
var extra string
fmt.Scanln(&extra)
// TODO: 추가 항목 유형에 따라 적절한 데코레이터로 음료를 감싸세요
// "milk"인 경우 MilkDecorator를 사용하세요
// "sugar"인 경우 SugarDecorator를 사용하세요
// "whip"인 경우 WhipDecorator를 사용하세요
}
// TODO: 최종 설명과 가격을 출력하세요
// 가격은 소수점 둘째 자리까지 $X.XX 형식으로 출력하세요
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
8에러 처리와 OOP
error 인터페이스사용자 정의 에러 타입에러 래핑 (fmt.Errorf)센티넬 에러errors.Is()와 errors.As()Panic, Defer, Recover요약 - 파일 파서