Hệ thống mượn sách
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 100 trên 107.
Thử thách
DễHãy xây dựng hệ thống mượn sách cho dự án quản lý thư viện của chúng ta! Bạn sẽ tạo một struct Library để quản lý các bộ sưu tập sách và người dùng, với các phương thức xử lý việc mượn và trả sách, đồng thời kiểm tra tính hợp lệ.
Bạn sẽ sắp xếp code trong bốn tệp:
book.go: structBooktừ bài học trước, gồm các trườngID,Title,Author,ISBNvàAvailable. Thêm hàm khởi tạoNewBookđể khởi tạo sách ở trạng thái có sẵn.user.go: structUsergồmID,Name,EmailvàBorrowedBooks(slice chứa ID sách). Thêm hàm khởi tạoNewUserđể khởi tạo người dùng với danh sách mượn trống.library.go: Tạo structLibraryđể lưu sách và người dùng trong các map (được lập chỉ mục bằng ID của chúng). Xây dựng các thành phần sau:- Hàm khởi tạo
NewLibrarytạo các map trống cho sách và người dùng - Phương thức
AddBookthêm sách vào thư viện - Phương thức
AddUserthêm người dùng vào thư viện - Phương thức
Borrownhận ID người dùng và ID sách, kiểm tra tính hợp lệ của thao tác, đánh dấu sách là không có sẵn, thêm ID sách vào danh sách sách đã mượn của người dùng và trả về lỗi nếu có sự cố - Phương thức
Returnđảo ngược quy trình mượn: đánh dấu sách là có sẵn và xóa sách khỏi danh sách đã mượn của người dùng
Phương thức
Borrowcần trả về các lỗi sau:user not foundnếu ID người dùng không tồn tạibook not foundnếu ID sách không tồn tạibook not availablenếu sách đã được mượn
Phương thức
Returncần trả về các lỗi sau:user not foundnếu ID người dùng không tồn tạibook not foundnếu ID sách không tồn tạibook not borrowednếu sách đã có sẵn
- Hàm khởi tạo
main.go: Kết hợp mọi thứ bằng cách tạo thư viện, thêm sách và người dùng, sau đó xử lý các thao tác mượn và trả sách.Đọc số lượng sách, sau đó đọc ID, tiêu đề, tác giả và ISBN của từng sách. Tiếp theo, đọc số lượng người dùng, rồi đọc ID, tên và email của từng người dùng. Sau đó, đọc số lượng thao tác, mỗi thao tác là
borrow [userID] [bookID]hoặcreturn [userID] [bookID].Với mỗi thao tác, in ra
OKnếu thành công hoặcError: [message]nếu thất bại.
Đầu vào sẽ gồm:
- Số lượng sách, sau đó là ID, tiêu đề, tác giả và ISBN của từng sách (mỗi thông tin trên một dòng riêng)
- Số lượng người dùng, sau đó là ID, tên và email của từng người dùng (mỗi thông tin trên một dòng riêng)
- Số lượng thao tác, sau đó là từng thao tác trên một dòng riêng
Ví dụ, với đầu vào:
2
B001
The Go Programming Language
Alan Donovan
978-0134190440
B002
Clean Code
Robert Martin
978-0132350884
1
U001
Alice Smith
alice@library.com
4
borrow U001 B001
borrow U001 B001
return U001 B001
borrow U002 B001Đầu ra của bạn sẽ là:
OK
Error: book not available
OK
Error: user not foundVà với đầu vào:
1
B100
Design Patterns
Gang of Four
978-0201633610
2
U001
Bob Jones
bob@email.com
U002
Carol White
carol@email.com
3
borrow U001 B100
borrow U002 B100
return U001 B100Đầu ra của bạn sẽ là:
OK
Error: book not available
OKVà với đầu vào:
1
B001
Go in Action
William Kennedy
978-1617291784
1
U001
Dave Miller
dave@test.com
2
return U001 B001
borrow U001 B999Đầu ra của bạn sẽ là:
Error: book not borrowed
Error: book not foundLưu ý Library đóng vai trò điều phối trung tâm. Thư viện kiểm tra tính hợp lệ của mọi thao tác trước khi thay đổi trạng thái, đảm bảo sách và người dùng luôn đồng bộ. Dùng các phương thức nhận con trỏ để phương thức có thể sửa đổi dữ liệu thực tế được lưu trong các map!
Tự làm thử
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Đọc số lượng sách
var numBooks int
fmt.Fscanln(reader, &numBooks)
// TODO: Tạo thư viện mới
// TODO: Đọc thông tin từng cuốn sách (ID, Title, Author, ISBN trên các dòng riêng)
// và thêm chúng vào thư viện
for i := 0; i < numBooks; i++ {
id, _ := reader.ReadString('\n')
id = strings.TrimSpace(id)
title, _ := reader.ReadString('\n')
title = strings.TrimSpace(title)
author, _ := reader.ReadString('\n')
author = strings.TrimSpace(author)
isbn, _ := reader.ReadString('\n')
isbn = strings.TrimSpace(isbn)
// TODO: Tạo sách và thêm vào thư viện
}
// Đọc số lượng người dùng
var numUsers int
fmt.Fscanln(reader, &numUsers)
// TODO: Đọc thông tin từng người dùng (ID, Name, Email trên các dòng riêng)
// và thêm chúng vào thư viện
for i := 0; i < numUsers; i++ {
id, _ := reader.ReadString('\n')
id = strings.TrimSpace(id)
name, _ := reader.ReadString('\n')
name = strings.TrimSpace(name)
email, _ := reader.ReadString('\n')
email = strings.TrimSpace(email)
// TODO: Tạo người dùng và thêm vào thư viện
}
// Đọc số lượng thao tác
var numOps int
fmt.Fscanln(reader, &numOps)
// TODO: Xử lý từng thao tác
// Định dạng: "borrow [userID] [bookID]" hoặc "return [userID] [bookID]"
for i := 0; i < numOps; i++ {
line, _ := reader.ReadString('\n')
line = strings.TrimSpace(line)
parts := strings.Split(line, " ")
// TODO: Phân tích thao tác và gọi phương thức phù hợp
// In ra "OK" nếu thành công hoặc "Error: [message]" nếu thất bại
}
}
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 pool12Các khái niệm OOP nâng cao
Mẫu tùy chọn hàmMẫu Builder trong GoChuỗi phương thứcBí danh kiểu dữ liệu và định nghĩa kiểuCơ bản về reflectionTổng quan về tạo code15Dự án: Quản lý thư viện
Tổng quan và cấu trúc dự ánStruct về sách và người dùngTự luyện tập: Trình biên dịch Go online