通过接口实现多态
本课属于 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 接口。
这种方法能让你的代码保持灵活且易于扩展。添加一种新的会说话的类型时,无需修改现有函数,只需实现该接口,就能自动运行。
挑战
简单让我们构建一个车辆描述系统,演示多态的实际效果。你将创建不同类型的车辆,它们通过接口共享同一种行为,然后编写一个适用于任何车辆的函数。
你将把代码分成两个文件:
vehicles.go:定义一个Describer接口,要求实现Describe() string方法。然后创建三种车辆类型,每种类型都以自己的方式实现这个接口:Car,包含Brand和Model字段。它的Describe()返回Car: [Brand] [Model]Motorcycle,包含Brand和EngineCC(int)字段。它的Describe()返回Motorcycle: [Brand] [EngineCC]ccBicycle,包含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")
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Go 在线编译器