第70集:面向对象设计原则
学习目标
- 理解面向对象设计的核心原则和目的
- 掌握SOLID五大原则的具体内容和应用
- 学会使用设计原则指导代码设计和重构
- 了解其他重要设计原则和最佳实践
- 能够运用设计原则解决实际编程问题
面向对象设计原则概述
什么是面向对象设计原则
面向对象设计原则是一套经过验证的指导方针,用于创建灵活、可维护、可扩展的代码。这些原则不是法律,而是经验性的建议,帮助开发者设计出高质量的面向对象系统。
为什么需要设计原则
- 提高代码质量:使代码更清晰、更易于理解
- 增强可维护性:降低代码修改的成本和风险
- 促进代码复用:设计良好的组件更容易被复用
- 降低耦合度:减少模块间的依赖关系
- 提高可扩展性:使系统能够轻松适应需求变化
- 简化测试:独立的组件更容易进行单元测试
设计原则的本质
设计原则的核心是平衡:在简单性、灵活性、性能和可维护性之间找到最佳平衡点。
SOLID设计原则
1. 单一职责原则 (SRP - Single Responsibility Principle)
原则定义
一个类应该只有一个引起它变化的原因。换句话说,一个类只负责一项职责或功能。
示例:违反SRP的代码
class User:
"""用户类 - 违反单一职责原则"""
def __init__(self, username: str, email: str):
self.username = username
self.email = email
def save_to_database(self):
"""保存用户到数据库"""
print(f"将用户 {self.username} 保存到数据库")
# 数据库保存逻辑...
def validate_email(self):
"""验证邮箱格式"""
if "@" not in self.email:
raise ValueError("邮箱格式无效")
def send_welcome_email(self):
"""发送欢迎邮件"""
print(f"向 {self.email} 发送欢迎邮件")
# 邮件发送逻辑...
def export_to_csv(self, filename: str):
"""导出用户数据到CSV"""
print(f"导出用户数据到 {filename}")
# CSV导出逻辑...
# 问题:User类承担了太多职责
# 1. 数据存储职责
# 2. 数据验证职责
# 3. 邮件发送职责
# 4. 数据导出职责示例:遵循SRP的重构
# 数据模型 - 只负责数据存储
class User:
"""用户模型 - 只负责数据存储"""
def __init__(self, username: str, email: str):
self.username = username
self.email = email
# 数据验证 - 只负责验证逻辑
class EmailValidator:
"""邮箱验证器 - 只负责验证邮箱"""
@staticmethod
def is_valid(email: str) -> bool:
"""验证邮箱格式"""
return "@" in email and "." in email
# 数据访问 - 只负责数据库操作
class UserRepository:
"""用户仓库 - 只负责数据访问"""
def save(self, user: User):
"""保存用户到数据库"""
print(f"将用户 {user.username} 保存到数据库")
def find_by_username(self, username: str) -> User:
"""根据用户名查找用户"""
print(f"根据用户名 {username} 查找用户")
# 查找逻辑...
return User(username, "test@example.com")
# 邮件服务 - 只负责邮件发送
class EmailService:
"""邮件服务 - 只负责邮件发送"""
def send_welcome_email(self, user: User):
"""发送欢迎邮件"""
print(f"向 {user.email} 发送欢迎邮件")
# 数据导出 - 只负责导出功能
class DataExporter:
"""数据导出器 - 只负责数据导出"""
@staticmethod
def export_user_to_csv(user: User, filename: str):
"""导出用户到CSV"""
print(f"导出用户 {user.username} 到 {filename}")
# 服务层 - 协调各组件
class UserService:
"""用户服务 - 协调各个组件"""
def __init__(self):
self.repository = UserRepository()
self.email_service = EmailService()
self.exporter = DataExporter()
def register_user(self, username: str, email: str) -> bool:
"""注册新用户"""
# 验证邮箱
if not EmailValidator.is_valid(email):
print("邮箱格式无效")
return False
# 创建用户
user = User(username, email)
# 保存用户
self.repository.save(user)
# 发送欢迎邮件
self.email_service.send_welcome_email(user)
return True
def export_user(self, username: str, filename: str):
"""导出用户数据"""
user = self.repository.find_by_username(username)
self.exporter.export_user_to_csv(user, filename)
# 使用示例
service = UserService()
service.register_user("张三", "zhangsan@example.com")
service.export_user("张三", "users.csv")SRP的好处
- 提高可读性:每个类的职责明确,更容易理解
- 降低复杂性:单个类的复杂性降低
- 增强可维护性:修改某个功能只需要修改对应的类
- 促进复用:单一职责的类更容易被复用
2. 开闭原则 (OCP - Open/Closed Principle)
原则定义
软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。
示例:违反OCP的代码
class PaymentProcessor:
"""支付处理器 - 违反开闭原则"""
def process_payment(self, payment_type: str, amount: float):
"""处理支付 - 每添加新支付方式都需要修改此方法"""
if payment_type == "credit_card":
print(f"处理信用卡支付: ¥{amount}")
# 信用卡处理逻辑...
elif payment_type == "paypal":
print(f"处理PayPal支付: ¥{amount}")
# PayPal处理逻辑...
elif payment_type == "alipay":
print(f"处理支付宝支付: ¥{amount}")
# 支付宝处理逻辑...
elif payment_type == "wechat":
print(f"处理微信支付: ¥{amount}")
# 微信支付处理逻辑...
else:
raise ValueError(f"不支持的支付方式: {payment_type}")
# 问题:每增加一种新的支付方式,都需要修改PaymentProcessor类
# 这违反了"对修改关闭"的原则示例:遵循OCP的重构
from abc import ABC, abstractmethod
from typing import List
# 抽象支付接口 - 对扩展开放
class PaymentMethod(ABC):
"""支付方式接口"""
@abstractmethod
def process(self, amount: float):
"""处理支付"""
pass
@abstractmethod
def get_name(self) -> str:
"""获取支付方式名称"""
pass
# 具体支付实现 - 可以无限扩展
class CreditCardPayment(PaymentMethod):
"""信用卡支付"""
def process(self, amount: float):
print(f"处理信用卡支付: ¥{amount}")
# 信用卡处理逻辑...
def get_name(self) -> str:
return "信用卡"
class PayPalPayment(PaymentMethod):
"""PayPal支付"""
def process(self, amount: float):
print(f"处理PayPal支付: ¥{amount}")
# PayPal处理逻辑...
def get_name(self) -> str:
return "PayPal"
class AlipayPayment(PaymentMethod):
"""支付宝支付"""
def process(self, amount: float):
print(f"处理支付宝支付: ¥{amount}")
# 支付宝处理逻辑...
def get_name(self) -> str:
return "支付宝"
class WechatPayment(PaymentMethod):
"""微信支付"""
def process(self, amount: float):
print(f"处理微信支付: ¥{amount}")
# 微信支付处理逻辑...
def get_name(self) -> str:
return "微信支付"
# 支付处理器 - 对修改关闭
class PaymentProcessor:
"""支付处理器 - 对修改关闭,对扩展开放"""
def __init__(self):
self.payment_methods: List[PaymentMethod] = []
def register_payment_method(self, method: PaymentMethod):
"""注册新的支付方式"""
self.payment_methods.append(method)
print(f"注册支付方式: {method.get_name()}")
def process_payment(self, method_name: str, amount: float) -> bool:
"""处理支付 - 不需要修改此方法就能支持新的支付方式"""
for method in self.payment_methods:
if method.get_name().lower() == method_name.lower():
method.process(amount)
return True
print(f"不支持的支付方式: {method_name}")
return False
# 使用示例
processor = PaymentProcessor()
# 注册所有支付方式
processor.register_payment_method(CreditCardPayment())
processor.register_payment_method(PayPalPayment())
processor.register_payment_method(AlipayPayment())
processor.register_payment_method(WechatPayment())
# 处理支付
processor.process_payment("信用卡", 100.0)
processor.process_payment("支付宝", 200.0)
# 添加新的支付方式,不需要修改PaymentProcessor类
class BitcoinPayment(PaymentMethod):
"""比特币支付 - 新增的支付方式"""
def process(self, amount: float):
print(f"处理比特币支付: ¥{amount}")
# 比特币处理逻辑...
def get_name(self) -> str:
return "比特币"
# 只需注册新支付方式,不需要修改现有代码
processor.register_payment_method(BitcoinPayment())
processor.process_payment("比特币", 300.0)OCP的好处
- 增强灵活性:系统更容易适应变化
- 提高稳定性:现有代码不会被破坏
- 促进复用:稳定的组件更容易被复用
- 降低测试成本:不需要重复测试已有功能
3. 里氏替换原则 (LSP - Liskov Substitution Principle)
原则定义
子类型必须能够替换其基类型,而不会产生任何错误或不期望的结果。简单来说,父类出现的地方,子类应该能够替换父类出现。
示例:违反LSP的代码
class Rectangle:
"""矩形类"""
def __init__(self, width: float, height: float):
self.width = width
self.height = height
def set_width(self, width: float):
self.width = width
def set_height(self, height: float):
self.height = height
def get_area(self) -> float:
return self.width * self.height
class Square(Rectangle):
"""正方形类 - 违反里氏替换原则"""
def __init__(self, side: float):
super().__init__(side, side)
def set_width(self, width: float):
# 问题:正方形的宽和高必须相等
self.width = width
self.height = width # 强制保持正方形特性
def set_height(self, height: float):
# 问题:正方形的宽和高必须相等
self.height = height
self.width = height # 强制保持正方形特性
# 违反LSP的问题演示
def test_rectangle(rectangle: Rectangle):
"""测试矩形函数"""
rectangle.set_width(5)
rectangle.set_height(4)
# 对于普通矩形,这个假设是成立的
assert rectangle.width == 5, f"宽度应该是5,但实际是{rectangle.width}"
assert rectangle.height == 4, f"高度应该是4,但实际是{rectangle.height}"
assert rectangle.get_area() == 20, f"面积应该是20,但实际是{rectangle.get_area()}"
# 测试普通矩形 - 正常工作
rectangle = Rectangle(1, 1)
test_rectangle(rectangle) # 通过
# 测试正方形 - 违反LSP,会导致问题
square = Square(1)
test_rectangle(square) # 断言失败!
print("问题:Square类不能替换Rectangle类,因为它们的行为不一致")示例:遵循LSP的重构
from abc import ABC, abstractmethod
from typing import Protocol
# 更好的设计:不要建立继承关系,而是通过组合
class Shape:
"""形状基类"""
@abstractmethod
def get_area(self) -> float:
"""计算面积"""
pass
class Rectangle(Shape):
"""矩形类"""
def __init__(self, width: float, height: float):
self.width = width
self.height = height
def get_area(self) -> float:
return self.width * self.height
def set_width(self, width: float):
self.width = width
def set_height(self, height: float):
self.height = height
class Square(Shape):
"""正方形类"""
def __init__(self, side: float):
self.side = side
def get_area(self) -> float:
return self.side * self.side
def set_side(self, side: float):
self.side = side
# 通用的形状处理函数
def print_area(shape: Shape):
"""打印形状面积 - 可以接受任何Shape的子类"""
area = shape.get_area()
print(f"形状面积: {area}")
# 测试
rectangle = Rectangle(5, 4)
square = Square(5)
# 两种形状都可以正确处理
print_area(rectangle) # 输出: 形状面积: 20
print_area(square) # 输出: 形状面积: 25
# 另一个LSP的例子:鸟类飞行的例子
class Bird:
"""鸟类"""
def fly(self):
"""飞行"""
print("鸟在天空飞翔")
class Sparrow(Bird):
"""麻雀 - 会飞的鸟"""
def fly(self):
print("麻雀在天空快速飞翔")
class Penguin(Bird):
"""企鹅 - 不会飞的鸟 - 违反LSP"""
def fly(self):
# 问题:企鹅不会飞,但继承了Bird的fly方法
raise Exception("企鹅不能飞!")
# 重构后的设计
class Bird:
"""鸟类基类"""
def make_sound(self):
"""鸟叫"""
pass
class FlyingBird(Bird):
"""会飞的鸟类"""
def fly(self):
"""飞行"""
pass
class Sparrow(FlyingBird):
"""麻雀 - 会飞的鸟"""
def fly(self):
print("麻雀在天空快速飞翔")
def make_sound(self):
print("麻雀啾啾叫")
class Penguin(Bird):
"""企鹅 - 不会飞的鸟,不继承FlyingBird"""
def make_sound(self):
print("企鹅嘎嘎叫")
def swim(self):
print("企鹅在水中游泳")
# 现在FlyingBird的子类都保证了能够飞行
def let_bird_fly(bird: FlyingBird):
"""让鸟飞翔 - 只接受会飞的鸟"""
bird.fly()
# 正确使用
sparrow = Sparrow()
let_bird_fly(sparrow) # 正常工作
# 企鹅不会被传递给let_bird_fly,因为它不是FlyingBird
penguin = Penguin()
# let_bird_fly(penguin) # 类型错误,无法通过编译LSP的好处
- 确保类型安全:子类型可以安全地替换基类型
- 提高代码可靠性:多态调用不会出现意外行为
- 增强可维护性:更容易理解和预测代码行为
- 促进正确继承:避免不当的继承关系
4. 接口隔离原则 (ISP - Interface Segregation Principle)
原则定义
客户端不应该被迫依赖它不使用的方法。换句话说,应该将大接口拆分成多个小接口,使每个接口都有单一的职责。
示例:违反ISP的代码
class Worker(ABC):
"""工人接口 - 违反接口隔离原则"""
@abstractmethod
def work(self):
"""工作"""
pass
@abstractmethod
def eat(self):
"""吃饭"""
pass
@abstractmethod
def sleep(self):
"""睡觉"""
pass
class HumanWorker(Worker):
"""人类工人"""
def work(self):
print("人类在工作")
def eat(self):
print("人类在吃饭")
def sleep(self):
print("人类在睡觉")
class RobotWorker(Worker):
"""机器人工人 - 被迫实现不需要的方法"""
def work(self):
print("机器人在工作")
def eat(self):
# 问题:机器人不需要吃饭,但被迫实现此方法
raise NotImplementedError("机器人不需要吃饭")
def sleep(self):
# 问题:机器人不需要睡觉,但被迫实现此方法
raise NotImplementedError("机器人不需要睡觉")
# 使用问题
def manage_worker(worker: Worker):
"""管理工人"""
worker.work()
worker.eat() # 对机器人会出错
worker.sleep() # 对机器人会出错
# 机器人工人会导致错误
robot = RobotWorker()
manage_worker(robot) # 调用eat或sleep时抛出异常示例:遵循ISP的重构
# 拆分成多个小接口
class Workable(ABC):
"""可工作接口"""
@abstractmethod
def work(self):
"""工作"""
pass
class Eatable(ABC):
"""可进食接口"""
@abstractmethod
def eat(self):
"""吃饭"""
pass
class Sleepable(ABC):
"""可睡眠接口"""
@abstractmethod
def sleep(self):
"""睡觉"""
pass
# 人类工人实现所有接口
class HumanWorker(Workable, Eatable, Sleepable):
"""人类工人 - 实现所有接口"""
def work(self):
print("人类在工作")
def eat(self):
print("人类在吃饭")
def sleep(self):
print("人类在睡觉")
# 机器人只实现需要的接口
class RobotWorker(Workable):
"""机器人工人 - 只实现工作接口"""
def work(self):
print("机器人在工作")
# 特定功能的管理函数
def manage_work(workable: Workable):
"""管理工作"""
workable.work()
def manage_meals(eatable: Eatable):
"""管理进食"""
eatable.eat()
def manage_rest(sleepable: Sleepable):
"""管理休息"""
sleepable.sleep()
# 使用
human = HumanWorker()
robot = RobotWorker()
# 管理工作
manage_work(human) # 人类工作
manage_work(robot) # 机器人工作
# 管理生活(只适用于人类)
manage_meals(human) # 人类吃饭
manage_rest(human) # 人类睡觉
# 机器人不会被要求吃饭或睡觉
# manage_meals(robot) # 类型错误
# manage_rest(robot) # 类型错误
# 另一个更实际的例子:多功能打印机
class MultiFunctionDevice(ABC):
"""多功能设备接口 - 违反ISP"""
@abstractmethod
def print(self, document):
"""打印"""
pass
@abstractmethod
def scan(self):
"""扫描"""
pass
@abstractmethod
def fax(self, document):
"""传真"""
pass
# 重构后的设计
class Printer(ABC):
"""打印机接口"""
@abstractmethod
def print(self, document):
pass
class Scanner(ABC):
"""扫描仪接口"""
@abstractmethod
def scan(self):
pass
class FaxMachine(ABC):
"""传真机接口"""
@abstractmethod
def fax(self, document):
pass
# 不同设备实现不同的功能组合
class SimplePrinter(Printer):
"""简单打印机 - 只有打印功能"""
def print(self, document):
print(f"打印文档: {document}")
class AllInOneDevice(Printer, Scanner, FaxMachine):
"""多功能一体机 - 实现所有功能"""
def print(self, document):
print(f"一体机打印文档: {document}")
def scan(self):
print("一体机扫描文档")
return "扫描结果"
def fax(self, document):
print(f"一体机发送传真: {document}")
# 特定功能处理
def handle_printing(printer: Printer):
"""处理打印任务"""
document = "重要文档"
printer.print(document)
def handle_scanning(scanner: Scanner):
"""处理扫描任务"""
result = scanner.scan()
print(f"扫描结果: {result}")
# 使用示例
simple_printer = SimplePrinter()
all_in_one = AllInOneDevice()
# 两种设备都可以用于打印
handle_printing(simple_printer) # 简单打印机打印
handle_printing(all_in_one) # 一体机打印
# 只有一体机可以扫描
# handle_scanning(simple_printer) # 类型错误
handle_scanning(all_in_one) # 一体机扫描ISP的好处
- 提高灵活性:客户端可以选择性地依赖需要的功能
- 降低耦合:客户端只依赖需要的接口,不依赖不需要的方法
- 增强可维护性:修改一个接口不会影响不使用它的客户端
- 促进组合:通过组合多个接口形成完整功能
5. 依赖倒置原则 (DIP - Dependency Inversion Principle)
原则定义
- 高层模块不应该依赖低层模块,两者都应该依赖抽象。
- 抽象不应该依赖细节,细节应该依赖抽象。
示例:违反DIP的代码
# 低层模块 - 具体实现
class EmailNotifier:
"""邮件通知器"""
def send(self, message: str, recipient: str):
print(f"发送邮件给 {recipient}: {message}")
class SMSNotifier:
"""短信通知器"""
def send(self, message: str, phone: str):
print(f"发送短信给 {phone}: {message}")
# 高层模块 - 直接依赖低层模块
class OrderSystem:
"""订单系统 - 违反依赖倒置原则"""
def __init__(self):
# 高层模块直接依赖低层模块的具体实现
self.email_notifier = EmailNotifier()
self.sms_notifier = SMSNotifier()
def process_order(self, order_id: str, customer_email: str, customer_phone: str):
"""处理订单"""
print(f"处理订单: {order_id}")
# 直接调用具体的低层模块
self.email_notifier.send(f"订单 {order_id} 已确认", customer_email)
self.sms_notifier.send(f"订单 {order_id} 已确认", customer_phone)
# 问题:如果需要添加新的通知方式,需要修改OrderSystem类示例:遵循DIP的重构
from abc import ABC, abstractmethod
from typing import List
# 抽象接口 - 被高层和低层共同依赖
class Notifier(ABC):
"""通知器接口"""
@abstractmethod
def send(self, message: str, recipient: str):
"""发送通知"""
pass
# 低层模块 - 实现抽象接口
class EmailNotifier(Notifier):
"""邮件通知器"""
def send(self, message: str, recipient: str):
print(f"发送邮件给 {recipient}: {message}")
class SMSNotifier(Notifier):
"""短信通知器"""
def send(self, message: str, recipient: str):
print(f"发送短信给 {recipient}: {message}")
class PushNotifier(Notifier):
"""推送通知器"""
def send(self, message: str, recipient: str):
print(f"发送推送通知给 {recipient}: {message}")
# 高层模块 - 依赖抽象接口
class OrderSystem:
"""订单系统 - 遵循依赖倒置原则"""
def __init__(self, notifiers: List[Notifier]):
# 高层模块依赖抽象接口,而不是具体实现
self.notifiers = notifiers
def process_order(self, order_id: str, contacts: dict):
"""处理订单"""
print(f"处理订单: {order_id}")
message = f"订单 {order_id} 已确认"
# 通过抽象接口调用,不关心具体实现
for notifier_type, recipient in contacts.items():
notifier = self._get_notifier(notifier_type)
if notifier:
notifier.send(message, recipient)
def _get_notifier(self, notifier_type: str) -> Notifier:
"""根据类型获取通知器"""
for notifier in self.notifiers:
if notifier_type.lower() in type(notifier).__name__.lower():
return notifier
return None
# 使用示例 - 可以灵活配置不同的通知方式
notifiers = [
EmailNotifier(),
SMSNotifier(),
PushNotifier()
]
order_system = OrderSystem(notifiers)
# 处理订单 - 只需要提供联系人信息
contacts = {
"email": "customer@example.com",
"sms": "13800138000",
"push": "device_token_123"
}
order_system.process_order("ORD-001", contacts)
# 添加新的通知方式不需要修改OrderSystem
class SlackNotifier(Notifier):
"""Slack通知器"""
def send(self, message: str, recipient: str):
print(f"发送Slack消息给 {recipient}: {message}")
# 只需添加到通知器列表中
notifiers.append(SlackNotifier())
contacts["slack"] = "#general"
order_system.process_order("ORD-002", contacts)
# 另一个实际例子:数据访问层
# 违反DIP的设计
class DatabaseService:
"""数据库服务 - 违反依赖倒置原则"""
def __init__(self):
self.mysql_connection = MySQLConnection() # 直接依赖具体实现
def get_user(self, user_id: str):
return self.mysql_connection.query(f"SELECT * FROM users WHERE id = {user_id}")
# 遵循DIP的设计
class DatabaseConnection(ABC):
"""数据库连接接口"""
@abstractmethod
def query(self, sql: str):
pass
class MySQLConnection(DatabaseConnection):
"""MySQL连接"""
def query(self, sql: str):
print(f"MySQL执行: {sql}")
return {"user": "MySQL数据"}
class PostgreSQLConnection(DatabaseConnection):
"""PostgreSQL连接"""
def query(self, sql: str):
print(f"PostgreSQL执行: {sql}")
return {"user": "PostgreSQL数据"}
class DatabaseService:
"""数据库服务 - 遵循依赖倒置原则"""
def __init__(self, connection: DatabaseConnection):
# 依赖抽象接口,不是具体实现
self.connection = connection
def get_user(self, user_id: str):
sql = f"SELECT * FROM users WHERE id = {user_id}"
return self.connection.query(sql)
# 使用时可以灵活切换数据库
mysql_service = DatabaseService(MySQLConnection())
user1 = mysql_service.get_user("123")
postgres_service = DatabaseService(PostgreSQLConnection())
user2 = postgres_service.get_user("123")DIP的好处
- 降低耦合度:高层模块不依赖低层模块的具体实现
- 提高灵活性:可以轻松替换实现而不影响高层模块
- 促进测试:可以轻松创建模拟对象进行单元测试
- 提高可维护性:修改低层实现不会影响高层模块
其他重要设计原则
1. 迪米特法则 (Law of Demeter, LoD)
原则定义
一个对象应该对其他对象有尽可能少的了解,只与直接的朋友通信。也称为"最少知识原则"。
示例:违反迪米特法则的代码
class Engine:
"""引擎类"""
def start(self):
print("引擎启动")
def get_starter(self):
return Starter()
class Starter:
"""启动器类"""
def ignite(self):
print("点火")
class Driver:
"""司机类"""
def start_car(self, car):
# 违反迪米特法则:司机直接与引擎的启动器交互
engine = car.get_engine()
starter = engine.get_starter()
starter.ignite() # 司机不应该直接操作启动器
class Car:
"""汽车类"""
def __init__(self):
self.engine = Engine()
def get_engine(self):
return self.engine
# 使用
driver = Driver()
car = Car()
driver.start_car(car) # 司机需要了解引擎和启动器的细节示例:遵循迪米特法则的重构
class Engine:
"""引擎类"""
def start(self):
print("引擎启动")
# 内部处理启动细节
starter = Starter()
starter.ignite()
class Starter:
"""启动器类"""
def ignite(self):
print("点火")
class Driver:
"""司机类 - 遵循迪米特法则"""
def start_car(self, car):
# 遵循迪米特法则:司机只与汽车交互,不关心内部细节
car.start()
class Car:
"""汽车类"""
def __init__(self):
self.engine = Engine()
def start(self):
"""汽车启动 - 封装内部复杂逻辑"""
print("汽车启动中...")
self.engine.start() # 汽车负责与引擎交互
print("汽车启动完成")
# 使用
driver = Driver()
car = Car()
driver.start_car(car) # 司机只需要知道如何启动汽车,不需要了解内部机制2. 组合优于继承原则
原则定义
优先使用组合而不是继承来获得代码复用和灵活性。
示例:过度使用继承的问题
class Animal:
"""动物类"""
def make_sound(self):
pass
class Bird(Animal):
"""鸟类"""
def fly(self):
print("鸟在飞翔")
def make_sound(self):
print("鸟在鸣叫")
# 问题:如果需要创建既能飞又会游泳的动物怎么办?
class Fish(Animal):
"""鱼类"""
def swim(self):
print("鱼在游泳")
def make_sound(self):
print("鱼在游动")
# 鸭子既有飞的能力又有游泳的能力
class Duck(Bird):
"""鸭子类 - 继承鸟类,但还需要游泳能力"""
def swim(self):
print("鸭子在游泳") # 重复实现了Fish的游泳逻辑
# 企鹅是鸟类但不会飞,继承不合理
class Penguin(Bird):
"""企鹅类 - 违反了继承的语义"""
def fly(self):
raise Exception("企鹅不能飞") # 违反里氏替换原则示例:使用组合重构
from typing import Protocol
# 定义能力接口
class Flyable(Protocol):
"""可飞行能力"""
def fly(self): ...
class Swimmable(Protocol):
"""可游泳能力"""
def swim(self): ...
# 实现能力类
class FlyingAbility:
"""飞行能力"""
def fly(self):
print("在天空飞翔")
class SwimmingAbility:
"""游泳能力"""
def swim(self):
print("在水中游泳")
class SingingAbility:
"""鸣叫能力"""
def sing(self):
print("发出鸣叫声")
# 动物基类
class Animal:
"""动物类"""
def __init__(self, name: str):
self.name = name
def introduce(self):
print(f"我是{self.name}")
# 使用组合创建不同能力的动物
class Bird(Animal):
"""鸟类"""
def __init__(self, name: str):
super().__init__(name)
self.flying_ability = FlyingAbility()
self.singing_ability = SingingAbility()
def fly(self):
self.flying_ability.fly()
def sing(self):
self.singing_ability.sing()
class Fish(Animal):
"""鱼类"""
def __init__(self, name: str):
super().__init__(name)
self.swimming_ability = SwimmingAbility()
def swim(self):
self.swimming_ability.swim()
class Duck(Animal):
"""鸭子 - 既有飞行能力又有游泳能力"""
def __init__(self, name: str):
super().__init__(name)
self.flying_ability = FlyingAbility()
self.swimming_ability = SwimmingAbility()
self.singing_ability = SingingAbility()
def fly(self):
self.flying_ability.fly()
def swim(self):
self.swimming_ability.swim()
def sing(self):
self.singing_ability.sing()
class Penguin(Animal):
"""企鹅 - 只有游泳能力,没有飞行能力"""
def __init__(self, name: str):
super().__init__(name)
self.swimming_ability = SwimmingAbility()
def swim(self):
self.swimming_ability.swim()
# 没有fly方法,不会承诺飞行能力
# 使用示例
duck = Duck("唐老鸭")
duck.introduce()
duck.fly()
duck.swim()
duck.sing()
penguin = Penguin("企鹅君")
penguin.introduce()
penguin.swim()
# penguin.fly() # 不存在fly方法,不会产生错误期望3. KISS原则 (Keep It Simple, Stupid)
原则定义
保持简单:大多数系统如果能保持简单,将能够更好地运行。简单的设计更容易理解、维护和扩展。
示例:复杂的设计
class ComplexOrderProcessor:
"""复杂的订单处理器 - 违反KISS原则"""
def __init__(self):
self.validator = OrderValidator()
self.pricing_engine = PricingEngine()
self.inventory_manager = InventoryManager()
self.payment_gateway = PaymentGateway()
self.shipping_service = ShippingService()
self.notification_service = NotificationService()
self.discount_calculator = DiscountCalculator()
self.tax_calculator = TaxCalculator()
self.rewards_points_manager = RewardsPointsManager()
self.audit_logger = AuditLogger()
self.metrics_collector = MetricsCollector()
def process_order(self, order):
# 过度复杂的流程
try:
# 验证阶段
validation_result = self.validator.validate(order)
if not validation_result.is_valid:
return self._create_error_response(validation_result.errors)
# 库存检查
inventory_status = self.inventory_manager.check_availability(order.items)
if not inventory_status.all_available:
return self._create_backorder_response(inventory_status.unavailable_items)
# 定价计算
base_price = self.pricing_engine.calculate_base_price(order.items)
discounts = self.discount_calculator.calculate_discounts(order, base_price)
discounted_price = base_price - sum(discounts)
taxes = self.tax_calculator.calculate_taxes(order, discounted_price)
total_price = discounted_price + sum(taxes)
# 支付处理
payment_result = self.payment_gateway.process_payment(order.payment_info, total_price)
if not payment_result.success:
return self._create_payment_error_response(payment_result.error)
# 库存扣减
self.inventory_manager.reserve_items(order.items)
# 订单完成
order_result = self._complete_order(order, total_price)
# 后台处理
self.shipping_service.schedule_shipping(order)
self.notification_service.send_confirmation(order)
self.rewards_points_manager.award_points(order.customer_id, total_price)
self.audit_logger.log_order_completion(order)
self.metrics_collector.record_order_metrics(order)
return self._create_success_response(order_result)
except Exception as e:
self.audit_logger.log_error(order, e)
return self._create_error_response(["系统错误"])
# 太多的辅助方法...示例:遵循KISS原则的重构
class SimpleOrderProcessor:
"""简单的订单处理器 - 遵循KISS原则"""
def __init__(self):
self.repositories = OrderRepositories()
self.services = OrderServices()
def process_order(self, order):
"""处理订单 - 简化的流程"""
try:
# 简化的流程:验证->处理->完成
self._validate_order(order)
result = self._process_payment(order)
return self._complete_order(order, result)
except Exception as e:
return self._handle_error(order, e)
def _validate_order(self, order):
"""验证订单"""
if not self.repositories.validator.is_valid(order):
raise ValueError("订单验证失败")
def _process_payment(self, order):
"""处理支付"""
price = self.services.pricing.calculate(order)
return self.services.payment.process(order.payment_info, price)
def _complete_order(self, order, payment_result):
"""完成订单"""
self.repositories.orders.save(order, payment_result)
self.services.notifications.send_confirmation(order)
return {"success": True, "order_id": order.id}
def _handle_error(self, order, error):
"""处理错误"""
self.repositories.errors.log(order, error)
return {"success": False, "error": str(error)}设计原则的实际应用
案例1:电商系统重构
# 重构前:违反多个原则的电商系统
class BadEcommerceSystem:
"""设计不当的电商系统"""
def process_purchase(self, user_id, product_id, payment_info):
# 单一职责问题:一个方法处理太多事情
user = self.get_user(user_id)
product = self.get_product(product_id)
if user.banned:
return "用户已被封禁"
if product.stock <= 0:
return "库存不足"
# 硬编码的支付处理
if payment_info["type"] == "credit_card":
# 信用卡支付逻辑...
pass
elif payment_info["type"] == "paypal":
# PayPal支付逻辑...
pass
# 其他业务逻辑...
return "购买成功"
# 重构后:遵循设计原则
from abc import ABC, abstractmethod
from typing import List, Dict, Optional
# 1. 单一职责原则
class UserRepository:
"""用户仓储 - 只负责用户数据操作"""
def get_user(self, user_id: str) -> Optional[Dict]:
# 获取用户逻辑...
return {"id": user_id, "banned": False}
class ProductRepository:
"""产品仓储 - 只负责产品数据操作"""
def get_product(self, product_id: str) -> Optional[Dict]:
# 获取产品逻辑...
return {"id": product_id, "stock": 100}
class OrderRepository:
"""订单仓储 - 只负责订单数据操作"""
def save_order(self, order: Dict) -> str:
# 保存订单逻辑...
return "ORDER-12345"
# 2. 开闭原则 - 对扩展开放
class PaymentProcessor(ABC):
"""支付处理器接口"""
@abstractmethod
def process_payment(self, payment_info: Dict, amount: float) -> bool:
pass
class CreditCardProcessor(PaymentProcessor):
"""信用卡支付处理器"""
def process_payment(self, payment_info: Dict, amount: float) -> bool:
print(f"处理信用卡支付: ¥{amount}")
return True
class PayPalProcessor(PaymentProcessor):
"""PayPal支付处理器"""
def process_payment(self, payment_info: Dict, amount: float) -> bool:
print(f"处理PayPal支付: ¥{amount}")
return True
# 3. 依赖倒置原则
class PurchaseService:
"""购买服务 - 依赖抽象而非具体实现"""
def __init__(self,
user_repo: UserRepository,
product_repo: ProductRepository,
order_repo: OrderRepository,
payment_processors: List[PaymentProcessor]):
self.user_repo = user_repo
self.product_repo = product_repo
self.order_repo = order_repo
self.payment_processors = payment_processors
def process_purchase(self, user_id: str, product_id: str, payment_info: Dict):
"""处理购买流程"""
# 验证用户
user = self.user_repo.get_user(user_id)
if not user:
return {"success": False, "error": "用户不存在"}
if user.get("banned", False):
return {"success": False, "error": "用户已被封禁"}
# 验证产品
product = self.product_repo.get_product(product_id)
if not product:
return {"success": False, "error": "产品不存在"}
if product.get("stock", 0) <= 0:
return {"success": False, "error": "库存不足"}
# 处理支付
processor = self._get_payment_processor(payment_info.get("type"))
if not processor:
return {"success": False, "error": "不支持的支付方式"}
success = processor.process_payment(payment_info, product.get("price", 0))
if not success:
return {"success": False, "error": "支付失败"}
# 完成订单
order = {
"user_id": user_id,
"product_id": product_id,
"amount": product.get("price", 0),
"status": "completed"
}
order_id = self.order_repo.save_order(order)
return {"success": True, "order_id": order_id}
def _get_payment_processor(self, payment_type: str) -> Optional[PaymentProcessor]:
"""获取支付处理器"""
for processor in self.payment_processors:
if payment_type.lower() in type(processor).__name__.lower():
return processor
return None
# 使用示例
# 创建依赖
user_repo = UserRepository()
product_repo = ProductRepository()
order_repo = OrderRepository()
payment_processors = [CreditCardProcessor(), PayPalProcessor()]
# 创建服务
purchase_service = PurchaseService(
user_repo, product_repo, order_repo, payment_processors
)
# 处理购买
payment_info = {
"type": "credit_card",
"number": "****-****-****-1234",
"cvv": "123"
}
result = purchase_service.process_purchase(
user_id="USER-001",
product_id="PROD-001",
payment_info=payment_info
)
print(f"购买结果: {result}")常见错误
错误1:过度设计
# 错误:为简单问题过度应用设计原则
class OverengineeredCalculator:
"""过度设计的计算器"""
def __init__(self):
self.add_operation = AdditionOperation()
self.subtract_operation = SubtractionOperation()
self.multiply_operation = MultiplicationOperation()
self.divide_operation = DivisionOperation()
self.operation_registry = OperationRegistry()
self.operation_registry.register("add", self.add_operation)
# ... 注册所有操作
def calculate(self, operation: str, a: float, b: float):
op = self.operation_registry.get_operation(operation)
if op:
return op.execute(a, b)
raise ValueError(f"未知操作: {operation}")
# 正确:简单问题用简单解决方案
class Calculator:
"""简单的计算器"""
def add(self, a: float, b: float) -> float:
return a + b
def subtract(self, a: float, b: float) -> float:
return a - b
def multiply(self, a: float, b: float) -> float:
return a * b
def divide(self, a: float, b: float) -> float:
if b == 0:
raise ValueError("除数不能为零")
return a / b错误2:教条式应用原则
# 错误:教条式应用单一职责原则
class Name:
"""名字类 - 过度拆分"""
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
class Address:
"""地址类"""
def __init__(self, street, city, country):
self.street = street
self.city = city
self.country = country
class Phone:
"""电话类"""
def __init__(self, number):
self.number = number
class Email:
"""邮箱类"""
def __init__(self, address):
self.address = address
# 正确:合理的内聚
class Contact:
"""联系方式类 - 合理的内聚"""
def __init__(self, name, address, phone, email):
self.name = name
self.address = address
self.phone = phone
self.email = email课后练习
分析一个你最近编写的类,检查它是否遵循了单一职责原则,如不遵循,请重构它。
设计一个文件处理器系统,使其遵循开闭原则,能够支持多种文件类型的处理,且在添加新文件类型时不需要修改现有代码。
创建一个动物行为系统,确保子类能够完全替代父类,遵循里氏替换原则。
设计一个智能家居系统,将不同的功能拆分成多个接口,遵循接口隔离原则,使设备只需实现需要的功能。
实现一个支付系统,使用依赖倒置原则,使系统能够轻松支持新的支付方式,而不需要修改核心业务逻辑。
总结
面向对象设计原则是编写高质量代码的指导方针:
SOLID原则总结
- SRP(单一职责):一个类只负责一个功能
- OCP(开闭原则):对扩展开放,对修改关闭
- LSP(里氏替换):子类应该能够替换父类
- ISP(接口隔离):不应该强迫实现不需要的接口
- DIP(依赖倒置):依赖抽象而不是具体实现
实践建议
- 平衡原则:不要教条式应用原则,要根据实际情况权衡
- 渐进重构:在现有代码基础上逐步改进,不要过度重构
- 代码审查:定期检查代码是否遵循设计原则
- 持续学习:理解原则背后的思想,而不是死记硬背
- 适度设计:避免过度设计,保持代码简洁有效
设计原则是工具,不是法律。理解原则的目的是帮助我们做出更好的设计决策,而不是束缚我们的创造力。在实际开发中,要根据具体情况灵活应用这些原则。