Nền tảng học trực tuyến
Thuộc phần Lập trình hướng đối tượng trong hành trình GO của Coddy. Bài học 105 trên 107.
Thử thách
DễHãy cùng xây dựng một nền tảng học trực tuyến để quản lý các khóa học, học viên và theo dõi tiến độ học tập! Bạn sẽ kết hợp các struct, interface, composition và polymorphism để tạo nên một hệ thống thống nhất, nơi học viên có thể đăng ký khóa học và hoàn thành bài học.
Bạn sẽ sắp xếp code trong năm tệp:
lesson.go: Định nghĩa structLessonđể biểu diễn từng đơn vị học tập. Mỗi bài học cóID,TitlevàDuration(tính bằng phút). Thêm hàm khởi tạoNewLessontrả về một con trỏ đến Lesson.course.go: Tạo structCoursegồmID,Title,Instructorvà một slice chứa các con trỏ*Lesson. Thêm hàm khởi tạoNewCoursevà phương thứcAddLesson. Đồng thời triển khai phương thứcTotalDuration() inttrả về tổng thời lượng của tất cả bài học trong khóa học.student.go: Định nghĩa structStudentvới các trườngID,NamevàEmail. Thêm hàm khởi tạoNewStudent.enrollment.go: Đây là nơi phần theo dõi tiến độ phát huy tác dụng. Tạo structEnrollmentliên kết một học viên với một khóa học và theo dõi những bài học họ đã hoàn thành. Struct này cần có:- Một con trỏ đến
Student - Một con trỏ đến
Course - Một map
CompletedLessons(ID bài học ánh xạ đến giá trị boolean)
Định nghĩa interface
Progressablevới phương thứcProgress() float64trả về tỷ lệ phần trăm (từ 0.0 đến 100.0).Triển khai các phương thức sau cho Enrollment:
NewEnrollment(student *Student, course *Course) *Enrollment: hàm khởi tạo tạo một map bài học đã hoàn thành rỗngCompleteLesson(lessonID string) string: đánh dấu một bài học là đã hoàn thành. Trả vềlesson not foundnếu bài học không tồn tại trong khóa học,already completednếu bài học đã hoàn thành, hoặccompletednếu thành côngProgress() float64: trả về tỷ lệ phần trăm bài học đã hoàn thành (số bài đã hoàn thành/tổng số bài * 100), hoặc 0.0 nếu khóa học không có bài học
- Một con trỏ đến
main.go: Xây dựng nền tảng và xử lý các thao tác đăng ký.Đọc ID, tiêu đề và giảng viên của khóa học. Sau đó đọc số lượng bài học cùng ID, tiêu đề và thời lượng của từng bài. Đọc ID, tên và email của học viên. Tạo một lượt đăng ký liên kết học viên với khóa học.
Sau đó đọc số lượng thao tác. Mỗi thao tác là
complete [lessonID]hoặcprogress. Với thao tác hoàn thành, hãy in thông báo kết quả. Với thao tác xem tiến độ, hãy in tiến độ với một chữ số thập phân, theo sau là dấu phần trăm.
Đầu vào sẽ có dạng như sau:
- ID khóa học, tiêu đề, giảng viên (3 dòng)
- Số lượng bài học, sau đó là ID, tiêu đề, thời lượng của từng bài học (mỗi bài 3 dòng)
- ID, tên, email học viên (3 dòng)
- Số lượng thao tác, sau đó là từng thao tác (mỗi thao tác 1 dòng)
Ví dụ, với đầu vào:
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 L002Kết quả đầu ra sẽ là:
0.0%
completed
33.3%
already completed
completedVà với đầu vào:
C100
Python Basics
Bob Teacher
2
L100
Intro
20
L101
Loops
40
S100
Charlie
charlie@edu.com
4
complete L999
complete L100
complete L101
progressKết quả đầu ra sẽ là:
lesson not found
completed
completed
100.0%Lưu ý cách struct Enrollment dùng composition để liên kết học viên với khóa học, còn interface Progressable cho phép mọi kiểu báo cáo trạng thái hoàn thành. Thiết kế này giúp bạn dễ dàng mở rộng. Bạn có thể thêm tính năng theo dõi tiến độ cho từng bài học hoặc toàn bộ lộ trình học bằng cùng interface!
Tự làm thử
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Hàm (function) hỗ trợ đọc một dòng
readLine := func() string {
scanner.Scan()
return strings.TrimSpace(scanner.Text())
}
// Đọc thông tin khóa học
courseID := readLine()
courseTitle := readLine()
courseInstructor := readLine()
// TODO: Tạo khóa học bằng NewCourse
_ = courseID
_ = courseTitle
_ = courseInstructor
// Đọc số lượng bài học
numLessons, _ := strconv.Atoi(readLine())
// TODO: Đọc từng bài học và thêm vào khóa học
for i := 0; i < numLessons; i++ {
lessonID := readLine()
lessonTitle := readLine()
lessonDuration, _ := strconv.Atoi(readLine())
// TODO: Tạo bài học và thêm vào khóa học
_ = lessonID
_ = lessonTitle
_ = lessonDuration
}
// Đọc thông tin học viên
studentID := readLine()
studentName := readLine()
studentEmail := readLine()
// TODO: Tạo học viên bằng NewStudent
_ = studentID
_ = studentName
_ = studentEmail
// TODO: Tạo bản ghi đăng ký liên kết học viên với khóa học
// Đọc số lượng thao tác
numOps, _ := strconv.Atoi(readLine())
// Xử lý từng thao tác
for i := 0; i < numOps; i++ {
operation := readLine()
if operation == "progress" {
// TODO: Gọi Progress() và in ra theo định dạng "%.1f%%"
fmt.Println("0.0%") // Thay bằng tiến độ thực tế
} else if strings.HasPrefix(operation, "complete ") {
lessonID := strings.TrimPrefix(operation, "complete ")
// TODO: Gọi CompleteLesson và in ra kết quả
_ = lessonID
fmt.Println("") // Thay bằng kết quả thực tế
}
}
}
Tất cả bài học trong Lập trình hướng đối tượng
1Nền tảng OOP trong Go
Tệp bên ngoàiWorkspace và module trong GoPackage và importTên được export và không được exportGiới thiệu về OOP trong GoStruct như lớpĐịnh nghĩa phương thức cho structReceiver kiểu con trỏ và kiểu giá trịKhởi tạo structHàm khởi tạoÔn tập: máy tính đơn giản4Interface
Giới thiệu về interfaceTriển khai ngầm địnhInterface như một hợp đồngInterface rỗng (any)Khẳng định kiểuswitch kiểuKết hợp interfaceInterface Stringer và ErrorÔn tập: Máy tính hình học7Tính đóng gói
Trường được xuất và không được xuấtĐóng gói ở cấp packagePhương thức getter và setterẨn thông tin trong GoÔn tập: hồ sơ học sinh10Generics (Go 1.18 trở lên)
Giới thiệu về GenericsTham số kiểuRàng buộc kiểuStruct tổng quátGiải pháp thay thế cho phương thức tổng quátÔn tập: tập hợp tổng quát13Các mẫu thiết kế phần 1
Giới thiệu về mẫu thiết kếMẫu SingletonMẫu FactoryMẫu Abstract FactoryMẫu ObserverMẫu Strategy2Tìm hiểu chuyên sâu về kiểu dữ liệu và struct
Kiểu cơ bản và kiểu tổng hợpĐịnh nghĩa kiểu dữ liệu tùy chỉnhThẻ structStruct ẩn danhStruct lồng nhauGiá trị zero và giá trị mặc địnhÔn tập: sổ liên lạc5Ưu tiên kết hợp hơn kế thừa
Vì sao Go không có kế thừaNhập môn nhúng structTự động nâng phương thứcNhúng nhiều structNhúng và kết tậpChe khuất phương thức được nhúngÔn tập: hệ phân cấp nhân viên8Xử lý lỗi và lập trình hướng đối tượng
Giao diện errorCác kiểu lỗi tùy chỉnhBọc lỗi với fmt.ErrorfLỗi sentinelerrors.Is() và errors.As()Panic, defer và recoverÔn tập: trình phân tích tệp3Con trỏ và bộ nhớ
Cơ bản về con trỏ trong GoCon trỏ đến structTruyền theo giá trị và tham chiếuHàm new()Thu gom rác trong GoÔn tập: xây dựng danh sách liên kết6Đa hình trong Go
Đa hình qua interfaceĐịnh kiểu vịt trong GoQuy tắc thỏa mãn interfaceCác tập hợp đa hìnhTiêm phụ thuộcÔn tập: bộ xử lý thanh toán9Lập trình đồng thời và lập trình hướng đối tượng
Cơ bản về goroutineChannel và giao tiếpChannel có bộ đệm và không có bộ đệmCâu lệnh selectsync.Mutex và sync.RWMutexsync.WaitGroupThiết kế struct an toàn luồngÔn tập: worker poolTự luyện tập: Trình biên dịch Go online