Menu
CoddyTech

借阅系统

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

challenge icon

挑战

简单

让我们为图书管理项目构建借阅系统吧!你将创建一个 Library 结构体,用于管理图书和用户集合,并通过方法处理借出和归还操作,同时进行适当的验证。

你需要将代码组织到四个文件中:

  • book.go:使用上一课中的 Book 结构体,其中包含 ID、Title、Author、ISBN 和 Available 字段。添加 NewBook 构造函数,将新图书初始化为可借阅状态。
  • user.go:使用包含 ID、Name、Email 和 BorrowedBooks(图书 ID 的切片)的 User 结构体。添加 NewUser 构造函数,将新用户的已借图书列表初始化为空。
  • library.go:创建一个 Library 结构体,使用映射存储图书和用户(以各自的 ID 为键)。构建以下组件:
    • 创建 NewLibrary 构造函数,将图书和用户的映射初始化为空
    • 创建 AddBook 方法,将图书添加到图书馆
    • 创建 AddUser 方法,将用户添加到图书馆
    • 创建 Borrow 方法,接收用户 ID 和图书 ID,验证操作,将图书标记为不可借阅,将图书 ID 添加到用户的已借图书列表中,并在发生问题时返回错误
    • 创建 Return 方法,撤销借阅操作:将图书标记为可借阅,并从用户的已借图书列表中移除该图书

    如果出现以下情况,Borrow 方法应返回相应错误:

    • 用户 ID 不存在时,返回 user not found
    • 图书 ID 不存在时,返回 book not found
    • 图书已被借出时,返回 book not available

    如果出现以下情况,Return 方法应返回相应错误:

    • 用户 ID 不存在时,返回 user not found
    • 图书 ID 不存在时,返回 book not found
    • 图书已经可以借阅时,返回 book not borrowed
  • main.go:将所有内容整合起来,创建图书馆,添加图书和用户,然后处理借出和归还操作。

    读取图书数量,然后依次读取每本图书的 ID、标题、作者和 ISBN。接着读取用户数量,然后依次读取每位用户的 ID、姓名和电子邮箱。最后读取操作数量,每项操作的格式为 borrow [userID] [bookID] 或 return [userID] [bookID]。

    每项操作成功时,输出 OK;失败时,输出 Error: [message]。

将提供以下输入:

  • 图书数量,然后是每本图书的 ID、标题、作者和 ISBN(每项各占一行)
  • 用户数量,然后是每位用户的 ID、姓名和电子邮箱(每项各占一行)
  • 操作数量,然后是每项操作(每项各占一行)

例如,给定以下输入:

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

输出应为:

OK
Error: book not available
OK
Error: user not found

再给定以下输入:

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

输出应为:

OK
Error: book not available
OK

再给定以下输入:

1
B001
Go in Action
William Kennedy
978-1617291784
1
U001
Dave Miller
dave@test.com
2
return U001 B001
borrow U001 B999

输出应为:

Error: book not borrowed
Error: book not found

注意,Library 充当中央协调器。它会在修改状态前验证所有操作,确保图书和用户的信息保持同步。使用指针接收者,可以让方法修改映射中实际存储的数据!

亲手试试

package main

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

func main() {
	reader := bufio.NewReader(os.Stdin)

	// 读取图书数量
	var numBooks int
	fmt.Fscanln(reader, &numBooks)

	// TODO: 创建一个新库

	// TODO: 读取每本书的详细信息(ID、Title、Author、ISBN 各占一行)
	// 并将它们添加到库中
	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: 创建图书并添加到库中
	}

	// 读取用户数量
	var numUsers int
	fmt.Fscanln(reader, &numUsers)

	// TODO: 读取每位用户的详细信息(ID、Name、Email 各占一行)
	// 并将它们添加到库中
	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: 创建用户并添加到库中
	}

	// 读取操作数量
	var numOps int
	fmt.Fscanln(reader, &numOps)

	// TODO: 处理每项操作
	// 格式:"borrow [userID] [bookID]" 或 "return [userID] [bookID]"
	for i := 0; i < numOps; i++ {
		line, _ := reader.ReadString('\n')
		line = strings.TrimSpace(line)
		parts := strings.Split(line, " ")

		// TODO: 解析操作并调用相应的方法
		// 成功时输出 "OK",失败时输出 "Error: [message]"
	}
}

面向对象编程中的所有课

自己练习: Go 在线编译器