本文目录导读:

我来为你详细讲解面向对象封装的完整案例,从基础到进阶。
基础封装案例:银行账户
简单属性封装
class BankAccount:
"""银行账户类 - 基础封装"""
def __init__(self, account_number, owner, balance=0):
self._account_number = account_number # 受保护属性
self._owner = owner # 受保护属性
self._balance = balance # 私有属性(用_表示)
self._transactions = [] # 交易记录
# 获取余额(只读属性)
def get_balance(self):
"""获取当前余额"""
return self._balance
# 存款
def deposit(self, amount):
"""存款操作"""
if amount <= 0:
raise ValueError("存款金额必须大于0")
self._balance += amount
self._transactions.append(f"存款: +{amount}元")
return self._balance
# 取款
def withdraw(self, amount):
"""取款操作"""
if amount <= 0:
raise ValueError("取款金额必须大于0")
if amount > self._balance:
raise ValueError("余额不足")
self._balance -= amount
self._transactions.append(f"取款: -{amount}元")
return self._balance
# 查看交易记录
def get_transactions(self):
"""获取交易记录副本,防止外部修改"""
return self._transactions.copy()
# 账户信息
def __str__(self):
return f"账户: {self._account_number}, 户主: {self._owner}, 余额: {self._balance}元"
使用@property装饰器
class BankAccountV2:
"""使用@property装饰器的银行账户"""
def __init__(self, account_number, owner, balance=0):
self._account_number = account_number
self._owner = owner
self._balance = balance
self._transactions = []
# 只读属性
@property
def account_number(self):
"""账户号只读"""
return self._account_number
@property
def owner(self):
"""户主只读"""
return self._owner
@property
def balance(self):
"""余额只读"""
return self._balance
@property
def transactions(self):
"""交易记录返回副本"""
return self._transactions.copy()
# 存款(修改方法)
def deposit(self, amount):
"""存款"""
self._validate_amount(amount)
self._balance += amount
self._transactions.append(("deposit", amount))
def withdraw(self, amount):
"""取款"""
self._validate_amount(amount)
if amount > self._balance:
raise ValueError("余额不足")
self._balance -= amount
self._transactions.append(("withdraw", amount))
# 私有辅助方法
def _validate_amount(self, amount):
"""验证金额合法性"""
if not isinstance(amount, (int, float)):
raise TypeError("金额必须是数字")
if amount <= 0:
raise ValueError("金额必须大于0")
def __str__(self):
return f"账户: {self.account_number}, 户主: {self.owner}, 余额: {self.balance}元"
中级封装案例:员工管理系统
from datetime import datetime
import re
class Employee:
"""员工类 - 中级封装"""
def __init__(self, emp_id, name, department, salary):
self._id = emp_id # 员工ID
self._name = name # 姓名
self._department = department # 部门
self._salary = salary # 工资
self._email = None # 邮箱
self._hire_date = datetime.now() # 入职日期
self._performance_scores = [] # 绩效评分
# 姓名(可读,不可直接修改)
@property
def name(self):
return self._name
# 部门(可读可修改)
@property
def department(self):
return self._department
@department.setter
def department(self, value):
"""设置部门,带验证"""
if not value or not value.strip():
raise ValueError("部门不能为空")
self._department = value.strip()
# 工资(提供get和set)
@property
def salary(self):
return self._salary
@salary.setter
def salary(self, value):
"""设置工资,带验证"""
if not isinstance(value, (int, float)):
raise TypeError("工资必须是数字")
if value < 0:
raise ValueError("工资不能为负")
self._salary = value
# 邮箱(带格式验证)
@property
def email(self):
return self._email
@email.setter
def email(self, value):
"""设置邮箱,验证格式"""
if value and not re.match(r'^[\w\.-]+@[\w\.-]+\.\w+$', value):
raise ValueError("邮箱格式不正确")
self._email = value
# 添加绩效评分
def add_performance_score(self, score):
"""添加绩效评分"""
if not isinstance(score, (int, float)):
raise TypeError("评分必须是数字")
if not 0 <= score <= 100:
raise ValueError("评分范围0-100")
self._performance_scores.append(score)
# 获取平均绩效
def get_average_performance(self):
"""获取平均绩效评分"""
if not self._performance_scores:
return 0
return sum(self._performance_scores) / len(self._performance_scores)
# 获取员工信息
def get_employee_info(self):
"""获取员工完整信息"""
return {
"ID": self._id,
"姓名": self._name,
"部门": self._department,
"工资": self._salary,
"邮箱": self._email or "未设置",
"入职日期": self._hire_date.strftime("%Y-%m-%d"),
"平均绩效": self.get_average_performance()
}
# 使用示例
emp = Employee("EMP001", "张三", "技术部", 15000)
emp.email = "zhangsan@company.com"
emp.add_performance_score(85)
emp.add_performance_score(92)
emp.add_performance_score(78)
print(emp.get_employee_info())
高级封装案例:订单管理系统
from typing import List, Dict, Optional
from datetime import datetime
import json
class OrderItem:
"""订单项类"""
def __init__(self, product_id: str, product_name: str, price: float, quantity: int):
self._product_id = product_id
self._product_name = product_name
self._price = price
self._quantity = quantity
@property
def product_id(self):
return self._product_id
@property
def subtotal(self) -> float:
"""计算小计"""
return self._price * self._quantity
def to_dict(self) -> Dict:
"""转为字典"""
return {
"product_id": self._product_id,
"product_name": self._product_name,
"price": self._price,
"quantity": self._quantity,
"subtotal": self.subtotal
}
def __str__(self):
return f"{self._product_name} x{self._quantity} = ¥{self.subtotal}"
class Order:
"""订单类 - 高级封装"""
def __init__(self, order_id: str, customer_name: str):
self._order_id = order_id
self._customer_name = customer_name
self._items: List[OrderItem] = []
self._status = "pending" # pending, paid, shipped, completed, cancelled
self._created_at = datetime.now()
self._discount_rate = 0.0 # 折扣率,0-1之间
# 订单状态常量
STATUS_PENDING = "pending"
STATUS_PAID = "paid"
STATUS_SHIPPED = "shipped"
STATUS_COMPLETED = "completed"
STATUS_CANCELLED = "cancelled"
@property
def order_id(self):
return self._order_id
@property
def status(self):
return self._status
@property
def created_at(self):
return self._created_at
@property
def total_amount(self) -> float:
"""计算订单总额"""
total = sum(item.subtotal for item in self._items)
return total * (1 - self._discount_rate)
def add_item(self, item: OrderItem):
"""添加订单项"""
if self._status != self.STATUS_PENDING:
raise ValueError("只有待处理状态的订单可以添加商品")
self._items.append(item)
def remove_item(self, product_id: str):
"""移除订单项"""
if self._status != self.STATUS_PENDING:
raise ValueError("只有待处理状态的订单可以移除商品")
self._items = [item for item in self._items if item.product_id != product_id]
def set_discount(self, rate: float):
"""设置折扣"""
if not 0 <= rate <= 0.9:
raise ValueError("折扣率必须在0-0.9之间")
self._discount_rate = rate
def update_status(self, new_status: str):
"""更新订单状态"""
valid_transitions = {
self.STATUS_PENDING: [self.STATUS_PAID, self.STATUS_CANCELLED],
self.STATUS_PAID: [self.STATUS_SHIPPED, self.STATUS_CANCELLED],
self.STATUS_SHIPPED: [self.STATUS_COMPLETED],
self.STATUS_COMPLETED: [],
self.STATUS_CANCELLED: []
}
if new_status not in valid_transitions.get(self._status, []):
raise ValueError(f"不能从{self._status}状态转换为{new_status}状态")
self._status = new_status
def to_dict(self) -> Dict:
"""转为字典"""
return {
"order_id": self._order_id,
"customer_name": self._customer_name,
"status": self._status,
"created_at": self._created_at.isoformat(),
"items": [item.to_dict() for item in self._items],
"discount_rate": self._discount_rate,
"total_amount": self.total_amount
}
def save_to_file(self, filepath: str):
"""保存订单到文件"""
with open(filepath, 'w', encoding='utf-8') as f:
json.dump(self.to_dict(), f, ensure_ascii=False, indent=2)
def __str__(self):
items_str = "\n".join(str(item) for item in self._items)
return f"订单号: {self._order_id}\n客户: {self._customer_name}\n状态: {self._status}\n{items_str}\n总额: ¥{self.total_amount}"
# 使用示例
order = Order("ORD-2024-001", "李四")
# 添加订单项
order.add_item(OrderItem("P001", "笔记本电脑", 5999.00, 1))
order.add_item(OrderItem("P002", "无线鼠标", 199.00, 2))
order.add_item(OrderItem("P003", "键盘", 299.00, 1))
# 设置折扣
order.set_discount(0.1) # 10%折扣
print(f"订单总额: ¥{order.total_amount}")
order.save_to_file("order.json")
# 尝试不合法状态转换
try:
order.update_status(order.STATUS_COMPLETED) # 从pending直接到completed不合法
except ValueError as e:
print(f"状态转换错误: {e}")
封装的实践原则
访问控制级别
class EncapsulationExample:
"""封装级别示例"""
def __init__(self):
self.public_attr = "公开属性" # 公共属性
self._protected_attr = "受保护属性" # 受保护(约定)
self.__private_attr = "私有属性" # 私有(name mangling)
def public_method(self):
"""公共方法"""
return "公开方法"
def _protected_method(self):
"""受保护方法"""
return "受保护方法"
def __private_method(self):
"""私有方法"""
return "私有方法"
def access_private(self):
"""在类内部访问私有成员"""
return self.__private_attr + " - " + self.__private_method()
封装的最佳实践
class BestPractices:
"""封装最佳实践示例"""
def __init__(self):
# 1. 使用下划线约定
self._data = []
# 2. 使用@property提供只读属性
self._read_only = "不可修改"
# 3. 在setter中添加验证
self._age = 0
# 2. 只读属性
@property
def read_only(self):
return self._read_only
# 3. 带验证的setter
@property
def age(self):
return self._age
@age.setter
def age(self, value):
"""验证年龄"""
if not isinstance(value, int):
raise TypeError("年龄必须是整数")
if not 0 <= value <= 150:
raise ValueError("年龄范围无效")
self._age = value
# 4. 返回副本而不是直接返回内部数据
def get_data(self):
"""返回数据副本"""
return self._data.copy()
# 5. 提供明确的操作方法
def add_item(self, item):
"""添加数据"""
self._data.append(item)
def remove_item(self, item):
"""移除数据"""
if item in self._data:
self._data.remove(item)
else:
raise ValueError(f"数据不存在: {item}")
总结要点
封装的好处:
- 数据安全:防止外部直接访问和修改内部数据
- 简化接口:只暴露必要的操作接口
- 灵活性:修改内部实现不影响外部调用
- 可维护性:代码结构清晰,易于维护
封装的原则:
- 最小化暴露:只暴露必要的方法和属性
- 数据验证:在setter中进行数据验证
- 返回副本:对于可变对象,返回副本而非原对象
- 提供明确方法:通过方法操作数据,而非直接访问
就是封装的完整案例,从简单到复杂,覆盖了封装的各个层面,选择哪种封装方式,需要根据项目的具体需求来确定。