📜  编程中的 SOLID 原则:通过现实生活中的例子来理解(1)

📅  最后修改于: 2023-12-03 15:11:42.438000             🧑  作者: Mango

编程中的 SOLID 原则:通过现实生活中的例子来理解

SOLID 原则是面向对象编程(Object Oriented Programming,OOP)中的一个设计原则集合。它们是以下五个原则的缩写:

  • 单一职责原则(Single Responsibility Principle,SRP)
  • 开闭原则(Open Closed Principle,OCP)
  • 里氏替换原则(Liskov Substitution Principle,LSP)
  • 接口隔离原则(Interface Segregation Principle,ISP)
  • 依赖反转原则(Dependency Inversion Principle,DIP)
单一职责原则

单一职责原则指的是一个类或者模块应该只负责一项功能。这样可以提高代码的可读性、可维护性和灵活性。类比现实世界中,一个人也应该只有一个主要职责,做好自己的本职工作。

例如,一个厨师只需要做好自己的料理,而不需要去清理餐具或者装修食堂。同样,一个图书管理员也只需要管理图书,而不需要负责计算机网络维护。

在代码中,一个例子是创建一个只负责打印日志的类,而不是让一个类既负责业务逻辑,又负责打印日志。

# 错误示范
class UserData:
    def create_user(self, user):
        # ...创建用户...
        print('User created at', datetime.datetime.now())
        # ...\添加用户到数据库...

# 正确示范
class UserData:
    def create_user(self, user):
        # ...创建用户...
        # ...\添加用户到数据库...

class Logger:
    def log(self, message):
        print(message)

user_data = UserData()
logger = Logger()

user_data.create_user('John Doe')
logger.log('User created at ' + str(datetime.datetime.now()))
开闭原则

开闭原则指的是“开放-封闭”原则。一个类或者模块应该对扩展开放,对修改封闭。换言之,更改类的行为应该是通过添加新功能来实现,而不是修改现有的代码。类似地,一个计算机软件也应该允许添加新的功能,而不是强制修改现有代码。

类比现实世界中,一个建筑也应该留有空间供以后的拓张和装修,而不是被固定在建筑物本身。

在代码中,一个例子是创建一个通用的数据处理类,让它根据传入的参数和类型进行不同的数据处理,而不是在每次需要新的数据处理时都去修改代码。

# 错误示范
class UserData:
    def get_users(self):
        # ...\从数据库读取所有用户数据...
        users = []
        for data in fetched_data:
            users.append(User(data))
        return users

    def get_admins(self):
        # ...\从数据库读取所有管理员数据...
        admins = []
        for data in fetched_data:
            admins.append(Admin(data))
        return admins

# 正确示范
class DataProcessor:
    def process(self, data_list, class_type):
        processed_data = []
        for data in data_list:
            processed_data.append(class_type(data))
        return processed_data

user_data = UserData()
data_processor = DataProcessor()

all_users = user_data.get_users()
all_admins = user_data.get_admins()

processed_users = data_processor.process(all_users, User)
processed_admins = data_processor.process(all_admins, Admin)
里氏替换原则

里氏替换原则指的是父类对象应该能够被它的子类对象所替换,而不影响程序的正确性。也就是说,在一个继承关系中,子类不应该改变父类所拥有的特性。

类比现实世界中,一个“鸟”应该可以被“鸽子”或者“老鹰”这样的子类所替换,而不会影响它所拥有的通用特性,例如“能飞”。

在代码中,一个例子是创建一个抽象的基类,让所有的子类都继承并保持一致的接口和功能。

# 错误示范
class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

class Square(Rectangle):
    def __init__(self, side):
        super().__init__(side, side)

# 正确示范
class Shape:
    def area(self):
        pass

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

class Square(Shape):
    def __init__(self, side):
        self.side = side

    def area(self):
        return self.side ** 2
接口隔离原则

接口隔离原则指的是应该尽可能地将接口拆分成更小的、更具体的接口。这样可以避免在实现接口时被强迫实现一些不需要的方法。也就是说,接口应该仅包含客户端需要使用的方法,而不应该包含其它的不必要的方法。

类比现实世界中,一个“动物学家”不需要了解一只鸟是如何飞行的。她只需要关心鸟是哪一类动物、鸟的外貌和行为。

在代码中,一个例子是创建一个只包含必要方法的接口,而不是让一个类去实现不需要的方法。

# 错误示范
class User:
    def register(self, username, password):
        # ...注册用户...

    def login(self, username, password):
        # ...登录...

    def logout(self, username):
        # ...退出...

class UserWebAPI:
    def get_user_data(self, user_id):
        # ...获取用户数据...

    def get_user_info(self, user_id):
        # ...获取用户信息...
        
# 正确示范
class UserAuth:
    def register(self, username, password):
        # ...注册用户...

    def login(self, username, password):
        # ...登录...

    def logout(self, username):
        # ...退出...

class UserData:
    def get_user_data(self, user_id):
        # ...获取用户数据...

class UserInfo:
    def get_user_info(self, user_id):
        # ...获取用户信息...
依赖反转原则

依赖反转原则指的是高层模块不应该依赖于低层模块,而是应该依赖于抽象接口。同时,抽象接口不应该依赖于具体实现,具体实现应该依赖于抽象接口。这样可以提高代码的灵活性、可扩展性、可维护性和可测试性。

类比现实世界中,一个高层经理不应该依赖于一个底层员工,而是应该依赖于公司的组织结构和规章制度。在公司中,具体实现并不重要,重要的是满足公司既定的规定和流程。

在代码中,一个例子是创建一个抽象接口和一个或多个具体实现,让高层模块依赖于抽象接口,而不是具体实现。

# 错误示范
class EmailSender:
    def send_email(self, message, recipients):
        # ...发送电子邮件...

class NotificationService:
    def __init__(self):
        self.email_sender = EmailSender()

    def send_notification(self, message, recipients):
        self.email_sender.send_email(message, recipients)

# 正确示范
class MessageSender:
    def send(self, message, recipients):
        pass

class EmailSender(MessageSender):
    def send(self, message, recipients):
        # ...发送电子邮件...

class NotificationService:
    def __init__(self, message_sender):
        self.message_sender = message_sender

    def send_notification(self, message, recipients):
        self.message_sender.send(message, recipients)

email_sender = EmailSender()
notification_service = NotificationService(email_sender)

notification_service.send_notification('New message', ['example@example.com'])

以上就是 SOLID 原则的五个方面及其代码实现的例子。通过遵循这些原则,可以创建出更好的代码库和更健壮的代码。