Menu
CoddyTech

嵌入多个结构体

本课属于 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 的问候
}

发生冲突时,你必须使用类型名作为限定符,明确指定要调用哪个嵌入类型的方法。

challenge icon

挑战

简单

让我们通过结构体嵌入来构建一个集成多种功能的智能家居设备系统。你将创建一个设备,通过嵌入不同的控制器类型,同时实现控制灯光和播放音乐的功能。

你会将代码分散到 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() 方法打印完整状态
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Go 在线编译器