Menu
Coddy logo textTech

데코레이터 패턴

Coddy GO 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 107개 중 93번째.

Decorator 패턴을 사용하면 객체를 다른 객체로 감싸 동적으로 새로운 동작을 추가할 수 있습니다. Adapter는 다른 인터페이스에 맞게 인터페이스를 변경하는 반면, Decorator는 동일한 인터페이스를 유지하면서 원래 객체 주위에 래퍼를 겹겹이 추가하여 기능을 향상합니다.

Go에서는 base 타입과 데코레이터가 모두 동일한 interface를 구현하도록 하여 이를 구현합니다:

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의 강력함은 여러 래퍼를 쌓아 동작을 결합하는 데 있습니다:

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는 코드를 수정하지 않고 객체에 책임을 추가해야 할 때 이상적이며, 특히 다양한 기능 조합이 필요할 때 유용합니다. 일반적으로 logging, 캐싱, 인증 및 압축 계층에 사용됩니다.

challenge icon

챌린지

쉬움

Decorator 패턴을 사용하여 커피숍 주문 시스템을 만들어 봅시다! base 커피 음료와 milk, sugar, whipped cream 같은 extra를 추가하는 decorator를 만들게 됩니다. 각 decorator는 이전 decorator를 감싸 description과 가격을 함께 구성합니다. decorator가 기능을 계층적으로 추가하는 방식을 보여 주는 완벽한 실전 예제입니다.

코드를 세 개의 파일로 구성합니다:

  • beverage.go: 핵심 interface와 base 커피 타입을 Define합니다.

    다음 두 methods를 가진 Beverage interface를 만드세요:

    • Description() string: drink가 무엇인지 반환합니다
    • Cost() float64: 가격을 반환합니다

    base beverage로 Coffee struct를 Implement하세요. description은 Coffee이고 cost는 2.00이어야 합니다.

  • decorators.go: 모든 beverage에 extra를 추가하는 decorator 타입을 만드세요.

    각 decorator는 Beverage를 감싸고 동일한 interface를 Implement합니다. 다음 세 decorator를 만드세요:

    • MilkDecorator: description에 , Milk를 추가하고 cost에 0.50을 추가합니다
    • SugarDecorator: description에 , Sugar를 추가하고 cost에 0.25를 추가합니다
    • WhipDecorator: description에 , Whip을 추가하고 cost에 0.75를 추가합니다

    각 decorator는 감싼 beverage의 methods를 호출하고 그 결과를 확장해야 합니다.

  • main.go: decorator를 쌓아 customized drink를 만드세요.

    추가할 extra의 count를 읽습니다. 그런 다음 각 extra에 대해 그 타입(milk, sugar 또는 whip)을 읽고, 현재 beverage를 적절한 decorator로 감쌉니다.

    모든 extra를 적용한 후 최종 drink의 description과 cost를 각각 별도의 줄에 출력합니다. cost는 소수점 이하 정확히 두 자리인 $X.XX 형식으로 Format하세요.

다음 입력이 제공됩니다:

  • 1번째 줄: 추가할 extra의 수
  • 이후 줄: 줄마다 하나의 extra 타입(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가 이전 beverage를 감싸 description과 cost를 함께 구성하는 방식에 주목하세요. 동일한 extra를 여러 번 추가할 수 있으며(예: double milk), decorator의 순서가 description에 나타나는 순서를 결정합니다. base coffee는 extra에 대해 아무것도 알지 못합니다. 각 decorator는 자신이 감싸는 대상에 단순히 기능을 추가할 뿐입니다!

직접 해보기

package main

import (
	"fmt"
)

func main() {
	// 추가 항목의 개수를 읽습니다
	var count int
	fmt.Scanln(&count)

	// 기본 커피로 시작합니다
	var drink Beverage = &Coffee{}

	// 각 추가 항목을 읽고 적절한 데코레이터로 drink를 감쌉니다
	for i := 0; i < count; i++ {
		var extra string
		fmt.Scanln(&extra)

		// TODO: 추가 항목 유형에 따라 적절한 데코레이터로 drink를 감싸세요
		// "milk"에는 MilkDecorator를 사용합니다
		// "sugar"에는 SugarDecorator를 사용합니다
		// "whip"에는 WhipDecorator를 사용합니다
	}

	// TODO: 최종 설명과 비용을 출력합니다
	// 비용을 정확히 소수점 두 자리의 $X.XX 형식으로 포맷합니다
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨

직접 연습해 보세요: 온라인 Go 컴파일러