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接口,EmailSender和SmsSender分别实现,通过构造函数或 setter 将具体实现注入到NotificationService。 - 价值:解耦,提高系统的灵活性和可测试性(可以轻松 mock 依赖)。
面试加分小贴士
- 组合使用:SOLID 是相辅相成的 —— 例如满足单一职责和接口隔离,自然更容易做到开闭。
- 不是银弹:过度应用 SOLID 也会导致类爆炸。在扩展性要求不高的简单场景,可以适度简化。
- 实例落地点:可以举例 Spring 框架 —— Bean 容器天然支持依赖注入(D),AOP 帮助实现开闭原则(O),各种
*Template类遵循里氏替换等。
如果你需要,我也可以为每个原则写一段 Java 代码示例,方便你在面试时手写演示。