Menu
Coddy logo textTech

데코레이터 패턴

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

데코레이터 패턴은 객체의 코드를 수정하지 않고도 객체에 책임을 추가해야 할 때, 특히 다양한 기능의 조합이 필요할 때 이상적입니다. 이는 로깅, 캐싱, 인증 및 압축 레이어에 흔히 사용됩니다.

challenge icon

챌린지

쉬움

데코레이터(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

각 데코레이터가 이전 음료를 감싸면서 설명과 가격을 어떻게 쌓아가는지 확인해 보세요. 동일한 옵션을 여러 번 추가할 수 있으며(예: 더블 우유), 데코레이터의 순서가 설명의 순서를 결정합니다. 기본 커피는 추가 옵션에 대해 전혀 알지 못하며, 각 데코레이터는 단순히 자신이 감싸고 있는 것을 강화할 뿐입니다!

직접 해보기

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 형식으로 출력하세요
}
quiz icon실력 점검

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

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