- SOLID原则是面向对象设计中的五个基本设计原则的首字母缩写。
- Single Responsibility Principe(SRP)单一职责原则
- Open/Closed Priciple(OCP)开放-封闭原则
- Liskov Substitution Principle(LSP)里氏替换原则
- Interface Segregation Principle(ISP)接口隔离原则
- Dependency Inversion Principle(DIP)依赖倒置原则
- Single Responsibility Principe(SRP)单一职责原则
- 核心思想
- class应该只有一个单一的职责,即一个类应该有且只有一个改变的理由。这意味着一个类应该只负责一个特定的功能或任务,而不是多个不相关的功能。这样做可以提高内聚,并使类更容易理解、修改、测试。
- 例
- 假设有一个简单的应用程序,用于处理用户信息,包括保存用户信息到数据库、从数据库中检索用户信息。
- 那么就可以将这个功能拆分成两个类:一个负责保存用户信息,一个负责检索用户信息。
- 如果要修改保存逻辑,那么就修改对应类;如果要修改检索逻辑,那么就修改另一个类。
- 核心思想
- Open/Closed Priciple(OCP)开放-封闭原则
- 核心思想
- 软件实体(类、模块、函数等)应该对扩展开放(get),对修改关闭(set)。这意味着在不修改现有代码的情况下,应该能通过添加新的代码来扩展系统的功能。这样做可以使系统更加稳定,减少修改现有代码可能带来的风险。
- 由来
- 由柏拉图梅特克斯(Bertrand Meyer)在《面向对象软件构造》一书中首次提出。
- 目的
- 提高系统可维护性、可扩展性、稳定性。通过遵循这一原则,可以使得系统更容易理解和修改,并减少对现有代码的影响。
- 实际应用中,可以通过几种方式来遵循这个原则:
- 抽象化:痛殴使用抽象类、接口、抽象函数来定义可扩展的接口,从而使得系统可以根据需要进行扩展,而不必修改现有代码。
- 多态性:利用多态性和继承机制,使系统可以通过添加新的子类来扩展功能,而不必修改基类或现有代码。
- 组合/聚合:通过组合或聚合关系来构建对象之间的关联关系,从而使得系统可以通过添加新的组件来扩展功能,而不必修改现有组件。
- 模块化:将系统分解成独立的模块或组件,使得每个模块只负责一个特定的功能,从而使系统可以通过添加新模块来扩展功能,而不必修改现有代码。
- 总之,遵循开放-封闭原则可以设计出易于扩展和维护的软件系统,通过封装变化和利用多态性,使得系统可以根据需要进行扩展,而不必修改现有代码。
- 例
- 现在定义一个抽象类,里面有一个抽象方法,用来“画图形”。
- 下面定义一个具体图形类,比如“圆形类”,只要圆形类继承了“画图形”类并实现”画图形“方法,那”圆形类“就真的有了画圆形的能力。
- 如果想要画一个三角形,那么只要定义一个”三角形类“,并继承实现抽象类抽象方法,就可以画三角形了。
- 这样一来就可以在不修改现有代码的情况下,通过添加(继承)新的(抽象)类来扩展程序的功能,符合开放-封闭原则。
- 核心思想
- Liskov Substitution Principle(LSP)里氏替换原则
- 核心思想
- 子类型必须能替换其基类型。也就是说任何可以接受基类型的地方都可以接受子类型,且不会引发意外行为。这样做可以保持系统的一致性和可靠性,并且确保使用继承时不会破坏代码的正确性。
- 更通俗点的解释:如果一个类型是派生类,那么它应该可以替换掉它的基类,并且不会破坏程序的正确性。也就是说,子类应该保持基类的行为,而不是产生意外的行为。
- 由来
- 是面向对象的基本原则。由芭芭拉利斯科夫(Barbara Liskov)在1987年首次提出。
- 目的
- 确保代码的一致性和可靠性,使得系统更易于理解、扩展、维护。如果违反了里氏替换原则,那么可能会导致程序的错误行为和不稳定性。
- 实际应用中,可以通过以下几点来遵循这个原则:
- 子类必须实现基类的所有行为,不能减少基类的约束条件。
- 子类可以增加新的行为,但不能修改基类已有行为。
- 子类的前置条件(即输入条件)必须比基类更宽松。
- 子类的后置条件(即输出条件)必须比基类更严格。
- 例
- 还是”画图形”功能。“矩形类”继承了“画图形类”的“画图形方法”,“正方形类”继承了“矩形类”。两个类都可以调用各自重写的“画图形方法”来画图形。
- 这个例子满足里氏替换原则:子类(正方形类)可以替换基类(矩形类)而不会引发意外的行为,程序行为保持一致。
- 核心思想
- Interface Segregation Principle(ISP)接口隔离原则
- 核心思想
- 客户端不应该不应该被迫依赖于它不会使用到的接口。这意味着接口的设计应该小而专注。这样做可以降低耦合,并使系统更灵活、更易维护。
- 由来
- 是面向对象的重要思想。由罗伯特马丁(Robert C. Martin)在他的《敏捷软件开发:原则、模式和实践》里首次提出。
- 实际应用中,可以通过以下几点来遵循这个原则:
- 将大而臃肿的接口拆分成多个小而专注的接口,每个接口只包含一个单一的功能或职责。
- 只在需要使用某个接口的地方引入该接口,避免将不需要的接口强加给客户端。
- 根据客户端的需求,设计合适的接口,并保持接口的稳定,避免频繁地修改接口。
- 例
- 有一个“文件管理接口”,里面有三个方法“读取文件”、“修改文件”、“删除文件”。
- 现在有一个类需要读取文件,这个类继承了“文件管理接口”。
- 但它只想读取文件,它并不想修改和删除文件。但是此时它不得不实现它不需要的修改、删除文件方法,因为它继承了一个大接口。
- 核心思想
- Dependency Inversion Principle(DIP)依赖倒置原则
- 核心思想
- 高层模块不应该依赖于低层模块,二者都应该依赖于抽象。
- 抽象不应该依赖于具体实现,具体实现应该依赖于抽象。
- 这样做可以降低模块间的耦合,并使系统更易于扩展和修改。
- 由来
- 有罗伯特马丁(Robert C. Martin)在他的《敏捷软件开发:原则、模式和实践》里首次提出。
- 实际应用中,可以通过以下几点来遵循这个原则:
- 高层模块和低层模块都应该依赖于同一个抽象接口或抽象类。
- 抽象接口或抽象类不应该依赖于具体实现,而是具体实现应该依赖于抽象接口或抽象类。
- 可以通过Dependency injection(依赖注入)等方式来实现依赖倒置,将具体实现的创建和注入交给外部,而不是在类的内部创建具体实现的对象
- 例
- 有一个“邮件发送类”,这个类直接依赖于邮件服务提供商(Gmail等)。
- 这个设计就违反了依赖倒置原则,因为这个类作为高层模块直接依赖于低层模块也就是Gmail。
- 下面修改这个设计,引入抽象。
- 定义一个抽象的邮件服务接口“IEmailService”,并让Gmail类实现这个接口。
- 上面的“邮件发送类”只依赖于IEmailService接口,而不是具体的邮件服务提供商Gmail。
- 现在,“邮件发送类”不再依赖于Gmail,而是依赖于抽象接口IEmailService。这样,可以轻松换不同的邮件服务提供商,而不需要对“邮件发送类”进行修改。
- 核心思想