여러 구조체 임베딩하기
Coddy GO 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 107개 중 37번째.
Go에서는 하나의 구조체 안에 여러 구조체를 임베드하여 서로 다른 소스의 동작을 결합할 수 있습니다. 이는 다른 언어의 다중 상속과 유사하지만, Go의 컴포지션 접근 방식을 사용합니다.
type Logger struct{}
func (l Logger) Log(msg string) string {
return "LOG: " + msg
}
type Notifier struct{}
func (n Notifier) Notify(msg string) string {
return "NOTIFY: " + msg
}
type Service struct {
Name string
Logger
Notifier
}
이제 Service 구조체는 두 임베디드 타입의 메서드에 모두 액세스할 수 있습니다:
func main() {
s := Service{Name: "OrderService"}
fmt.Println(s.Log("started")) // LOG: 시작됨
fmt.Println(s.Notify("new order")) // NOTIFY: 새 주문
}
여러 구조체를 임베딩할 때, 두 임베딩된 타입에 같은 이름의 메서드가 있으면 naming conflict가 발생할 수 있습니다. Go는 이 모호성을 자동으로 해결하지 않습니다.
type A struct{}
func (A) Greet() string { return "Hello from A" }
type B struct{}
func (B) Greet() string { return "Hello from B" }
type Combined struct {
A
B
}
func main() {
c := Combined{}
// c.Greet() - 컴파일 오류: 모호한 선택자
fmt.Println(c.A.Greet()) // Hello from A
fmt.Println(c.B.Greet()) // Hello from B
}
충돌이 발생하면, call하려는 embedded type의 method를 type name을 한정자로 사용하여 explicitly 지정해야 합니다.
챌린지
쉬움struct 임베딩을 통해 여러 기능을 결합하는 스마트 홈 device 시스템을 만들어 봅시다. 별도의 controller 타입을 임베딩하여 조명을 제어하고 음악을 재생할 수 있는 device를 만듭니다.
코드를 세 개의 파일로 구성합니다:
controllers.go: 서로 다른 기능을 제공하는 두 개의 독립적인 controller struct를 Create합니다:LightController에는Brightnessfield (int)와SetLight(level int) stringmethod가 있으며, 이 method는Light set to [level]%을 반환합니다.AudioController에는Volumefield (int)와SetVolume(level int) stringmethod가 있으며, 이 method는Volume set to [level]%을 반환합니다.
Status() stringmethod도 가져야 합니다. 이로 인해 처리해야 할 naming conflict이 발생합니다.LightController.Status()는Brightness: [Brightness]%를 반환하고,AudioController.Status()는Volume: [Volume]%을 반환해야 합니다.device.go:LightController와AudioController를 모두 임베딩하고Namefield를 가진SmartDevicestruct를 Create합니다. device 이름과 두 controller의 status를 함께 반환하는FullStatus() stringmethod를 추가하고, 각 임베딩된 타입의Status()method를 명시적으로 호출하여 ambiguous한 상황을 resolve합니다.main.go: input에서 device 구성을 Read하고,SmartDevice를 Create한 다음, promoted method를 사용하여 조명과 음량 level을 설정하고 전체 status를 표시합니다.
다음 input이 제공됩니다:
- 1번째 줄: device 이름
- 2번째 줄: initial brightness level (정수)
- 3번째 줄: initial volume level (정수)
- 4번째 줄: 설정할 new brightness level (정수)
- 5번째 줄: 설정할 new volume level (정수)
FullStatus() method는 다음을 반환해야 합니다:
[Name] - [LightController.Status()], [AudioController.Status()]SetLight와 SetVolume을 호출한 결과를 출력하세요. 이 method들은 임베딩된 타입에서 promoted됩니다. 그런 다음 전체 status를 출력하세요.
예를 들어 Living Room Hub, 50, 30, 75, 60이 주어졌다면 출력은 다음과 같아야 합니다:
Light set to 75%
Volume set to 60%
Living Room Hub - Brightness: 75%, Volume: 60%SetLight와 SetVolume은 method promotion을 통해 SmartDevice에서 직접 접근할 수 있지만, 두 임베딩된 타입 모두 해당 method를 가지고 있으므로 Status()에는 명시적인 qualification이 필요하다는 점에 유의하세요.
직접 해보기
package main
import (
"fmt"
)
func main() {
// 입력 읽기
var name string
fmt.Scanln(&name)
var initialBrightness int
fmt.Scanln(&initialBrightness)
var initialVolume int
fmt.Scanln(&initialVolume)
var newBrightness int
fmt.Scanln(&newBrightness)
var newVolume int
fmt.Scanln(&newVolume)
// TODO: 주어진 이름과 초기값으로 SmartDevice 생성
// TODO: 승격된 SetLight 메서드를 사용하고 결과 출력
// TODO: 승격된 SetVolume 메서드를 사용하고 결과 출력
// TODO: FullStatus() 메서드를 사용하여 전체 상태 출력
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
8에러 처리와 OOP
error 인터페이스사용자 정의 에러 타입에러 래핑 (fmt.Errorf)센티넬 에러errors.Is()와 errors.As()Panic, Defer, Recover요약 - 파일 파서직접 연습해 보세요: 온라인 Go 컴파일러