Menu
CoddyTech

遮蔽嵌入的方法

本课属于 Coddy GO 学习旅程的“面向对象编程”单元。第 39 课,共 107 课。

有时,你需要让外层结构体提供自己的方法实现,而嵌入类型已经有这个方法了。这称为遮蔽,它可以让你用自定义行为覆盖提升的方法。

当你在外层结构体上定义一个与嵌入方法同名的方法时,外层方法优先:

type Animal struct {
    Name string
}

func (a Animal) Speak() string {
    return "Some sound"
}

type Dog struct {
    Animal
    Breed string
}

func (d Dog) Speak() string {
    return "Woof!"
}

现在,当你在 Dog 上调用 Speak() 时,会使用外层方法:

func main() {
    d := Dog{Animal: Animal{Name: "Rex"}, Breed: "Labrador"}
    
    fmt.Println(d.Speak())        // Woof!:Dog 的方法
    fmt.Println(d.Animal.Speak()) // Some sound:Animal 的方法
}

嵌入的方法并没有消失。你仍然可以通过嵌入类型的名称显式访问它。如果你想自定义行为,同时保留原始实现以供使用,这种模式就很有用。比如,你可以在重写的方法中调用原始实现:

func (d Dog) Speak() string {
    return d.Animal.Speak() + " (but actually Woof!)"
}

通过遮蔽,你可以控制对外提供哪种行为,同时通过显式限定仍可访问原始行为。

challenge icon

挑战

简单

我们来构建一个通知系统,让不同的通知渠道可以自定义消息的格式和发送方式。你将使用方法遮蔽,让特定渠道覆盖默认行为,同时在需要时仍能访问原始实现。

你将把代码分别放在三个文件中:

  • base.go:创建一个 BaseNotification 结构体,其中包含 Title 和 Priority 字段(两者都是字符串)。为它添加一个 Format() string 方法,以标准格式返回通知:[Priority] Title。
  • channels.go:创建两种嵌入 BaseNotification 并用各自实现遮蔽 Format 方法的通知渠道类型:
    • EmailNotification,包含一个 Recipient 字段。它的 Format() 应返回 Email to [Recipient]: [Priority] Title
    • SlackNotification,包含一个 Channel 字段。它的 Format() 应调用嵌入的 BaseNotification.Format() 方法,并在结果前添加 #[Channel]:
  • main.go:从输入中读取通知详情,创建这两种通知,并通过打印以下内容展示方法遮蔽:
    1. EmailNotification 格式化后的输出(使用它遮蔽后的方法)
    2. EmailNotification 的基础格式(显式调用嵌入的方法)
    3. SlackNotification 格式化后的输出(内部会使用基础方法)

输入内容如下:

  • 第 1 行:通知标题
  • 第 2 行:优先级
  • 第 3 行:电子邮件收件人
  • 第 4 行:Slack 频道名称

例如,输入 Server Alert、HIGH、admin@company.com 和 ops-alerts 时,输出应为:

Email to admin@company.com: HIGH Server Alert
[HIGH] Server Alert
#ops-alerts: [HIGH] Server Alert

注意,EmailNotification 会完全替换基础格式,而 SlackNotification 则会调用原始方法并添加频道信息来增强格式。这两种方式都展示了如何有效使用方法遮蔽。

亲手试试

package main

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

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	// 读取通知标题
	scanner.Scan()
	title := scanner.Text()
	
	// 读取优先级
	scanner.Scan()
	priority := scanner.Text()
	
	// 读取邮件收件人
	scanner.Scan()
	recipient := scanner.Text()
	
	// 读取 Slack 频道名称
	scanner.Scan()
	channel := scanner.Text()
	
	// TODO:使用基础通知、标题、优先级和收件人创建 EmailNotification
	
	// TODO:使用基础通知、标题、优先级和频道创建 SlackNotification
	
	// TODO:打印 EmailNotification 的格式化输出(使用其覆盖嵌入方法的方法)
	
	// TODO:打印 EmailNotification 的基础格式(显式调用嵌入的方法)
	
	// TODO:打印 SlackNotification 的格式化输出
	
	fmt.Println() // 占位代码,实现时删除
}
quiz icon自我测试

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

面向对象编程中的所有课

自己练习: Go 在线编译器