Menu
Coddy logo textTech

메서드 프로모션

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

구조체 임베딩이 필드를 외부 구조체로 승격시키는 것과 마찬가지로, Go는 임베디드 타입의 메서드도 승격시킵니다. 이는 임베디드 구조체에 정의된 메서드를 외부 구조체에서 직접 호출할 수 있음을 의미합니다.

type Engine struct {
    Horsepower int
}

func (e Engine) Start() string {
    return "Engine started"
}

type Car struct {
    Model string
    Engine  // 임베디드됨
}

CarEngine을 임베딩하므로, Start() 메서드가 자동으로 승격됩니다:

func main() {
    c := Car{
        Model:  "Sedan",
        Engine: Engine{Horsepower: 200},
    }
    
    fmt.Println(c.Start())         // 엔진이 시작됨
    fmt.Println(c.Engine.Start())  // 이것도 작동합니다
}

이러한 승격(promotion)은 인터페이스에 중요한 결과를 가져옵니다. 임베디드 타입이 인터페이스를 충족하면, 외부 타입도 자동으로 해당 인터페이스를 충족하게 됩니다:

type Starter interface {
    Start() string
}

func Ignite(s Starter) {
    fmt.Println(s.Start())
}

func main() {
    c := Car{Model: "Sedan", Engine: Engine{Horsepower: 200}}
    Ignite(c)  // Car는 Engine을 통해 Starter를 충족합니다
}

Car 타입은 Starter를 명시적으로 구현하지 않지만, 임베디드된 Engine이 필요한 메서드를 가지고 있기 때문에 인터페이스를 충족합니다. 이것이 Go가 전통적인 상속 없이 동작 재사용을 달성하는 방법입니다.

challenge icon

챌린지

쉬움

임베디드 타입의 메서드가 외부 타입으로 어떻게 승격(promotion)되는지, 그리고 이것이 구성을 통해 인터페이스 만족을 어떻게 가능하게 하는지 보여주는 음악 플레이어 시스템을 구축해 보겠습니다.

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

  • audio.go: 음악 플레이어를 위한 구성 요소를 생성합니다:
    • Brand 필드(string)와 "[Brand] playing audio"를 반환하는 Play() string 메서드를 가진 AudioPlayer 구조체
    • Play() string 메서드를 요구하는 Playable 인터페이스
    • AudioPlayer를 임베드하고 Model 필드를 가진 Smartphone 구조체
    • AudioPlayer를 임베드하고 Model 필드를 가진 Tablet 구조체
  • main.go: 임의의 Playable을 인자로 받아 Play() 호출 결과를 반환하는 StartPlayback 함수를 생성합니다. 입력에서 기기 정보를 읽어 Smartphone과 Tablet을 모두 생성하고, 두 타입 모두 Play()를 직접 구현하지 않았음에도 임베드된 AudioPlayer를 통해 Playable 인터페이스를 만족함을 증명합니다.

다음 입력이 제공됩니다:

  • 1행: 스마트폰 모델 이름
  • 2행: 스마트폰 오디오 브랜드
  • 3행: 태블릿 모델 이름
  • 4행: 태블릿 오디오 브랜드

각 기기에 대해 모델 이름을 출력한 다음, 해당 기기를 StartPlayback에 전달한 결과를 출력하세요.

예를 들어, iPhone 15, Apple Audio, iPad Pro, Beats가 주어지면 출력은 다음과 같아야 합니다:

iPhone 15
Apple Audio playing audio
iPad Pro
Beats playing audio

AudioPlayerPlay() 메서드가 각 외부 타입으로 자동으로 승격되어, 추가 코드 없이도 SmartphoneTablet 모두 StartPlayback에 전달되어 Playable 인터페이스를 만족하는 방식에 주목하세요.

치트 시트

구조체가 다른 타입을 임베딩(embedding)하면, 임베딩된 타입의 메서드는 자동으로 외부 구조체로 승격(promote)됩니다:

type Engine struct {
    Horsepower int
}

func (e Engine) Start() string {
    return "Engine started"
}

type Car struct {
    Model string
    Engine  // 임베딩됨
}

func main() {
    c := Car{
        Model:  "Sedan",
        Engine: Engine{Horsepower: 200},
    }
    
    fmt.Println(c.Start())         // Engine started
    fmt.Println(c.Engine.Start())  // 역시 작동합니다
}

임베딩된 타입이 인터페이스를 충족하면, 외부 타입도 자동으로 해당 인터페이스를 충족하게 됩니다:

type Starter interface {
    Start() string
}

func Ignite(s Starter) {
    fmt.Println(s.Start())
}

func main() {
    c := Car{Model: "Sedan", Engine: Engine{Horsepower: 200}}
    Ignite(c)  // Car는 Engine을 통해 Starter를 충족합니다
}

이를 통해 전통적인 상속 없이도 구성을 통한 동작 재사용이 가능해집니다.

직접 해보기

package main

import (
	"bufio"
	"fmt"
	"os"
)

// TODO: 임의의 Playable을 수락하고
// Play() 호출 결과를 반환하는 StartPlayback 함수를 생성하세요.

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	// 스마트폰 모델 읽기
	scanner.Scan()
	smartphoneModel := scanner.Text()
	
	// 스마트폰 오디오 브랜드 읽기
	scanner.Scan()
	smartphoneBrand := scanner.Text()
	
	// 태블릿 모델 읽기
	scanner.Scan()
	tabletModel := scanner.Text()
	
	// 태블릿 오디오 브랜드 읽기
	scanner.Scan()
	tabletBrand := scanner.Text()
	
	// TODO: AudioPlayer가 포함된 Smartphone을 생성하세요.
	
	// TODO: AudioPlayer가 포함된 Tablet을 생성하세요.
	
	// TODO: 스마트폰 모델을 출력한 다음, 스마트폰으로 StartPlayback을 호출하세요.
	
	// TODO: 태블릿 모델을 출력한 다음, 태블릿으로 StartPlayback을 호출하세요.
	
	_ = smartphoneModel
	_ = smartphoneBrand
	_ = tabletModel
	_ = tabletBrand
	fmt.Println("TODO: Complete the implementation")
}
quiz icon실력 점검

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

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