feat(面向对象): 完成第三阶段差异化课程

This commit is contained in:
zhiye.sun
2026-08-11 17:53:53 +08:00
parent b348dc0a1f
commit f1d9548646
16 changed files with 2131 additions and 14 deletions

View File

@@ -0,0 +1,177 @@
# 第 3-5 课综合项目:内存版图书管理系统
#
# 本项目只使用内存数据,不读写文件、不连接数据库。
# 请按照题目顺序实现,不要删除业务规则、测试流程、预期输出和验收说明。
# 第一部分:定义业务异常
# 1. 定义 LibraryError(Exception),类体中只写 pass。
# 2. 定义 BookNotFoundError(LibraryError),类体中只写 pass。
# 3. 定义 BookUnavailableError(LibraryError),类体中只写 pass。
# 4. 定义 MemberNotFoundError(LibraryError),类体中只写 pass。
#
# Python 与 Java 对照:
# - 自定义异常同样通过继承建立分类;
# - Python 没有 Java 受检异常Checked Exception的强制 throws 声明;
# - 调用者可以捕获具体异常,也可以统一捕获 LibraryError。
# 第二部分:定义 Book 数据类
# 1. 使用 @dataclass。
# 2. 定义字段:
# isbn: str
# title: str
# author: str
# available: bool = True
# 3. 定义 __str__返回格式
# “《Python 编程》|作者:小明|状态:可借”。
# 4. available 为 False 时,状态文字应为“已借出”。
#
# 设计提醒:
# - dataclass 自动生成初始化、调试显示和字段相等比较;
# - __str__ 只负责一个 Book 自身的友好显示。
# 第三部分:定义 Member 数据类
# 1. 使用 @dataclass。
# 2. 定义字段:
# member_id: str
# name: str
# borrowed_isbns: list[str] = field(default_factory=list)
# 3. 定义 borrowed_count 只读 property返回已借 ISBN 的数量。
#
# 设计提醒:
# - 使用 default_factory避免不同会员共享同一借阅列表
# - Member 只保存自己的借阅结果,不负责查找图书馆中的书。
# 第四部分:定义 ConsoleNotifier
# 1. 不需要继承任何通知父类或接口。
# 2. 定义 send(self, message) 方法,直接输出:
# “通知:{message}”。
#
# 鸭子类型提醒:
# - Library 只要求通知器具有 send() 方法;
# - 不要在 Library 中使用 isinstance() 判断通知器类型。
# 第五部分:定义 Library 类和初始化方法
# 1. 定义 __init__(self, name) 方法并保存 self.name。
# 2. 创建实例属性:
# self.books: dict[str, Book] = {}
# self.members: dict[str, Member] = {}
# 3. books 使用 ISBN 作为键members 使用会员编号作为键。
#
# 组合提醒:
# - Library “拥有”多个 Book 和 Member因此这里使用组合而不是继承
# - 不要让 Library 继承 list 或 dict。
# 第六部分:实现 add_book(self, book)
# 1. book.isbn 已存在时抛出 LibraryError("ISBN 已存在。")。
# 2. 不存在时把 book 保存到 self.books。
# 3. 本方法不需要输出。
# 第七部分:实现 register_member(self, member)
# 1. member.member_id 已存在时抛出 LibraryError("会员编号已存在。")。
# 2. 不存在时把 member 保存到 self.members。
# 3. 本方法不需要输出。
# 第八部分:实现两个内部查找方法
# 1. 定义 _get_book(self, isbn),找到时返回 Book。
# 2. 找不到时抛出 BookNotFoundError("图书不存在。")。
# 3. 定义 _get_member(self, member_id),找到时返回 Member。
# 4. 找不到时抛出 MemberNotFoundError("会员不存在。")。
#
# 单下划线提醒:
# - _get_book 和 _get_member 表示 Library 内部使用的辅助方法;
# - 单下划线是约定,不是 Java private 式的强制限制。
# 第九部分:实现 borrow_book(self, member_id, isbn, notifier)
# 1. 通过 _get_member() 查找会员。
# 2. 通过 _get_book() 查找图书。
# 3. book.available 为 False 时抛出:
# BookUnavailableError("图书已被借出。")。
# 4. 校验通过后执行:
# book.available = False
# member.borrowed_isbns.append(isbn)
# 5. 调用 notifier.send(),消息格式:
# “小明成功借阅《Python 编程》”。
#
# 多态提醒:
# - notifier 的类型注解可以省略,不影响验收;
# - 只调用 send(),不要判断通知器的具体类型。
# 第十部分:实现 return_book(self, member_id, isbn, notifier)
# 1. 通过两个内部查找方法取得会员和图书。
# 2. isbn 不在 member.borrowed_isbns 中时抛出:
# LibraryError("该会员没有借阅这本书。")。
# 3. 校验通过后执行:
# member.borrowed_isbns.remove(isbn)
# book.available = True
# 4. 调用 notifier.send(),消息格式:
# “小明成功归还《Python 编程》”。
# 第十一部分:实现 available_books(self)
# 1. 返回所有 available 为 True 的 Book 对象组成的新列表。
# 2. 不要修改 self.books。
# 3. 可以使用列表推导式。
# 第十二部分:定义 main() 并完成正常流程
# 1. 创建 Library("城市图书馆")。
# 2. 创建 ConsoleNotifier()。
# 3. 添加以下两本书:
# Book("978-001", "Python 编程", "小明")
# Book("978-002", "流畅的 Python", "Luciano")
# 4. 注册 Member("M001", "小红")。
# 5. 调用 library.available_books() 取得当前可借图书列表。
# 6. 使用 len() 统计列表长度并输出“初始可借数量2”参考写法
# print(f"初始可借数量:{len(library.available_books())}")
# 7. 借出 ISBN 为 978-001 的书。
# 8. 再次调用 available_books(),使用 len() 输出“借阅后可借数量1”。
# 9. 读取前面创建的 Member 对象的 borrowed_count输出“会员已借数量1”。
# 10. 归还 ISBN 为 978-001 的书。
# 11. 再次调用 available_books(),使用 len() 输出“归还后可借数量2”。
# 12. 再次读取该 Member 对象的 borrowed_count输出“会员已借数量0”。
# 第十三部分:在 main() 中完成异常流程
# 1. 再次借出 978-001。
# 2. 使用 try...except 再借一次同一本书。
# 3. 捕获 LibraryError并输出
# “重复借阅已被阻止:图书已被借出。”。
# 4. 使用 try...except 借阅 ISBN 为 978-999 的书。
# 5. 捕获 LibraryError并输出
# “不存在的图书已被阻止:图书不存在。”。
# 6. 添加程序入口判断并调用 main()。
#
# 完整预期输出:
# 初始可借数量2
# 通知小红成功借阅《Python 编程》
# 借阅后可借数量1
# 会员已借数量1
# 通知小红成功归还《Python 编程》
# 归还后可借数量2
# 会员已借数量0
# 通知小红成功借阅《Python 编程》
# 重复借阅已被阻止:图书已被借出。
# 不存在的图书已被阻止:图书不存在。
# 最终验收标准:
# 1. Book 和 Member 使用 dataclass且不同会员不共享借阅列表
# 2. Book 的 __str__ 能区分“可借”和“已借出”;
# 3. Member.borrowed_count 能随借还操作变化;
# 4. Library 通过组合管理 Book 和 Member
# 5. 借书和还书同时更新图书状态与会员借阅记录;
# 6. 通知器通过鸭子类型调用,没有具体类型判断;
# 7. 业务异常继承关系正确,并能统一捕获 LibraryError
# 8. 重复 ISBN、重复会员、图书不存在、会员不存在、重复借阅和错误归还均被阻止
# 9. available_books() 返回新列表,不修改图书字典;
# 10. 程序实际输出的业务结果正确;说明文字或非必要类型注解不阻塞验收。