Menu
Coddy logo textTech

전략 패턴

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

전략 패턴(Strategy pattern)은 알고리즘 군을 정의하고, 각각을 캡슐화하여 실행 시점에 서로 교체 가능하게 만듭니다. 여러 객체가 하나의 이벤트에 반응하는 옵저버(Observer) 패턴과 달리, 전략 패턴은 단일 객체가 동적으로 다양한 동작 간에 전환할 수 있도록 합니다.

Go 언어에서 알고리즘을 위한 인터페이스를 정의하고 컨텍스트 구조체(context struct)에 다양한 구현체를 주입함으로써 Strategy 패턴을 구현합니다:

type PaymentStrategy interface {
    Pay(amount float64) string
}

type CreditCard struct{}
func (c CreditCard) Pay(amount float64) string {
    return fmt.Sprintf("Paid %.2f via Credit Card", amount)
}

type PayPal struct{}
func (p PayPal) Pay(amount float64) string {
    return fmt.Sprintf("Paid %.2f via PayPal", amount)
}

context 구조체는 strategy 인터페이스에 대한 참조를 유지하므로, 컨텍스트의 코드를 변경하지 않고도 알고리즘을 교체할 수 있습니다:

type Checkout struct {
    strategy PaymentStrategy
}

func (c *Checkout) SetStrategy(s PaymentStrategy) {
    c.strategy = s
}

func (c *Checkout) ProcessPayment(amount float64) string {
    return c.strategy.Pay(amount)
}

이제 런타임에 결제 수단을 변경할 수 있습니다:

checkout := &Checkout{}

checkout.SetStrategy(CreditCard{})
fmt.Println(checkout.ProcessPayment(100.00))  // 신용카드로 100.00 결제됨

checkout.SetStrategy(PayPal{})
fmt.Println(checkout.ProcessPayment(50.00))   // PayPal로 50.00 결제됨

전략 패턴은 작업을 수행하는 여러 가지 방법이 있고 사용자 입력, 설정 또는 런타임 조건에 따라 그중 하나를 선택해야 할 때 이상적입니다. 일반적인 사용 사례로는 정렬 알고리즘, 압축 방법 및 검증 규칙 등이 있습니다.

challenge icon

챌린지

쉬움

전략(Strategy) 패턴을 사용하여 텍스트 포맷팅 시스템을 만들어 봅시다! 런타임에 교체 가능한 다양한 포맷팅 전략을 생성하여, 동일한 텍스트 프로세서가 핵심 코드를 변경하지 않고도 서로 다른 출력 스타일을 생성할 수 있도록 할 것입니다.

코드는 다음 세 개의 파일로 구성됩니다:

  • formatter.go: 전략 인터페이스와 구체적인 포맷팅 전략을 정의합니다.

    전략의 규칙에 따라 텍스트를 변환하는 Format(text string) string 메서드를 가진 TextFormatter 인터페이스를 생성하세요.

    세 가지 포맷팅 전략을 구현하세요:

    • UppercaseFormatter — 텍스트를 모두 대문자로 변환합니다.
    • SnakeCaseFormatter — 공백을 언더스코어(_)로 바꾸고 텍스트를 소문자로 변환합니다.
    • TitleFormatter — 각 단어의 첫 글자를 대문자로 변환합니다 (단어는 공백으로 구분됩니다).
  • processor.go: 포맷팅 전략을 사용하는 컨텍스트 구조체를 생성합니다.

    TextFormatter 전략을 보유하는 TextProcessor 구조체를 만드세요. 다음 메서드들을 추가하세요:

    • SetFormatter(f TextFormatter): 활성 포맷팅 전략을 변경합니다.
    • Process(text string) string: 현재 포맷터를 텍스트에 적용합니다.

    또한 초기 포맷터가 설정되지 않은 프로세서를 반환하는 NewTextProcessor() 생성자를 만드세요.

  • main.go: 런타임에 전략을 전환하는 것을 시연합니다.

    텍스트 한 줄을 읽은 다음, 포맷 작업 횟수를 읽습니다. 각 작업에 대해 포맷 유형(upper, snake, 또는 title)을 읽고, 프로세서에 적절한 포맷터를 설정한 뒤, 원본 텍스트를 처리하고 결과를 출력하세요.

다음과 같은 입력이 제공됩니다:

  • 1행: 포맷할 텍스트
  • 2행: 포맷 작업 횟수
  • 다음 행들: 각 작업에 대한 포맷 유형

예를 들어, 다음과 같이 입력되면:

hello world today
3
upper
snake
title

출력은 다음과 같아야 합니다:

HELLO WORLD TODAY
hello_world_today
Hello World Today

그리고 다음과 같이 입력되면:

Go Programming Language
2
snake
upper

출력은 다음과 같아야 합니다:

go_programming_language
GO PROGRAMMING LANGUAGE

그리고 다음과 같이 입력되면:

design patterns are useful
1
title

출력은 다음과 같아야 합니다:

Design Patterns Are Useful

동일한 TextProcessor가 포맷터 전략을 교체하는 것만으로 어떻게 완전히 다른 출력을 생성하는지 확인해 보세요. 프로세서는 자신이 어떤 구체적인 포맷터를 사용하는지 알 필요도 없고 신경 쓰지도 않습니다. 그저 현재 설정된 전략에 작업을 위임할 뿐입니다!

치트 시트

전략 패턴(Strategy pattern)은 일련의 알고리즘을 정의하고, 각 알고리즘을 캡슐화하며, 실행 중에 이들을 서로 교체할 수 있도록 만듭니다. 이를 통해 단일 객체가 동적으로 다양한 동작 간에 전환할 수 있습니다.

알고리즘을 위한 인터페이스를 정의합니다:

type PaymentStrategy interface {
    Pay(amount float64) string
}

구체적인 전략들을 구현합니다:

type CreditCard struct{}
func (c CreditCard) Pay(amount float64) string {
    return fmt.Sprintf("Paid %.2f via Credit Card", amount)
}

type PayPal struct{}
func (p PayPal) Pay(amount float64) string {
    return fmt.Sprintf("Paid %.2f via PayPal", amount)
}

전략 인터페이스에 대한 참조를 보유하는 컨텍스트 구조체를 생성합니다:

type Checkout struct {
    strategy PaymentStrategy
}

func (c *Checkout) SetStrategy(s PaymentStrategy) {
    c.strategy = s
}

func (c *Checkout) ProcessPayment(amount float64) string {
    return c.strategy.Pay(amount)
}

실행 중에 전략을 변경합니다:

checkout := &Checkout{}

checkout.SetStrategy(CreditCard{})
fmt.Println(checkout.ProcessPayment(100.00))  // Credit Card를 통해 100.00 결제됨

checkout.SetStrategy(PayPal{})
fmt.Println(checkout.ProcessPayment(50.00))   // PayPal을 통해 50.00 결제됨

전략 패턴은 작업을 수행하는 여러 방법이 있고 사용자 입력, 설정 또는 실행 시점의 조건에 따라 하나를 선택해야 할 때 이상적입니다.

직접 해보기

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)
	
	// 포맷할 텍스트를 읽습니다
	text, _ := reader.ReadString('\n')
	text = strings.TrimSpace(text)
	
	// 작업 횟수를 읽습니다
	countStr, _ := reader.ReadString('\n')
	count, _ := strconv.Atoi(strings.TrimSpace(countStr))
	
	// 새로운 텍스트 프로세서를 생성합니다
	processor := NewTextProcessor()
	
	// 각 포맷 작업을 처리합니다
	for i := 0; i < count; i++ {
		formatType, _ := reader.ReadString('\n')
		formatType = strings.TrimSpace(formatType)
		
		// TODO: formatType("upper", "snake", 또는 "title")에 따라
		// 프로세서에 적절한 포맷터를 설정하고
		// 처리된 텍스트를 출력하세요
		
	}
}
quiz icon실력 점검

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

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