嵌入多个结构体
本课属于 Coddy GO 学习旅程的“面向对象编程”单元。第 37 课,共 107 课。
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: started
fmt.Println(s.Notify("new order")) // NOTIFY: new order
}
嵌入多个结构体时,如果两个嵌入类型的方法同名,就可能发生命名冲突。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()) // 来自 A 的问候
fmt.Println(c.B.Greet()) // 来自 B 的问候
}
发生冲突时,你必须使用类型名作为限定符,明确指定要调用哪个嵌入类型的方法。
挑战
简单让我们通过结构体嵌入来构建一个集成多种功能的智能家居设备系统。你将创建一个设备,通过嵌入不同的控制器类型,同时实现控制灯光和播放音乐的功能。
你会将代码分散到 3 个文件中:
controllers.go:创建两个独立的控制器结构体,分别提供不同的功能:- 一个
LightController,包含Brightness字段(int)和一个SetLight(level int) string方法,该方法返回Light set to [level]% - 一个
AudioController,包含Volume字段(int)和一个SetVolume(level int) string方法,该方法返回Volume set to [level]%
Status() string方法。这会产生一个需要你处理的命名冲突。LightController.Status()应返回Brightness: [Brightness]%,而AudioController.Status()应返回Volume: [Volume]%。- 一个
device.go:创建一个带有Name字段的SmartDevice结构体,并嵌入LightController和AudioController。添加一个名为FullStatus() string的方法,使其返回设备名称以及两个控制器的状态。你需要显式调用每个嵌入类型的Status()方法来消除歧义。main.go:从输入中读取设备配置,创建一个SmartDevice,使用提升的方法设置灯光和音量,然后显示完整状态。
将提供以下输入:
- 第 1 行:设备名称
- 第 2 行:初始亮度级别(整数)
- 第 3 行:初始音量级别(整数)
- 第 4 行:要设置的新亮度级别(整数)
- 第 5 行:要设置的新音量级别(整数)
你的 FullStatus() 方法应返回:
[Name] - [LightController.Status()], [AudioController.Status()]打印调用 SetLight 和 SetVolume 的结果(这两个方法是从嵌入类型提升而来的),然后打印完整状态。
例如,输入 Living Room Hub、50、30、75 和 60 时,输出应为:
Light set to 75%
Volume set to 60%
Living Room Hub - Brightness: 75%, Volume: 60%请注意,SetLight 和 SetVolume 通过方法提升,可以直接在 SmartDevice 上访问;但由于两个嵌入类型都有 Status() 方法,因此需要显式限定才能调用。
亲手试试
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() 方法打印完整状态
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Go 在线编译器