SOLID 模式并不是一种单一的设计模式,而是由罗伯特·C·马丁(Robert C. Martin)在 21 世纪早期引入的五个面向对象编程和设计的基本原则的缩写,这五个原则旨在使软件系统更加易于维护、扩展和理解。以下是对 SOLID 中每个原则的详细介绍:
单一职责原则(Single Responsibility Principle - SRP)
- 定义:一个类应该只有一个引起它变化的原因。也就是说,一个类应该只负责一项职责。
- 示例:假设有一个 User 类,它既负责用户信息的存储,又负责用户信息的验证和用户信息的持久化(如保存到数据库)。按照单一职责原则,我们应该将这些职责分离。可以创建一个 UserInfo 类来负责用户信息的存储,一个 UserValidator 类来负责用户信息的验证,一个 UserRepository 类来负责用户信息的持久化。
# 负责用户信息存储
class UserInfo:
def __init__(self, name, email):
self.name = name
self.email = email
# 负责用户信息验证
class UserValidator:
def validate(self, user_info):
if not user_info.name or not user_info.email:
return False
return True
# 负责用户信息持久化
class UserRepository:
def save(self, user_info):
# 模拟保存到数据库
print(f"Saving user {user_info.name} with email {user_info.email} to database.")
开闭原则(Open/Closed Principle - OCP)
- 定义:软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。这意味着当需要添加新功能时,应该通过扩展现有代码来实现,而不是修改现有的代码。
- 示例:假设有一个图形绘制程序,最初只有绘制圆形的功能。如果按照开闭原则设计,当需要添加绘制矩形的功能时,不应该修改原有的绘制圆形的代码。可以通过抽象出一个 Shape 基类,让圆形和矩形类继承自该基类,并实现各自的绘制方法。
from abc import ABC, abstractmethod
# 抽象基类
class Shape(ABC):
@abstractmethod
def draw(self):
pass
# 圆形类
class Circle(Shape):
def draw(self):
print("Drawing a circle.")
# 矩形类
class Rectangle(Shape):
def draw(self):
print("Drawing a rectangle.")
# 绘制函数
def draw_shape(shape):
shape.draw()
circle = Circle()
rectangle = Rectangle()
draw_shape(circle)
draw_shape(rectangle)
里氏替换原则(Liskov Substitution Principle - LSP)
- 定义:子类对象能够替换其父类对象,而程序的行为不会发生改变。也就是说,子类应该能够完全替代父类,并且不会破坏程序的正确性。
- 示例:假设有一个 Bird 类,其中有一个 fly 方法。Sparrow 类继承自 Bird 类,并重写了 fly 方法。而 Ostrich 类虽然也继承自 Bird 类,但鸵鸟不会飞,所以不应该重写 fly 方法或者抛出异常,否则就违反了里氏替换原则。可以通过抽象出一个 FlyingBird 类来解决这个问题。
from abc import ABC, abstractmethod
# 抽象鸟类
class Bird(ABC):
@abstractmethod
def make_sound(self):
pass
# 会飞的鸟类
class FlyingBird(Bird):
@abstractmethod
def fly(self):
pass
# 麻雀类
class Sparrow(FlyingBird):
def fly(self):
print("Sparrow is flying.")
def make_sound(self):
print("Chirp chirp!")
# 鸵鸟类
class Ostrich(Bird):
def make_sound(self):
print("Boom boom!")
def make_bird_fly(bird):
if isinstance(bird, FlyingBird):
bird.fly()
sparrow = Sparrow()
ostrich = Ostrich()
make_bird_fly(sparrow)
# make_bird_fly(ostrich) # 不会报错,因为鸵鸟不是 FlyingBird 类型
接口隔离原则(Interface Segregation Principle - ISP)
- 定义:客户端不应该依赖它不需要的接口。一个类对另一个类的依赖应该建立在最小的接口上。
- 示例:假设有一个 Worker 接口,其中包含 work 和 eat 方法。但有些工人只需要工作,不需要吃饭(如机器人)。按照接口隔离原则,应该将 Worker 接口拆分为 Workable 和 Eatable 两个接口。
from abc import ABC, abstractmethod
# 工作接口
class Workable(ABC):
@abstractmethod
def work(self):
pass
# 吃饭接口
class Eatable(ABC):
@abstractmethod
def eat(self):
pass
# 人类工人类
class HumanWorker(Workable, Eatable):
def work(self):
print("Human worker is working.")
def eat(self):
print("Human worker is eating.")
# 机器人类
class RobotWorker(Workable):
def work(self):
print("Robot worker is working.")
human = HumanWorker()
robot = RobotWorker()
human.work()
human.eat()
robot.work()
# robot.eat() # 会报错,因为 RobotWorker 没有实现 eat 方法
依赖倒置原则(Dependency Inversion Principle - DIP)
- 定义:高层模块不应该依赖低层模块,两者都应该依赖抽象。抽象不应该依赖细节,细节应该依赖抽象。
- 示例:假设有一个 Light 类表示电灯,一个 Switch 类表示开关。如果 Switch 类直接依赖 Light 类,那么当需要添加其他电器(如风扇)时,就需要修改 Switch 类。按照依赖倒置原则,可以抽象出一个 Device 接口,让 Light 类和 Fan 类实现该接口,Switch 类依赖 Device 接口。
from abc import ABC, abstractmethod
# 设备接口
class Device(ABC):
@abstractmethod
def turn_on(self):
pass
@abstractmethod
def turn_off(self):
pass
# 电灯类
class Light(Device):
def turn_on(self):
print("Light is on.")
def turn_off(self):
print("Light is off.")
# 风扇类
class Fan(Device):
def turn_on(self):
print("Fan is on.")
def turn_off(self):
print("Fan is off.")
# 开关类
class Switch:
def __init__(self, device):
self.device = device
def operate(self):
self.device.turn_on()
self.device.turn_off()
light = Light()
fan = Fan()
light_switch = Switch(light)
fan_switch = Switch(fan)
light_switch.operate()
fan_switch.operate()
遵循 SOLID 原则可以提高代码的可维护性、可扩展性和可测试性,使软件系统更加健壮和灵活。