面向对象综合案例
# 面向对象实战案例
面向对象编程 (OOP) 不仅仅是语法,更是一种强大的编程思想。通过将现实世界中的概念抽象为代码中的“对象”,我们可以构建出结构清晰、易于维护和扩展的复杂系统。
# 1. 案例一:银行账户系统——封装与数据验证
这个案例将重点展示如何通过 封装 保护数据安全,并利用 @property
装饰器实现优雅的数据验证。
# 1.1 需求分析
- 创建一个
BankAccount
类,管理用户的银行账户。 - 属性: 账户名 (
owner
)、余额 (balance
)、账户类型 (account_type
)。 - 行为:
- 开户: 创建账户时,必须提供户主名和初始余额。
- 存款 (
deposit
): 存入金额必须为正数。 - 取款 (
withdraw
): 取款金额必须为正数,且不能超过当前余额。 - 查询信息 (
display_info
): 显示账户的详细信息。
- 约束: 余额不能被外部直接修改,必须通过存款和取款方法进行。
# 1.2 设计思路
- 封装余额: 将余额属性
_balance
设为内部属性(以_
开头),防止外部直接访问。 @property
装饰器: 创建一个名为balance
的只读属性,外部只能通过它来安全地“读取”_balance
的值。- 方法设计:
__init__
: 初始化所有属性,并对初始余额进行验证。deposit
和withdraw
: 实现核心业务逻辑,并在方法内部修改_balance
。display_info
: 格式化输出账户信息。
# 1.3 代码实现与讲解
class BankAccount:
"""
一个代表银行账户的类。
这个类通过封装和属性验证来确保账户操作的安全性。
"""
def __init__(self, owner, initial_balance=0.0):
# 公开属性:账户所有者姓名
self.owner = owner
# 内部属性:账户余额,以单下划线开头表示不建议外部直接访问
# 我们将通过 getter 和 setter 来控制它
if initial_balance < 0:
raise ValueError("初始余额不能为负数!")
self._balance = float(initial_balance)
@property
def balance(self):
"""
余额的 getter。
使用 @property 装饰器,让我们可以像访问属性一样调用这个方法(例如 account.balance)。
这是一个只读属性,外部无法通过 account.balance = XXX 来修改。
"""
return self._balance
def deposit(self, amount):
"""
存款方法。
"""
# 验证存款金额
if amount > 0:
self._balance += float(amount)
print(f"✅ 存款成功:{amount:.2f} 元。")
self.display_info()
else:
print("❌ 存款失败:金额必须为正数。")
def withdraw(self, amount):
"""
取款方法。
"""
# 验证取款金额
if amount <= 0:
print("❌ 取款失败:金额必须为正数。")
# 验证余额是否充足
elif amount > self._balance:
print(f"❌ 取款失败:余额不足。当前余额 {self.balance:.2f} 元,尝试取款 {amount:.2f} 元。")
else:
self._balance -= float(amount)
print(f"✅ 取款成功:{amount:.2f} 元。")
self.display_info()
def display_info(self):
"""
显示当前账户信息。
"""
print(f"--- 账户信息 ---\n户主: {self.owner}\n余额: {self.balance:.2f} 元\n---------------")
# --- 代码测试 ---
print("--- 创建账户 ---")
my_account = BankAccount("张三", 1000.0)
my_account.display_info()
print("\n--- 存取款操作 ---")
my_account.deposit(500.50)
my_account.withdraw(200)
my_account.withdraw(1500) # 尝试超额取款
my_account.deposit(-100) # 尝试存入负数
# 尝试直接修改余额(会失败,因为 balance 是只读属性)
try:
my_account.balance = 5000
except AttributeError as e:
print(f"\n❌ 直接修改余额失败: {e}")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# 2. 案例二:图书馆系统——类之间的协作
这个案例将重点展示如何设计多个类 (Book
和 Library
),并让它们相互协作,共同完成一个更复杂的任务。
# 2.1 需求分析
Book
类: 存储单本书籍的信息,包括书名 (title
)、作者 (author
) 和 ISBN 编号 (isbn
)。Library
类: 代表整个图书馆,需要管理多本书籍。- 功能:
- 入库 (
add_book
): 将一本新书(一个Book
对象)添加到图书馆。 - 借阅 (
borrow_book
): 根据 ISBN 借出一本书,并记录其为“已借出”状态。 - 归还 (
return_book
): 根据 ISBN 归还一本书,并记录其为“可用”状态。 - 查询 (
find_book
): 根据 ISBN 查找书籍信息。 - 展示 (
display_all_books
): 显示图书馆所有藏书及其状态。
- 入库 (
# 2.2 设计思路
Book
类: 一个简单的数据类。为其添加__str__
方法可以方便地打印书籍信息。Library
类:- 使用一个字典
_books
来存储所有Book
对象,键为 ISBN,值为Book
对象。这使得通过 ISBN 查找书籍非常高效。 - 使用一个集合
_borrowed_isbns
来专门记录已借出书籍的 ISBN。集合的查找、添加和删除操作效率很高。
- 使用一个字典
- 协作:
Library
类的方法会接收、存储和操作Book
类的实例,体现了类之间的协作关系。
# 2.3 代码实现与讲解
class Book:
"""代表一本书的数据类。"""
def __init__(self, title, author, isbn):
self.title = title
self.author = author
self.isbn = isbn # ISBN 作为书籍的唯一标识
def __str__(self):
"""
定义对象的字符串表示形式。
当我们 print(一个Book对象) 时,会自动调用这个方法。
"""
return f"《{self.title}》 by {self.author} (ISBN: {self.isbn})"
class Library:
"""代表图书馆的类,管理书籍的借阅。"""
def __init__(self, name):
self.name = name
# 内部属性:使用字典存储所有书籍,ISBN为键,Book对象为值
self._books = {}
# 内部属性:使用集合存储已借出书籍的ISBN,查询效率高
self._borrowed_isbns = set()
def add_book(self, book):
"""将一本书(Book对象)添加入库。"""
if isinstance(book, Book):
if book.isbn not in self._books:
self._books[book.isbn] = book
print(f"✅ 入库成功: {book}")
else:
print(f"🟡 信息: ISBN {book.isbn} 已存在,无需重复入库。")
else:
print("❌ 错误: 只能添加入库 Book 类型的对象。")
def borrow_book(self, isbn):
"""根据 ISBN 借书。"""
# 检查书籍是否存在
if isbn not in self._books:
print(f"❌ 借阅失败: ISBN {isbn} 不存在。")
# 检查书籍是否已被借出
elif isbn in self._borrowed_isbns:
print(f"🟡 借阅失败: 《{self._books[isbn].title}》已被借出。")
# 执行借阅
else:
self._borrowed_isbns.add(isbn)
print(f"✅ 借阅成功: 《{self._books[isbn].title}》。")
def return_book(self, isbn):
"""根据 ISBN 还书。"""
if isbn not in self._books:
print(f"❌ 归还失败: ISBN {isbn} 不属于本馆。")
elif isbn in self._borrowed_isbns:
self._borrowed_isbns.remove(isbn)
print(f"✅ 归还成功: 《{self._books[isbn].title}》。")
else:
print(f"🟡 信息: 《{self._books[isbn].title}》未被借出,无需归还。")
def display_all_books(self):
"""显示所有馆藏书籍及其状态。"""
print(f"\n--- {self.name} 藏书清单 ---")
if not self._books:
print("图书馆暂无藏书。")
return
for isbn, book in self._books.items():
status = "已借出" if isbn in self._borrowed_isbns else "在馆"
print(f"- {book} [状态: {status}]")
print("------------------------")
# --- 代码测试 ---
# 创建书籍对象
book1 = Book("Python编程:从入门到实践", "Eric Matthes", "978-7-115-42802-8")
book2 = Book("流畅的Python", "Luciano Ramalho", "978-7-115-45417-1")
book3 = Book("算法导论", "Thomas H. Cormen", "978-7-111-40701-0")
# 创建图书馆并入库书籍
my_library = Library("城市图书馆")
my_library.add_book(book1)
my_library.add_book(book2)
my_library.add_book(book3)
my_library.display_all_books()
# 借阅与归还
print("\n--- 借阅与归还操作 ---")
my_library.borrow_book("978-7-115-45417-1")
my_library.borrow_book("978-7-115-45417-1") # 重复借阅
my_library.return_book("978-7-115-45417-1")
my_library.borrow_book("999-9-999-99999-9") # 借阅不存在的书
my_library.display_all_books()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# 3. 案例三:员工管理系统——继承与多态
这个案例将重点展示如何使用 继承 来构建类层次结构,并利用 多态 编写出更具扩展性的通用代码。
# 3.1 需求分析
- 基类
Employee
: 包含所有员工共有的属性,如姓名 (name
) 和月薪 (salary
)。以及一个计算年薪get_annual_salary
的方法。 - 子类
Manager
: 继承Employee
,并有自己独特的属性——绩效奖金 (bonus
)。其年薪计算方式为:月薪 * 12 + 绩效奖金。 - 子类
Developer
: 继承Employee
,并有自己独特的属性——项目分红 (dividend
)。其年薪计算方式为:月薪 * 12 + 项目分红。 - 功能:
- 创建不同类型的员工(经理、开发者)。
- 能够统一计算并打印所有员工的年薪,无论其具体职位是什么。
# 3.2 设计思路
- 继承:
Manager
和Developer
都从Employee
继承。它们自动获得了name
和salary
属性,以及__init__
方法。通过super().__init__()
调用父类的构造方法来初始化通用属性。 - 方法重写:
Manager
和Developer
都需要重写get_annual_salary
方法,以实现各自独特的年薪计算逻辑。 - 多态: 创建一个统一的函数,该函数接收一个员工列表(其中可以包含
Manager
和Developer
对象)。遍历列表,对每个员工对象调用get_annual_salary
方法。Python 会自动根据对象的实际类型(是Manager
还是Developer
)来调用正确的重写方法。这就是多态。
# 3.3 代码实现与讲解
class Employee:
"""员工基类。"""
def __init__(self, name, salary):
self.name = name
self.salary = salary
def get_annual_salary(self):
"""计算基础年薪。"""
return self.salary * 12
def display_info(self):
"""显示员工基本信息。"""
print(f"姓名: {self.name}, 职位: {self.__class__.__name__}, 年薪: {self.get_annual_salary()} 元")
class Manager(Employee):
"""经理子类。"""
def __init__(self, name, salary, bonus):
# 调用父类的 __init__ 方法来初始化 name 和 salary
super().__init__(name, salary)
# 初始化子类特有的属性
self.bonus = bonus
def get_annual_salary(self):
"""
重写父类的方法,以实现新的年薪计算逻辑。
先通过 super() 获取父类的计算结果,再加上自己的奖金。
"""
base_annual_salary = super().get_annual_salary()
return base_annual_salary + self.bonus
class Developer(Employee):
"""开发者子类。"""
def __init__(self, name, salary, dividend):
super().__init__(name, salary)
self.dividend = dividend
def get_annual_salary(self):
"""重写父类的方法。"""
base_annual_salary = super().get_annual_salary()
return base_annual_salary + self.dividend
# --- 多态演示 ---
def print_all_employee_info(employees):
"""
这个函数接收一个包含不同员工对象的列表。
它不关心每个员工的具体类型,只管调用它们的 display_info 方法。
Python 会自动根据对象的实际类型执行相应的方法,这就是多态。
"""
print("\n--- 公司年度薪酬报表 ---")
for employee in employees:
employee.display_info()
print("------------------------")
# --- 代码测试 ---
# 创建不同类型的员工对象
manager1 = Manager("王经理", 20000, 50000)
dev1 = Developer("小张", 15000, 30000)
dev2 = Developer("小李", 16000, 35000)
# 将所有员工放入同一个列表
employee_list = [manager1, dev1, dev2]
# 调用多态函数,统一处理
print_all_employee_info(employee_list)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# 4. 总结
通过以上三个案例,我们实践了面向对象的三大核心支柱:
- 封装 (案例一): 我们将
_balance
隐藏起来,只通过deposit
和withdraw
方法来修改它,保证了数据的安全性和一致性。 - 协作 (案例二): 我们设计了
Book
和Library
两个独立的类,它们各司其职,并通过对象间的交互共同实现了一个完整的图书馆系统。 - 继承与多态 (案例三): 我们通过
Employee
基类实现了代码的重用,并通过Manager
和Developer
子类扩展了功能。最后,利用多态性,我们编写了可以统一处理不同类型对象的通用代码,大大增强了程序的灵活性和可扩展性。
编辑此页 (opens new window)
上次更新: 2025/07/23, 06:33:16