Menu
CoddyTech

多态集合

本课属于 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 消息的通知系统、更新不同敌人类型的游戏,或处理多种文件格式的文档处理器。集合不关心具体类型,只要求每个元素都提供所需的行为。

challenge icon

挑战

简单

让我们构建一个任务管理系统,通过统一的接口处理不同类型的任务。你将创建多种任务类型,并将它们放在一个多态集合中统一处理。

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

  • 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] pts
    • Documentation,包含 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 并调用它
}
quiz icon自我测试

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

面向对象编程中的所有课

自己练习: Go 在线编译器