Menu
CoddyTech

在线学习平台

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

challenge icon

挑战

简单

让我们构建一个电子学习平台,用来管理课程和学生,并跟踪学习进度!你将结合结构体、接口、组合和多态,创建一个完整的系统,让学生能够注册课程并完成课时。

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

  • lesson.go:定义一个 Lesson 结构体,用来表示单个学习单元。每个课时都有一个 ID、Title 和 Duration(以分钟为单位)。添加一个 NewLesson 构造函数,返回指向 Lesson 的指针。
  • course.go:创建一个 Course 结构体,其中包含 ID、Title、Instructor,以及一个由 *Lesson 指针组成的切片。添加一个 NewCourse 构造函数和一个 AddLesson 方法。还要实现一个 TotalDuration() int 方法,返回课程中所有课时的总时长。
  • student.go:定义一个包含 ID、Name 和 Email 字段的 Student 结构体。添加一个 NewStudent 构造函数。
  • enrollment.go:进度跟踪功能将在这里实现。创建一个 Enrollment 结构体,将学生和课程关联起来,并跟踪学生已完成的课时。它应包含:
    • 一个指向 Student 的指针
    • 一个指向 Course 的指针
    • 一个 CompletedLessons 字典(课时 ID 对应布尔值)

    定义一个 Progressable 接口,其中包含一个 Progress() float64 方法,返回百分比(0.0 到 100.0)。

    为 Enrollment 实现以下方法:

    • NewEnrollment(student *Student, course *Course) *Enrollment:构造函数,用于初始化一个空的已完成课时字典
    • CompleteLesson(lessonID string) string:将课时标记为已完成。如果课程中不存在该课时,则返回 lesson not found;如果已经完成,则返回 already completed;成功时返回 completed
    • Progress() float64:返回已完成课时的百分比(已完成数量/总数量 * 100);如果课程没有课时,则返回 0.0
  • main.go:构建平台并处理选课操作。

    读取课程的 ID、标题和讲师。然后读取课时数量,以及每个课时的 ID、标题和时长。读取学生的 ID、姓名和电子邮箱。创建一个选课记录,将学生和课程关联起来。

    然后读取操作数量。每个操作可以是 complete [lessonID] 或 progress。对于完成课时的操作,打印结果消息。对于进度操作,将进度格式化为小数点后一位,并在末尾加上百分号后打印。

输入内容如下:

  • 课程 ID、标题、讲师(3 行)
  • 课时数量,然后是每个课时的 ID、标题和时长(每个课时 3 行)
  • 学生 ID、姓名、电子邮箱(3 行)
  • 操作数量,然后是每个操作(每个操作 1 行)

例如,给定以下输入:

C001
Go Fundamentals
Jane Smith
3
L001
Variables
30
L002
Functions
45
L003
Structs
60
S001
Alice
alice@learn.com
5
progress
complete L001
progress
complete L001
complete L002

输出应为:

0.0%
completed
33.3%
already completed
completed

再例如,给定以下输入:

C100
Python Basics
Bob Teacher
2
L100
Intro
20
L101
Loops
40
S100
Charlie
charlie@edu.com
4
complete L999
complete L100
complete L101
progress

输出应为:

lesson not found
completed
completed
100.0%

注意,Enrollment 结构体通过组合将学生和课程关联起来,而 Progressable 接口允许任何类型报告其完成状态。这种设计便于扩展。你可以使用相同的接口,为单个课时或整个学习路径添加进度跟踪功能!

亲手试试

package main

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

func main() {
	scanner := bufio.NewScanner(os.Stdin)

	// 用于读取一行的辅助函数
	readLine := func() string {
		scanner.Scan()
		return strings.TrimSpace(scanner.Text())
	}

	// 读取课程信息
	courseID := readLine()
	courseTitle := readLine()
	courseInstructor := readLine()

	// TODO:使用 NewCourse 创建课程
	_ = courseID
	_ = courseTitle
	_ = courseInstructor

	// 读取课的数量
	numLessons, _ := strconv.Atoi(readLine())

	// TODO:读取每节课并添加到课程中
	for i := 0; i < numLessons; i++ {
		lessonID := readLine()
		lessonTitle := readLine()
		lessonDuration, _ := strconv.Atoi(readLine())

		// TODO:创建课并添加到课程中
		_ = lessonID
		_ = lessonTitle
		_ = lessonDuration
	}

	// 读取学生信息
	studentID := readLine()
	studentName := readLine()
	studentEmail := readLine()

	// TODO:使用 NewStudent 创建学生
	_ = studentID
	_ = studentName
	_ = studentEmail

	// TODO:创建关联学生与课程的选课记录

	// 读取操作数量
	numOps, _ := strconv.Atoi(readLine())

	// 处理每个操作
	for i := 0; i < numOps; i++ {
		operation := readLine()

		if operation == "progress" {
			// TODO:调用 Progress(),并以 "%.1f%%" 格式输出
			fmt.Println("0.0%") // 替换为实际进度
		} else if strings.HasPrefix(operation, "complete ") {
			lessonID := strings.TrimPrefix(operation, "complete ")
			// TODO: 调用 CompleteLesson 并打印结果
			_ = lessonID
			fmt.Println("") // 替换为实际结果
		}
	}
}

面向对象编程中的所有课

自己练习: Go 在线编译器