템플릿 메서드 패턴
Coddy GO 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 107개 중 94번째.
템플릿 메서드 패턴(Template Method pattern)은 기본 타입에서 알고리즘의 골격을 정의하여, 하위 타입이 전체 구조를 변경하지 않고도 특정 단계를 재정의할 수 있도록 합니다. 데코레이터(Decorator)가 객체를 감싸서 동작을 추가하는 반면, 템플릿 메서드는 알고리즘의 흐름을 제어하고 개별 단계의 커스터마이징을 허용합니다.
Go에서는 상속이 없기 때문에, 구조체 임베딩(struct embedding)과 인터페이스를 결합하여 이 패턴을 구현합니다. "템플릿" 구조체는 알고리즘 구조를 정의하고 커스터마이징할 수 있는 메서드들을 호출합니다:
type DataProcessor interface {
ReadData() string
ProcessData(data string) string
SaveData(result string) string
}
type BaseProcessor struct {
Impl DataProcessor
}
func (b *BaseProcessor) Execute() string {
data := b.Impl.ReadData()
result := b.Impl.ProcessData(data)
return b.Impl.SaveData(result)
}구체적인 구현체들은 알고리즘의 흐름을 변경하지 않으면서 각 단계의 자체 버전을 제공합니다:
type CSVProcessor struct{}
func (c CSVProcessor) ReadData() string { return "csv-data" }
func (c CSVProcessor) ProcessData(d string) string { return "processed-" + d }
func (c CSVProcessor) SaveData(r string) string { return "Saved: " + r }
type JSONProcessor struct{}
func (j JSONProcessor) ReadData() string { return "json-data" }
func (j JSONProcessor) ProcessData(d string) string { return "parsed-" + d }
func (j JSONProcessor) SaveData(r string) string { return "Stored: " + r }템플릿 메서드 Execute는 정해진 순서대로 단계를 조율합니다:
csvProc := &BaseProcessor{Impl: CSVProcessor{}}
fmt.Println(csvProc.Execute()) // 저장됨: processed-csv-data
jsonProc := &BaseProcessor{Impl: JSONProcessor{}}
fmt.Println(jsonProc.Execute()) // 저장됨: parsed-json-data템플릿 메서드는 데이터 가져오기/내보내기 파이프라인, 보고서 생성기 또는 테스트 프레임워크와 같이 동일한 구조를 공유하지만 특정 단계에서 차이가 있는 알고리즘이 있을 때 이상적입니다.
챌린지
쉬움Template Method 패턴을 사용하여 보고서 생성 시스템을 구축해 봅시다! 서로 다른 보고서 유형이 데이터 수집, 포맷팅, 결과 출력이라는 동일한 생성 프로세스를 따르되, 각 보고서 유형이 이러한 단계들을 다르게 커스터마이징하는 프레임워크를 만들 것입니다.
코드를 세 개의 파일로 구성합니다:
report.go: 인터페이스와 보고서 생성 알고리즘을 조율하는 기본 프로세서를 정의합니다.보고서 생성 단계를 나타내는 세 가지 메서드를 가진
ReportGenerator인터페이스를 생성합니다:GatherData() string— 보고서의 원시 데이터를 가져옵니다.FormatData(data string) string— 데이터를 보고서 형식으로 변환합니다.OutputReport(formatted string) string— 최종 출력 메시지를 생성합니다.
ReportGenerator구현체를 보유하는ReportProcessor구조체를 만듭니다. 수집, 포맷팅, 출력의 세 단계를 순서대로 실행하고 최종 결과를 반환하는Generate() string메서드를 추가합니다.generators.go: 각 단계를 커스터마이징하는 구체적인 보고서 생성기를 구현합니다.두 가지 보고서 유형을 생성합니다:
SalesReport(`Region` 필드(string) 포함)GatherData()는 `sales-data-[region]`을 반환합니다.FormatData(data)는 `SALES REPORT: [data]`를 반환합니다.OutputReport(formatted)는 `Printed: [formatted]`를 반환합니다.
InventoryReport(`Warehouse` 필드(string) 포함)GatherData()는 `inventory-[warehouse]`을 반환합니다.FormatData(data)는 `*** [data] ***`를 반환합니다.OutputReport(formatted)는 `Exported: [formatted]`를 반환합니다.
main.go: 동일한 알고리즘 구조가 구현에 따라 어떻게 다른 결과를 생성하는지 보여줍니다.보고서 유형(
sales또는inventory)과 설정 값(sales의 경우 지역, inventory의 경우 창고)을 읽습니다. 적절한 생성기를 만들고, 이를ReportProcessor로 감싼 뒤,Generate()를 호출하고 결과를 출력합니다.
다음 입력이 제공됩니다:
- 1행: 보고서 유형 (
sales또는inventory) - 2행: 설정 값 (지역 이름 또는 창고 이름)
예를 들어, 다음과 같이 주어지면:
sales
North출력은 다음과 같아야 합니다:
Printed: SALES REPORT: sales-data-North그리고 다음과 같이 주어지면:
inventory
MainHub출력은 다음과 같아야 합니다:
Exported: *** inventory-MainHub ***그리고 다음과 같이 주어지면:
sales
West출력은 다음과 같아야 합니다:
Printed: SALES REPORT: sales-data-WestReportProcessor가 항상 동일한 세 단계를 동일한 순서로 호출하지만, 각 보고서 유형이 해당 단계의 자체 구현을 제공하는 방식에 주목하세요. 알고리즘의 뼈대는 고정되어 있고 세부 사항은 달라집니다. 이것이 바로 Template Method 패턴이 작동하는 방식입니다!
치트 시트
템플릿 메서드 패턴(Template Method pattern)은 알고리즘의 구조를 기본 타입에 정의하고, 하위 타입에서 전체 구조를 변경하지 않고 특정 단계를 재정의할 수 있도록 하는 패턴입니다.
Go에서는 구조체 임베딩(struct embedding)과 인터페이스를 결합하여 이 패턴을 구현합니다. "템플릿" 구조체는 알고리즘 구조를 정의하고 커스터마이징할 수 있는 메서드들을 호출합니다.
type DataProcessor interface {
ReadData() string
ProcessData(data string) string
SaveData(result string) string
}
type BaseProcessor struct {
Impl DataProcessor
}
func (b *BaseProcessor) Execute() string {
data := b.Impl.ReadData()
result := b.Impl.ProcessData(data)
return b.Impl.SaveData(result)
}구체적인 구현체들은 알고리즘의 흐름은 유지한 채 각 단계의 자체 버전을 제공합니다.
type CSVProcessor struct{}
func (c CSVProcessor) ReadData() string { return "csv-data" }
func (c CSVProcessor) ProcessData(d string) string { return "processed-" + d }
func (c CSVProcessor) SaveData(r string) string { return "Saved: " + r }
type JSONProcessor struct{}
func (j JSONProcessor) ReadData() string { return "json-data" }
func (j JSONProcessor) ProcessData(d string) string { return "parsed-" + d }
func (j JSONProcessor) SaveData(r string) string { return "Stored: " + r }템플릿 메서드는 정해진 순서대로 단계들을 조율합니다.
csvProc := &BaseProcessor{Impl: CSVProcessor{}}
fmt.Println(csvProc.Execute()) // Saved: processed-csv-data
jsonProc := &BaseProcessor{Impl: JSONProcessor{}}
fmt.Println(jsonProc.Execute()) // Stored: parsed-json-data템플릿 메서드 패턴은 데이터 임포트/엑스포트 파이프라인, 리포트 생성기 또는 테스트 프레임워크와 같이 알고리즘이 동일한 구조를 공유하지만 특정 단계가 다른 경우에 이상적입니다.
직접 해보기
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// 보고서 유형 읽기
scanner.Scan()
reportType := scanner.Text()
// 설정 값 읽기 (지역 또는 창고)
scanner.Scan()
configValue := scanner.Text()
// TODO: reportType에 따라 적절한 생성기(generator) 생성
// - reportType이 "sales"인 경우, Region을 configValue로 설정하여 SalesReport 생성
// - reportType이 "inventory"인 경우, Warehouse를 configValue로 설정하여 InventoryReport 생성
// TODO: 생성기를 사용하여 ReportProcessor 생성
// TODO: Generate()를 호출하고 결과 출력
_ = reportType
_ = configValue
fmt.Println("TODO: Generate and print the report")
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
8에러 처리와 OOP
error 인터페이스사용자 정의 에러 타입에러 래핑 (fmt.Errorf)센티넬 에러errors.Is()와 errors.As()Panic, Defer, Recover요약 - 파일 파서