SOLID 是面向对象编程(OOP)的五大设计原则,它有助于编写 可维护、可扩展、可读性高 的代码。
🌟 SOLID 五大原则
原则 | 含义 | 关键点 |
---|---|---|
S - 单一职责原则(SRP) | 一个类只做一件事,只对一个变化原因负责 | 降低耦合,提高可维护性 |
O - 开闭原则(OCP) | 对扩展开放,对修改关闭 | 通过 新增代码 而不是修改已有代码来扩展功能 |
L - 里氏替换原则(LSP) | 子类可以替换父类,程序逻辑不变 | 继承要符合父类行为 |
I - 接口隔离原则(ISP) | 接口要小而精,不要让类实现不需要的接口 | 避免臃肿的接口 |
D - 依赖倒置原则(DIP) | 依赖抽象,而不是具体实现 | 降低耦合,提高扩展性 |
🔹 1. 单一职责原则(SRP - Single Responsibility Principle)
“一个类只负责一件事”
❌ 不遵循 SRP(违反单一职责)
class Report:
def generate_report(self):
print("生成报告")
def save_to_file(self):
print("将报告保存到文件")
def send_email(self):
print("发送报告邮件")
📌 问题:
- 这个类既负责生成报告、又负责存储、还负责发送邮件,职责太多,导致耦合严重。
✅ 遵循 SRP(拆分类)
class ReportGenerator:
def generate(self):
print("生成报告")
class ReportSaver:
def save(self):
print("将报告保存到文件")
class ReportSender:
def send(self):
print("发送报告邮件")
✅ 改进:拆分成多个单一职责类,每个类只负责一件事,更易维护。
🔹 2. 开闭原则(OCP - Open/Closed Principle)
“对扩展开放,对修改关闭”
添加新功能时,尽量不修改已有代码,而是扩展它!
❌ 违反 OCP(修改已有代码)
class PaymentProcessor:
def pay(self, method):
if method == "credit_card":
print("使用信用卡支付")
elif method == "paypal":
print("使用 PayPal 支付")
📌 问题:
- 如果要增加 “微信支付”,就必须修改
pay()
方法,违反 OCP。
✅ 遵循 OCP(使用抽象扩展)
from abc import ABC, abstractmethod
class PaymentMethod(ABC):
@abstractmethod
def pay(self):
pass
class CreditCardPayment(PaymentMethod):
def pay(self):
print("使用信用卡支付")
class PayPalPayment(PaymentMethod):
def pay(self):
print("使用 PayPal 支付")
class PaymentProcessor:
def __init__(self, method: PaymentMethod):
self.method = method
def process_payment(self):
self.method.pay()
✅ 改进:新增支付方式 只需 创建新类,而不需要修改 PaymentProcessor
代码。
🔹 3. 里氏替换原则(LSP - Liskov Substitution Principle)
“子类必须能替换父类,而不会破坏程序逻辑”
❌ 违反 LSP(子类破坏父类逻辑)
class Bird:
def fly(self):
print("我会飞")
class Penguin(Bird):
def fly(self):
raise Exception("企鹅不会飞!")
📌 问题:
Penguin
继承了Bird
,但它不能飞,如果代码依赖Bird.fly()
,就可能崩溃!
✅ 遵循 LSP(合理的继承关系)
class Bird:
pass
class FlyingBird(Bird):
def fly(self):
print("我会飞")
class NonFlyingBird(Bird):
def walk(self):
print("我不会飞,但我会走")
class Sparrow(FlyingBird):
pass
class Penguin(NonFlyingBird):
pass
✅ 改进:
- 把
Bird
拆分成 会飞的鸟 和 不会飞的鸟,避免Penguin.fly()
崩溃。
🔹 4. 接口隔离原则(ISP - Interface Segregation Principle)
“接口要小而精,不要让类实现不需要的接口”
❌ 违反 ISP(接口太大)
class Animal:
def eat(self):
pass
def fly(self):
pass # 🐶 狗不会飞,但必须实现它(无意义)
class Dog(Animal):
def eat(self):
print("狗在吃东西")
def fly(self):
raise Exception("狗不会飞")
📌 问题:
Dog
被迫实现fly()
方法,这个方法对它毫无意义!
✅ 遵循 ISP(拆分接口)
class Eatable:
def eat(self):
pass
class Flyable:
def fly(self):
pass
class Dog(Eatable):
def eat(self):
print("狗在吃东西")
class Bird(Eatable, Flyable):
def eat(self):
print("鸟在吃东西")
def fly(self):
print("鸟在飞")
✅ 改进:
Dog
只实现Eatable
,Bird
既能eat()
又能fly()
,避免不必要的方法。
🔹 5. 依赖倒置原则(DIP - Dependency Inversion Principle)
“高层模块不应该依赖低层模块,而应该依赖抽象”
❌ 违反 DIP(依赖具体实现)
class MySQLDatabase:
def connect(self):
print("连接 MySQL")
class UserRepository:
def __init__(self):
self.db = MySQLDatabase() # 直接依赖 MySQL
def get_user(self):
self.db.connect()
print("查询用户")
📌 问题:
UserRepository
绑定了MySQLDatabase
,如果换成 PostgreSQL,就必须修改UserRepository
。
✅ 遵循 DIP(依赖抽象)
from abc import ABC, abstractmethod
class Database(ABC):
@abstractmethod
def connect(self):
pass
class MySQLDatabase(Database):
def connect(self):
print("连接 MySQL")
class PostgreSQLDatabase(Database):
def connect(self):
print("连接 PostgreSQL")
class UserRepository:
def __init__(self, db: Database):
self.db = db # 依赖抽象(接口)
def get_user(self):
self.db.connect()
print("查询用户")
✅ 改进:
UserRepository
依赖Database
接口,可以随时换数据库,而不用改UserRepository
代码。
📌 总结
原则 | 关键思想 | 示例 |
---|---|---|
S - 单一职责 | 一个类只做一件事 | 生成报告 ≠ 存储 ≠ 发送 |
O - 开闭原则 | 扩展新功能时,不修改原代码 | PaymentProcessor 依赖抽象支付方式 |
L - 里氏替换 | 子类可以替换父类,不影响程序运行 | Penguin 不能 fly() |
I - 接口隔离 | 接口要小,不强迫类实现不需要的功能 | Dog 没有 fly() |
D - 依赖倒置 | 依赖抽象,而不是具体实现 | UserRepository 依赖 Database |
🚀 掌握 SOLID 让你的代码更优雅、更易维护!