依赖倒转原则¶
约 691 个字 15 行代码 1 张图片 预计阅读时间 4 分钟
场景:电话遥控修电脑¶
娇娇电脑蓝屏,小菜通过电话指导她 拔一根内存条 排查故障——用户无需懂 CPU 如何制造,只需懂 标准接口(内存插槽)。PC 硬件的 易插拔 正是 依赖倒转 的生动类比。
原则定义¶
依赖倒转原则(Dependency Inversion Principle,DIP):
- 高层模块不应该依赖低层模块,二者都应该依赖抽象。
- 抽象不应该依赖细节,细节应该依赖抽象。
白话:针对接口编程,不要针对实现编程。
主板、CPU、内存、硬盘都 针对接口(插槽/针脚标准) 设计;若内存必须绑定某品牌主板,换内存就要换主板——显然不合理。
为何叫「倒转」¶
面向过程时代:高层直接调用底层函数库(如访问数据库的函数),高层依赖低层。
面向对象时代:高层与低层都依赖 抽象接口,依赖方向 倒转 了——细节(MySQL 驱动、PostgreSQL 驱动)依赖抽象(Connection 接口)。
与 PC 硬件的类比¶
| 概念 | 软件 |
|---|---|
| CPU 强内聚、标准接口 | 类封装内部实现,对外暴露稳定 API |
| 内存坏了只换内存 | 模块可独立替换,不影响其他部件 |
| 主板预留插槽 | 依赖抽象,而非具体实现 |
里氏代换原则(LSP)¶
DIP 常与 里氏代换原则 一起理解:
子类型必须能够替换掉它们的父类型。 程序中使用父类的地方,换成子类后行为应仍然正确。
反例:鸟会飞,企鹅不会飞——在 编程世界 里企鹅不应继承「鸟」;应抽象出 Flyable 等更精确的接口。
简单对比¶
Java
// 违反 DIP:高层直接依赖具体 JDBC
class ReportService {
void export() {
MySqlReportExporter ex = new MySqlReportExporter();
ex.run();
}
}
// 更符合 DIP
interface ReportExporter { void run(); }
class ReportService {
private final ReportExporter exporter;
ReportService(ReportExporter exporter) { this.exporter = exporter; }
void export() { exporter.run(); }
}
依赖注入(DI) 是实现 DIP 的常用手段:构造器注入、Setter 注入、IoC 容器等。
SOLID 中的位置¶
| 字母 | 原则 | 与 DIP 关系 |
|---|---|---|
| S | 单一职责 | 职责清晰才易抽象 |
| O | 开放-封闭 | 扩展通过新实现接入 |
| L | 里氏代换 | 保证多态替换安全 |
| I | 接口隔离 | 小接口降低依赖面 |
| D | 依赖倒转 | 依赖方向指向抽象 |
小结¶
PC 硬件发展与面向对象思想 异曲同工:都通过 标准接口 实现 强内聚、松耦合。依赖倒转让高层逻辑与基础设施解耦,是工厂、抽象工厂、策略等模式共同的理论基础。