Skip to main content

SOLID

我们来深入拆解 SOLID 原则。这是面向对象设计的五大核心原则,也是面试中高频出现的“进阶基础题”。掌握它,能让你设计的类更易维护、扩展和测试。

SOLID 是什么?

SOLID 是五个原则首字母的缩写,由 Robert C. Martin(Bob 大叔)提出:

  • S:单一职责原则 (Single Responsibility Principle)
  • O:开闭原则 (Open/Closed Principle)
  • L:里氏替换原则 (Liskov Substitution Principle)
  • I:接口隔离原则 (Interface Segregation Principle)
  • D:依赖倒置原则 (Dependency Inversion Principle)

1. S – 单一职责原则

一个类应该只有一个引起它变化的原因。
即:一个类只负责一项职责。

  • 反例:一个 User 类同时处理 saveToDatabase()sendEmail() —— 数据库变更和邮件逻辑变更都会修改这个类。
  • 正解:拆成 UserRepository(持久化)和 EmailService(发送邮件)。
  • 价值:降低耦合,提高可读性,修改一个功能不影响其他。

2. O – 开闭原则

对扩展开放,对修改关闭。
新增功能时,尽量通过添加新代码实现,而不是修改已有代码。

  • 反例:在 Order 类里用 if (type == "A")... else if (type == "B")...,每增加一种订单类型就要修改这个类。
  • 正解:定义一个 DiscountStrategy 接口,不同折扣类型实现该接口,通过策略模式注入。
  • 价值:系统更稳定,已有测试通过的功能不会因扩展而被破坏。

3. L – 里氏替换原则

子类对象必须能够替换掉父类对象,且程序行为不变。
换句话说:继承时不要改变父类的预期行为。

  • 反例Rectangle 父类,Square 子类重写 setWidth/Height 方法,导致设置宽度时高度也被改变 —— 使用 Rectangle 的地方传入 Square 后行为异常。
  • 正解:不要为“代码复用”而强行继承,如果子类无法完全遵守父类契约,考虑改用组合或提取更抽象的接口(如 Shape)。
  • 价值:保证类型体系的多态安全,避免运行时出人意料。

4. I – 接口隔离原则

不应该强迫客户依赖它们不用的方法。
把“胖接口”拆分成多个更小、更具体的接口。

  • 反例:一个 Worker 接口有 work(), eat(), sleep()。机器人实现时必须把 eat()sleep() 空实现或抛异常。
  • 正解:拆成 Workable(含 work())和 Eatable(含 eat()),机器人只实现 Workable,人类同时实现两个。
  • 价值:避免接口污染,使实现类更干净,也降低了接口变更对不相关实现的影响。

5. D – 依赖倒置原则

高层模块不应依赖低层模块,两者都应依赖抽象;抽象不应依赖细节,细节应依赖抽象。
也就是:面向接口编程,而非面向实现编程。

  • 反例NotificationService 直接 new EmailSender() —— 想改成短信发送时,不得不修改 NotificationService
  • 正解:定义 MessageSender 接口,EmailSenderSmsSender 分别实现,通过构造函数或 setter 将具体实现注入到 NotificationService
  • 价值:解耦,提高系统的灵活性和可测试性(可以轻松 mock 依赖)。

面试加分小贴士

  • 组合使用:SOLID 是相辅相成的 —— 例如满足单一职责和接口隔离,自然更容易做到开闭。
  • 不是银弹:过度应用 SOLID 也会导致类爆炸。在扩展性要求不高的简单场景,可以适度简化。
  • 实例落地点:可以举例 Spring 框架 —— Bean 容器天然支持依赖注入(D),AOP 帮助实现开闭原则(O),各种 *Template 类遵循里氏替换等。

如果你需要,我也可以为每个原则写一段 Java 代码示例,方便你在面试时手写演示。