Menu
CoddyTech

通过接口实现多态

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

多态让不同类型通过共享接口得到统一处理。在 Go 中,这完全通过接口实现,无需继承或类层次结构。

当函数将接口类型作为参数时,任何实现该接口的具体类型都可以传入。函数不需要知道具体的类型,只关心接口定义的行为:

type Speaker interface {
    Speak() string
}

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

type Cat struct{ Name string }
func (c Cat) Speak() string { return "Meow!" }

func MakeSound(s Speaker) {
    fmt.Println(s.Speak())
}

现在,MakeSound 可以与任何具有 Speak() 方法的类型配合使用:

func main() {
    dog := Dog{Name: "Rex"}
    cat := Cat{Name: "Whiskers"}
    
    MakeSound(dog)  // 汪!
    MakeSound(cat)  // 喵!
}

同一个函数调用会根据传入值的实际类型产生不同的行为。这就是多态的实际应用。MakeSound 函数只需编写一次,就能适用于无限多种类型,只要这些类型满足 Speaker 接口。

这种方法能让你的代码保持灵活且易于扩展。添加一种新的会说话的类型时,无需修改现有函数,只需实现该接口,就能自动运行。

challenge icon

挑战

简单

让我们构建一个车辆描述系统,演示多态的实际效果。你将创建不同类型的车辆,它们通过接口共享同一种行为,然后编写一个适用于任何车辆的函数。

你将把代码分成两个文件:

  • vehicles.go:定义一个 Describer 接口,要求实现 Describe() string 方法。然后创建三种车辆类型,每种类型都以自己的方式实现这个接口:
    • Car,包含 Brand 和 Model 字段。它的 Describe() 返回 Car: [Brand] [Model]
    • Motorcycle,包含 Brand 和 EngineCC(int)字段。它的 Describe() 返回 Motorcycle: [Brand] [EngineCC]cc
    • Bicycle,包含 Type 字段(例如 "Mountain" 或 "Road")。它的 Describe() 返回 Bicycle: [Type]
  • main.go:创建一个名为 PrintDescription 的函数,它接受任何 Describer 并打印调用 Describe() 的结果。从输入中读取车辆详情,创建每种车辆各一个,并将它们分别传给 PrintDescription,以演示同一个函数可以适用于三种不同类型。

将提供以下输入:

  • 第 1 行:Car 品牌
  • 第 2 行:Car 型号
  • 第 3 行:Motorcycle 品牌
  • 第 4 行:Motorcycle 发动机排量(整数)
  • 第 5 行:Bicycle 类型

例如,输入 Toyota、Camry、Honda、600 和 Mountain 时,输出应为:

Car: Toyota Camry
Motorcycle: Honda 600cc
Bicycle: Mountain

注意,PrintDescription 不需要知道它接收的是 Car、Motorcycle 还是 Bicycle。它只需调用 Describe(),每种类型都会返回各自独特的输出。这就是多态:一个函数,多种行为。

亲手试试

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

// TODO: 创建一个名为 PrintDescription 的函数,接收任意 Describer
// 并打印调用 Describe() 的结果

func main() {
	reader := bufio.NewReader(os.Stdin)

	// 读取汽车信息
	carBrand, _ := reader.ReadString('\n')
	carBrand = strings.TrimSpace(carBrand)
	carModel, _ := reader.ReadString('\n')
	carModel = strings.TrimSpace(carModel)

	// 读取摩托车信息
	motoBrand, _ := reader.ReadString('\n')
	motoBrand = strings.TrimSpace(motoBrand)
	motoEngineStr, _ := reader.ReadString('\n')
	motoEngineStr = strings.TrimSpace(motoEngineStr)
	motoEngine, _ := strconv.Atoi(motoEngineStr)

	// 读取自行车信息
	bicycleType, _ := reader.ReadString('\n')
	bicycleType = strings.TrimSpace(bicycleType)

	// TODO: 使用输入的值创建 Car、Motorcycle 和 Bicycle

	// TODO: 对每种车辆调用 PrintDescription,演示多态
	fmt.Println("TODO: Print vehicle descriptions")
}
quiz icon自我测试

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

面向对象编程中的所有课

自己练习: Go 在线编译器