多态集合
本课属于 Coddy GO 学习旅程的“面向对象编程”单元。第 44 课,共 107 课。
接口最强大的应用之一,是在同一个集合中存储不同类型的值。接口类型的切片可以保存任何满足该接口的值,让你将相关但类型不同的值分组在一起。
设想一种需要管理各种形状的场景。你可以使用一个接口类型的切片,而不是为每种类型分别创建切片:
type Shape interface {
Area() float64
}
type Circle struct{ Radius float64 }
func (c Circle) Area() float64 { return 3.14159 * c.Radius * c.Radius }
type Rectangle struct{ Width, Height float64 }
func (r Rectangle) Area() float64 { return r.Width * r.Height }
func main() {
shapes := []Shape{
Circle{Radius: 5},
Rectangle{Width: 4, Height: 3},
Circle{Radius: 2},
}
for _, s := range shapes {
fmt.Printf("Area: %.2f\n", s.Area())
}
}
shapes 切片中既包含圆形,也包含矩形。遍历时,每个元素都会根据其实际类型调用相应的 Area()。这就是多态在集合中的应用:同一个循环可以统一处理所有形状类型。
构建处理不同项目的系统时,多态集合至关重要:例如发送电子邮件和 SMS 消息的通知系统、更新不同敌人类型的游戏,或处理多种文件格式的文档处理器。集合不关心具体类型,只要求每个元素都提供所需的行为。
挑战
简单让我们构建一个任务管理系统,通过统一的接口处理不同类型的任务。你将创建多种任务类型,并将它们放在一个多态集合中统一处理。
你将把代码分布在三个文件中:
task.go:定义一个Task接口,要求实现Summary() string方法。这个接口是所有任务类型都必须满足的通用约定。types.go:创建三种不同的任务类型,每种类型都实现Task接口:BugFix,包含ID(string)和Severity(string)字段。它的Summary()返回Bug #[ID] ([Severity])Feature,包含Name(string)和Points(int)字段。它的Summary()返回Feature: [Name] - [Points] ptsDocumentation,包含Topic(string)字段。它的Summary()返回Docs: [Topic]
main.go:创建一个名为PrintBacklog的函数,它接收一个Task切片,并将每个任务的摘要单独打印在一行中。从输入中读取任务详情,为每种任务类型各创建一个实例,将它们收集到同一个[]Task切片中,然后传给PrintBacklog。
将提供以下输入:
- 第 1 行:Bug ID
- 第 2 行:Bug 严重程度
- 第 3 行:Feature 名称
- 第 4 行:Feature 点数(整数)
- 第 5 行:Documentation 主题
例如,给定 1042、critical、Dark Mode、8 和 API Reference,你的输出应为:
Bug #1042 (critical)
Feature: Dark Mode - 8 pts
Docs: API Reference这种设计的优势在于,PrintBacklog 不需要为 Bug、Feature 或 Documentation 编写单独的逻辑。它只需遍历切片,并对每个元素调用 Summary()。每种任务类型都会以自己的独特格式响应,这展示了多态集合如何让你统一处理不同类型。
亲手试试
package main
import (
"fmt"
)
// TODO: 创建一个接收 Task 切片的 PrintBacklog 函数
// 并将每个任务的摘要分别打印在单独的一行
func main() {
// 读取输入
var bugID string
var bugSeverity string
var featureName string
var featurePoints int
var docTopic string
fmt.Scanln(&bugID)
fmt.Scanln(&bugSeverity)
fmt.Scanln(&featureName)
fmt.Scanln(&featurePoints)
fmt.Scanln(&docTopic)
// TODO: 为每种任务类型(BugFix、Feature、Documentation)各创建一个任务
// TODO: 将所有任务放入同一个 []Task 切片
// TODO: 将任务切片传给 PrintBacklog 并调用它
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Go 在线编译器