依赖倒置原则是什么
依赖倒置原则(Dependency Inversion Principle)由 Robert C. Martin 提出,包含两条要求:高层策略不依赖低层细节,两者都依赖抽象;抽象不依赖细节,细节依赖抽象。落地方式是端口与适配器——在架构边界定义接口,业务核心只依赖接口,数据库、消息队列、框架都退到接口实现的一侧。
基本信息
| 提出 | Robert C. Martin,SOLID 五项中的 D |
|---|---|
| 两条要求 | 高层不依赖低层;抽象不依赖细节 |
| 落地手段 | 端口(接口)+ 适配器(实现) |
| 收益 | 业务逻辑可脱离基础设施独立测试与演进 |
典型使用场景
- 业务层需要读写数据库:定义仓储接口,具体驱动藏在实现一侧
- 需要替换或并存多个外部服务:支付、短信、对象存储共用同一个端口
- 单元测试要绕开外部依赖:注入内存实现,不启动真实数据库
- 框架升级影响面过大:业务代码不引用框架类型,升级范围收窄
- 同一套规则被多个入口复用:API 与批处理调用同一个领域核心
依赖方向对照
依赖方向对照
// 反面:业务直接依赖具体实现
class OrderService {
private final MySQLOrderRepository repo = new MySQLOrderRepository();
}
// 正面:接口定义在业务侧,实现反向依赖接口
interface OrderRepository { void save(Order order); }
class OrderService {
private final OrderRepository repo;
OrderService(OrderRepository repo) { this.repo = repo; }
}常见误解与澄清
| 常见说法 | 问题所在 | 正确理解 |
|---|---|---|
| 用了依赖注入框架就满足 DIP | 框架只是注入手段,依赖方向可能依旧反的 | 看引用方向:业务包是否引用了基础设施包 |
| 每个类都应该抽一个接口 | 没有替换需求时只是增加跳转层级 | 只在跨边界、需替换或需测试隔离处抽象 |
| 接口放在实现包里更顺手 | 实现包变成被依赖方,方向没变 | 接口定义在调用方一侧的领域包 |
依赖注入是手段,DIP 是原则:用了 DI 容器也可能把具体实现注入业务类,方向仍然是反的。
与六边形架构的关系:DIP 是原则,端口-适配器是它在架构层面的落地形式,Cockburn 的表述与之一致。