设计模式的 7 大基本原则有哪些?

一则或许对你有用的小广告

欢迎 加入小哈的星球 ,你将获得: 专属的项目实战(已更新的所有项目都能学习) / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新开坑项目: 《Spring AI 项目实战(问答机器人、RAG 增强检索、联网搜索)》 正在持续爆肝中,基于 Spring AI + Spring Boot3.x + JDK 21...点击查看;
  • 《从零手撸:仿小红书(微服务架构)》 已完结,基于 Spring Cloud Alibaba + Spring Boot3.x + JDK 17...点击查看项目介绍; 演示链接: http://116.62.199.48:7070/;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/

面试考察点

当面试官询问设计模式的基本原则时,他们的核心考察点并不仅仅是让你背诵七个名字,而是希望你:

  1. 理解设计模式的灵魂:考察你是否明白所有设计模式背后共通的设计哲学和指导思想,而不仅仅是死记硬背23种模式的实现。
  2. 评估代码设计能力:通过你对原则的理解,判断你在日常编码中是否具备写出高内聚、低耦合、易复用、易扩展代码的意识和能力。
  3. 考察知识体系完整性:确认你对面向对象设计(OOD)的核心思想有系统性的认知,这些原则是 OOD 的基石。
  4. 探究实践与理论的结合:面试官不仅仅想知道“是什么”,更想知道你能否举例说明如何运用这些原则解决实际问题,以及违反原则会导致什么后果。

核心答案

设计模式的七大基本原则,也称为 SOLID + 2 原则,是指导我们进行高质量软件设计的核心思想。它们分别是:

  1. 单一职责原则 (Single Responsibility Principle, SRP):一个类只应有一个引起它变化的原因。
  2. 开闭原则 (Open-Closed Principle, OCP):软件实体(类、模块、函数)应该对扩展开放,对修改关闭。
  3. 里氏替换原则 (Liskov Substitution Principle, LSP):所有引用基类的地方必须能透明地使用其子类的对象,而程序的行为不会发生改变。
  4. 接口隔离原则 (Interface Segregation Principle, ISP):客户端不应该被迫依赖它不使用的接口。一个类对另一个类的依赖应该建立在最小的接口上。
  5. 依赖倒置原则 (Dependency Inversion Principle, DIP):高层模块不应依赖低层模块,二者都应依赖其抽象。抽象不应依赖细节,细节应依赖抽象。
  6. 迪米特法则 (Law of Demeter, LoD),又称最少知识原则:一个对象应该对其他对象保持最少的了解。只与直接的朋友通信。
  7. 合成复用原则 (Composite Reuse Principle, CRP):尽量使用对象组合(has-a)/聚合(contains-a),而不是继承(is-a)关系来达到复用的目的。

深度解析

这些原则共同的目标是构建可维护、可复用、灵活且健壮的软件系统。

  • 单一职责原则 (SRP)

    • 核心:一个类只负责一项职责。如果类承担了过多职责,那么任何一个职责的变化都可能影响该类,导致脆弱的设计。
    • 示例:一个 UserService 类,如果同时负责用户信息持久化(saveToDB)和发送通知邮件(sendEmail),就违反了 SRP。应将持久化和邮件服务拆分为两个独立的类,UserService 仅负责业务逻辑协调。
    • 最佳实践:在微服务架构中,一个服务的边界定义正是 SRP 的宏观体现。
  • 开闭原则 (OCP)

    • 核心:通过 抽象多态 来实现。当需求变化时,我们通过增加新的代码(如新的子类、实现类)来扩展功能,而非修改现有的、稳定的代码。
    • 代码示例
      // 抽象:对扩展开放
      interface DiscountStrategy {
          double apply(double price);
      }
      // 具体实现:可以不断扩展
      class VIPDiscount implements DiscountStrategy {
          @Override public double apply(double price) { return price * 0.8; }
      }
      class FestivalDiscount implements DiscountStrategy {
          @Override public double apply(double price) { return price - 50; }
      }
      class Order {
          private DiscountStrategy strategy; // 依赖抽象
          public void setStrategy(DiscountStrategy strategy) { this.strategy = strategy; }
          public double calculateFinalPrice(double originalPrice) {
              return strategy.apply(originalPrice); // 使用抽象
          }
      }
      
    • 常见误区:OCP 不是绝对的,100% 的关闭不可能。应关注于识别出最有可能变化的区域,并为之设计稳定的抽象。
  • 里氏替换原则 (LSP)

    • 核心:它是对继承关系的严格约束。子类可以扩展父类的功能,但不能改变父类原有的功能(行为)。
    • 经典反例Square 继承 RectangleRectanglesetWidthsetHeight 方法,但 Square 的这两个方法会相互影响,改变了父类“长宽可独立设置”的行为约定,导致在使用父类 Rectangle 的地方替换为 Square 会出现错误。
    • 实践:在重写方法时,子类的前置条件(对输入的要求)不能比父类更严格,后置条件(对输出的承诺)不能比父类更宽松。
  • 接口隔离原则 (ISP)

    • 核心:避免 “胖接口”。将庞大的接口拆分为更小、更具体的接口,让客户端只需知道它们关心的方法。
    • 示例:一个 Animal 接口定义了 eat(), fly(), swim() 方法。对于 Dog 类来说,实现 fly() 是毫无意义的“接口污染”。应拆分为 Eatable, Flyable, Swimmable 等多个接口。
  • 依赖倒置原则 (DIP)

    • 核心:它是实现松耦合的关键。程序的依赖关系应从“具体”倒置为“抽象”。
    • 对比:传统自上而下的设计是:高层模块 Application -> 依赖 -> 中层模块 Logger -> 依赖 -> 低层模块 FileWriter。倒置后:Application 依赖 Logger 接口,FileLogger(实现类)和 DatabaseLogger(实现类)也依赖同一个 Logger 接口。控制权从低层(FileWriter)转移到了高层(Application)。
    • 框架体现:Spring 框架的 控制反转(IoC)依赖注入(DI) 是 DIP 原则的完美实践。我们只依赖 @Service@Repository 这样的抽象,由容器提供具体实例。
  • 迪米特法则 (LoD)

    • 核心:降低类之间的耦合度。一个类应该尽量减少对其他类内部结构的了解。
    • “朋友” 定义:出现在成员变量、方法参数、方法返回值中的类称为直接朋友。出现在方法内部的局部变量中的类不是直接朋友。
    • 反例A.getB().getC().doSomething() 这就是典型的 “链式调用”,AC 产生了依赖,耦合度变高。更好的方式可能是由 B 提供一个代理方法。
  • 合成复用原则 (CRP)

    • 核心:继承是 白箱复用(破坏封装性),组合/聚合是 黑箱复用(保持封装性)。
    • 优势:组合关系比继承关系更灵活。可以在运行时动态改变行为(通过注入不同的组件),避免了继承体系的无限膨胀和僵化。
    • 示例Car 类不应该继承 Engine 类(“车是一个引擎”?这说不通),而应该组合一个 Engine 实例(“车有一个引擎”)。

总结

七大设计原则是构建优秀软件的导航图,其核心共同点在于 通过抽象和中间层来隔离变化、降低耦合,其中 依赖倒置(DIP) 是许多原则(如开闭原则、里氏替换)能够实现的关键架构保障。在实际项目中,应灵活运用这些原则,而非教条地遵循,最终目标是交付易于维护和扩展的高质量代码。